aibridge

package module
v1.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 9, 2026 License: AGPL-3.0 Imports: 28 Imported by: 2

README

aibridge

aibridge is an HTTP gateway that sits between AI clients and upstream AI providers (Anthropic, OpenAI). It intercepts requests to record token usage, prompts, and tool invocations per user. Optionally supports centralized MCP tool injection with allowlist/denylist filtering.

Architecture

┌─────────────────┐     ┌───────────────────────────────────────────┐
│    AI Client    │     │                    aibridge               │
│  (Claude Code,  │────▶│  ┌─────────────────┐    ┌─────────────┐   │
│   Cursor, etc.) │     │  │  RequestBridge  │───▶│  Providers  │   │
└─────────────────┘     │  │  (http.Handler) │    │  (Anthropic │   │
                        │  └─────────────────┘    │   OpenAI)   │   │
                        │                         └──────┬──────┘   │
                        │                                │          │
                        │                                ▼          │    ┌─────────────┐
                        │  ┌─────────────────┐    ┌─────────────┐   │    │  Upstream   │
                        │  │    Recorder     │◀───│ Interceptor │─── ───▶│    API      │
                        │  │ (tokens, tools, │    │ (streaming/ │   │    │ (Anthropic  │
                        │  │  prompts)       │    │  blocking)  │   │    │   OpenAI)   │
                        │  └────────┬────────┘    └──────┬──────┘   │    └─────────────┘
                        │           │                    │          │
                        │           ▼             ┌──────▼──────┐   │
                        │  ┌ ─ ─ ─ ─ ─ ─ ─ ┐      │  MCP Proxy  │   │
                        │  │    Database   │      │   (tools)   │   │
                        │  └ ─ ─ ─ ─ ─ ─ ─ ┘      └─────────────┘   │
                        └───────────────────────────────────────────┘
Components
  • RequestBridge: The main http.Handler that routes requests to providers
  • Provider: Defines bridged routes (intercepted) and passthrough routes (proxied)
  • Interceptor: Handles request/response processing and streaming
  • Recorder: Interface for capturing usage data (tokens, prompts, tools)
  • MCP Proxy (optional): Connects to MCP servers to list tool, inject them into requests, and invoke them in an inner agentic loop

Request Flow

  1. Client sends request to /anthropic/v1/messages or /openai/v1/chat/completions
  2. Actor extraction: Request must have an actor in context (via AsActor()).
  3. Upstream call: Request forwarded to the AI provider
  4. Response relay: Response streamed/sent to client
  5. Recording: Token usage, prompts, and tool invocations recorded

With MCP enabled: Tools from configured MCP servers are centrally defined and injected into requests (prefixed bmcp_). Allowlist/denylist regex patterns control which tools are available. When the model selects an injected tool, the gateway invokes it in an inner agentic loop, and continues the conversation loop until complete.

Passthrough routes (/v1/models, /v1/messages/count_tokens) are reverse-proxied directly.

Observability

Prometheus Metrics

Create metrics with NewMetrics(prometheus.Registerer):

Metric Type Description
interceptions_total Counter Intercepted request count
interceptions_inflight Gauge Currently processing requests
interceptions_duration_seconds Histogram Request duration
tokens_total Counter Token usage (input/output)
prompts_total Counter User prompt count
injected_tool_invocations_total Counter MCP tool invocations
passthrough_total Counter Non-intercepted requests
Recorder Interface

Implement Recorder to persist usage data to your database. The example uses SQLite (example/recorder.go):

  • aibridge_interceptions - request metadata (provider, model, initiator, timestamps)
  • aibridge_token_usages - input/output token counts per response
  • aibridge_user_prompts - user prompts
  • aibridge_tool_usages - tool invocations (injected and client-defined)
type Recorder interface {
    RecordInterception(ctx context.Context, req *InterceptionRecord) error
    RecordInterceptionEnded(ctx context.Context, req *InterceptionRecordEnded) error
    RecordTokenUsage(ctx context.Context, req *TokenUsageRecord) error
    RecordPromptUsage(ctx context.Context, req *PromptUsageRecord) error
    RecordToolUsage(ctx context.Context, req *ToolUsageRecord) error
}

Example

See example/ for a complete runnable example with SQLite persistence and DeepWiki MCP integration.

Setup
  1. Get API keys from the provider consoles:

  2. Set environment variables:

    export ANTHROPIC_API_KEY="sk-ant-..."
    export OPENAI_API_KEY="sk-..."
    
  3. Run the example:

    cd example && go run .
    
  4. Test with curl:

    curl -X POST http://localhost:8080/anthropic/v1/messages \
      -H "Content-Type: application/json" \
      -d '{
        "model": "claude-sonnet-4-20250514",
        "max_tokens": 1024,
        "messages": [{"role": "user", "content": "Hello!"}],
        "stream": true
      }'
    
  5. Test with Claude Code: Claude Code allows a base URL override via ANTHROPIC_BASE_URL.

    image with cloude code example

Supported Routes

Provider Route Type
Anthropic /anthropic/v1/messages Bridged (intercepted)
Anthropic /anthropic/v1/models Passthrough
Anthropic /anthropic/v1/messages/count_tokens Passthrough
OpenAI /openai/v1/chat/completions Bridged (intercepted)
OpenAI /openai/v1/models Passthrough

Documentation

Index

Constants

View Source
const (
	ProviderAnthropic = config.ProviderAnthropic
	ProviderOpenAI    = config.ProviderOpenAI
	ProviderCopilot   = config.ProviderCopilot
)

Const + Type + function aliases for backwards compatibility.

View Source
const (
	SSEEventTypeMessage = "message"
	SSEEventTypeError   = "error"
	SSEEventTypePing    = "ping"
)

Variables

This section is empty.

Functions

func AsActor

func AsActor(ctx context.Context, actorID string, metadata recorder.Metadata) context.Context

func NewAnthropicProvider

func NewAnthropicProvider(cfg config.Anthropic, bedrockCfg *config.AWSBedrock) provider.Provider

func NewCopilotProvider added in v1.0.0

func NewCopilotProvider(cfg config.Copilot) provider.Provider

func NewMetrics added in v0.2.0

func NewMetrics(reg prometheus.Registerer) *metrics.Metrics

func NewOpenAIProvider

func NewOpenAIProvider(cfg config.OpenAI) provider.Provider

Types

type AWSBedrockConfig added in v0.1.6

type AWSBedrockConfig = config.AWSBedrock

type AnthropicConfig added in v0.1.6

type AnthropicConfig = config.Anthropic

type CopilotConfig added in v1.0.0

type CopilotConfig = config.Copilot

type InterceptionRecord

type InterceptionRecord = recorder.InterceptionRecord

type InterceptionRecordEnded added in v0.1.5

type InterceptionRecordEnded = recorder.InterceptionRecordEnded

type Metadata

type Metadata = recorder.Metadata

type Metrics added in v0.2.0

type Metrics = metrics.Metrics

type OpenAIConfig added in v0.1.6

type OpenAIConfig = config.OpenAI

type PromptUsageRecord

type PromptUsageRecord = recorder.PromptUsageRecord

type Provider

type Provider = provider.Provider

type Recorder

type Recorder = recorder.Recorder

func NewRecorder

func NewRecorder(logger slog.Logger, tracer trace.Tracer, clientFn func() (Recorder, error)) Recorder

type RequestBridge

type RequestBridge struct {
	// contains filtered or unexported fields
}

RequestBridge is an http.Handler which is capable of masquerading as AI providers' APIs; specifically, OpenAI's & Anthropic's at present. RequestBridge intercepts requests to - and responses from - these upstream services to provide a centralized governance layer.

RequestBridge has no concept of authentication or authorization. It does have a concept of identity, in the narrow sense that it expects an [actor] to be defined in the context, to record the initiator of each interception.

RequestBridge is safe for concurrent use.

func NewRequestBridge

func NewRequestBridge(ctx context.Context, providers []provider.Provider, rec recorder.Recorder, mcpProxy mcp.ServerProxier, logger slog.Logger, m *metrics.Metrics, tracer trace.Tracer) (*RequestBridge, error)

NewRequestBridge creates a new *RequestBridge and registers the HTTP routes defined by the given providers. Any routes which are requested but not registered will be reverse-proxied to the upstream service.

A [intercept.Recorder] is also required to record prompt, tool, and token use.

mcpProxy will be closed when the RequestBridge is closed.

Circuit breaker configuration is obtained from each provider's CircuitBreakerConfig() method. Providers returning nil will not have circuit breaker protection.

func (*RequestBridge) InflightRequests

func (b *RequestBridge) InflightRequests() int32

func (*RequestBridge) ServeHTTP

func (b *RequestBridge) ServeHTTP(rw http.ResponseWriter, r *http.Request)

ServeHTTP exposes the internal http.Handler, which has all [Provider]s' routes registered. It also tracks inflight requests.

func (*RequestBridge) Shutdown

func (b *RequestBridge) Shutdown(ctx context.Context) error

Shutdown will attempt to gracefully shutdown. This entails waiting for all requests to complete, and shutting down the MCP server proxier. TODO: add tests.

type SSEEvent

type SSEEvent struct {
	Type  string
	Data  string
	ID    string
	Retry int
}

type SSEParser

type SSEParser struct {
	// contains filtered or unexported fields
}

func NewSSEParser

func NewSSEParser() *SSEParser

func (*SSEParser) AllEvents

func (p *SSEParser) AllEvents() map[string][]SSEEvent

func (*SSEParser) EventsByType

func (p *SSEParser) EventsByType(eventType string) []SSEEvent

func (*SSEParser) MessageEvents

func (p *SSEParser) MessageEvents() []SSEEvent

func (*SSEParser) Parse

func (p *SSEParser) Parse(reader io.Reader) error

type TokenUsageRecord

type TokenUsageRecord = recorder.TokenUsageRecord

type ToolUsageRecord

type ToolUsageRecord = recorder.ToolUsageRecord

Directories

Path Synopsis
internal
Package mcpmock is a generated GoMock package.
Package mcpmock is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL