Movatterモバイル変換


[0]ホーム

URL:


aibridge

packagemodule
v0.2.0Latest Latest
Warning

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

Go to latest
Published: Nov 24, 2025 License:AGPL-3.0Imports:40Imported by:2

Details

Repository

github.com/coder/aibridge

Links

README

aibridge

Documentation

Index

Constants

View Source
const (InterceptionCountStatusFailed    = "failed"InterceptionCountStatusCompleted = "completed")
View Source
const (SSEEventTypeMessage = "message"SSEEventTypeError   = "error"SSEEventTypePing    = "ping")
View Source
const (ProviderAnthropic = "anthropic")
View Source
const (ProviderOpenAI = "openai")

Variables

View Source
var ErrEventStreamClosed =errors.New("event stream closed")
View Source
var UnknownRoute =errors.New("unknown route")

Functions

funcAsActor

func AsActor(ctxcontext.Context, actorIDstring, metadataMetadata)context.Context

Types

typeAWSBedrockConfigadded inv0.1.6

type AWSBedrockConfig struct {RegionstringAccessKey, AccessKeySecretstringModel, SmallFastModelstring// EndpointOverride allows overriding the Bedrock endpoint URL for testing.// If set, requests will be sent to this URL instead of the default AWS Bedrock endpoint.EndpointOverridestring}

typeAnthropicConfigadded inv0.1.6

type AnthropicConfigProviderConfig

typeAnthropicErrorResponse

type AnthropicErrorResponse struct {*anthropic.ErrorResponseStatusCodeint `json:"-"`}

func (*AnthropicErrorResponse)Error

typeAnthropicMessagesBlockingInterception

type AnthropicMessagesBlockingInterception struct {AnthropicMessagesInterceptionBase}

func (*AnthropicMessagesBlockingInterception)ProcessRequest

func (*AnthropicMessagesBlockingInterception)Setup

func (*AnthropicMessagesBlockingInterception)Streamingadded inv0.2.0

typeAnthropicMessagesInterceptionBase

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

func (*AnthropicMessagesInterceptionBase)ID

func (*AnthropicMessagesInterceptionBase)Model

func (*AnthropicMessagesInterceptionBase)Setup

func (i *AnthropicMessagesInterceptionBase) Setup(loggerslog.Logger, recorderRecorder, mcpProxymcp.ServerProxier)

typeAnthropicMessagesStreamingInterception

type AnthropicMessagesStreamingInterception struct {AnthropicMessagesInterceptionBase}

func (*AnthropicMessagesStreamingInterception)ProcessRequest

ProcessRequest handles a request to /v1/messages.This API has a state-machine behind it, which is described inhttps://docs.claude.com/en/docs/build-with-claude/streaming#event-types.

Each stream uses the following event flow:- `message_start`: contains a Message object with empty content.- A series of content blocks, each of which have a `content_block_start`, one or more `content_block_delta` events, and a `content_block_stop` event.- Each content block will have an index that corresponds to its index in the final Message content array.- One or more `message_delta` events, indicating top-level changes to the final Message object.- A final `message_stop` event.

It will inject any tools which have been provided by themcp.ServerProxier.

When a response from the server includes an event indicating that a tool must be invoked, a conditionalflow takes place:

a) if the tool is not injected (i.e. defined by the client), relay the event unmodifiedb) if the tool is injected, it will be invoked by themcp.ServerProxier in the remote MCP server, and itsresults relayed to the SERVER. The response from the server will be handled synchronously, and this loopcan continue until all injected tool invocations are completed and the response is relayed to the client.

func (*AnthropicMessagesStreamingInterception)Setup

func (*AnthropicMessagesStreamingInterception)Streamingadded inv0.2.0

typeAnthropicProvider

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

AnthropicProvider allows for interactions with the Anthropic API.

funcNewAnthropicProvider

func NewAnthropicProvider(cfgAnthropicConfig, bedrockCfg *AWSBedrockConfig) *AnthropicProvider

func (*AnthropicProvider)AuthHeader

func (p *AnthropicProvider) AuthHeader()string

func (*AnthropicProvider)BaseURL

func (p *AnthropicProvider) BaseURL()string

func (*AnthropicProvider)BridgedRoutes

func (p *AnthropicProvider) BridgedRoutes() []string

func (*AnthropicProvider)CreateInterceptor

func (p *AnthropicProvider) CreateInterceptor(whttp.ResponseWriter, r *http.Request) (Interceptor,error)

func (*AnthropicProvider)InjectAuthHeader

func (p *AnthropicProvider) InjectAuthHeader(headers *http.Header)

func (*AnthropicProvider)Name

func (p *AnthropicProvider) Name()string

func (*AnthropicProvider)PassthroughRoutes

func (p *AnthropicProvider) PassthroughRoutes() []string

typeAsyncRecorder

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

AsyncRecorder callsRecorder methods asynchronously and logs any errors which may occur.

funcNewAsyncRecorder

func NewAsyncRecorder(loggerslog.Logger, wrappedRecorder, timeouttime.Duration) *AsyncRecorder

func (*AsyncRecorder)RecordInterception

func (a *AsyncRecorder) RecordInterception(ctxcontext.Context, req *InterceptionRecord)error

RecordInterception must NOT be called asynchronously.If an interception cannot be recorded, the whole request should fail.

func (*AsyncRecorder)RecordInterceptionEndedadded inv0.1.5

func (a *AsyncRecorder) RecordInterceptionEnded(ctxcontext.Context, req *InterceptionRecordEnded)error

func (*AsyncRecorder)RecordPromptUsage

func (a *AsyncRecorder) RecordPromptUsage(_context.Context, req *PromptUsageRecord)error

func (*AsyncRecorder)RecordTokenUsage

func (a *AsyncRecorder) RecordTokenUsage(_context.Context, req *TokenUsageRecord)error

func (*AsyncRecorder)RecordToolUsage

func (a *AsyncRecorder) RecordToolUsage(_context.Context, req *ToolUsageRecord)error

func (*AsyncRecorder)Wait

func (a *AsyncRecorder) Wait()

func (*AsyncRecorder)WithInitiatorIDadded inv0.2.0

func (a *AsyncRecorder) WithInitiatorID(initiatorIDstring)

func (*AsyncRecorder)WithMetricsadded inv0.2.0

func (a *AsyncRecorder) WithMetrics(metrics *Metrics)

func (*AsyncRecorder)WithModeladded inv0.2.0

func (a *AsyncRecorder) WithModel(modelstring)

func (*AsyncRecorder)WithProvideradded inv0.2.0

func (a *AsyncRecorder) WithProvider(providerstring)

typeChatCompletionNewParamsWrapper

type ChatCompletionNewParamsWrapper struct {openai.ChatCompletionNewParams `json:""`Streambool `json:"stream,omitempty"`}

ChatCompletionNewParamsWrapper exists because the "stream" param is not included in openai.ChatCompletionNewParams.

func (*ChatCompletionNewParamsWrapper)LastUserPrompt

func (c *ChatCompletionNewParamsWrapper) LastUserPrompt() (*string,error)

func (ChatCompletionNewParamsWrapper)MarshalJSON

func (cChatCompletionNewParamsWrapper) MarshalJSON() ([]byte,error)

func (*ChatCompletionNewParamsWrapper)UnmarshalJSON

func (c *ChatCompletionNewParamsWrapper) UnmarshalJSON(raw []byte)error

typeConfig

type Config struct {OpenAIProviderConfigAnthropicProviderConfigBedrockAWSBedrockConfig}

typeInterceptionRecord

type InterceptionRecord struct {IDstringInitiatorID, Provider, ModelstringMetadataMetadataStartedAttime.Time}

typeInterceptionRecordEndedadded inv0.1.5

type InterceptionRecordEnded struct {IDstringEndedAttime.Time}

typeInterceptor

type Interceptor interface {// ID returns the unique identifier for this interception.ID()uuid.UUID// Setup injects some required dependencies. This MUST be called before using the interceptor// to process requests.Setup(loggerslog.Logger, recorderRecorder, mcpProxymcp.ServerProxier)// Model returns the model in use for this [Interceptor].Model()string// ProcessRequest handles the HTTP request.ProcessRequest(whttp.ResponseWriter, r *http.Request)error// Specifies whether an interceptor handles streaming or not.Streaming()bool}

Interceptor describes a (potentially) stateful interaction with an AI provider.

typeMessageNewParamsWrapper

type MessageNewParamsWrapper struct {anthropic.MessageNewParams `json:""`Streambool `json:"stream,omitempty"`}

MessageNewParamsWrapper exists because the "stream" param is not included in anthropic.MessageNewParams.

func (*MessageNewParamsWrapper)LastUserPrompt

func (b *MessageNewParamsWrapper) LastUserPrompt() (*string,error)

func (MessageNewParamsWrapper)MarshalJSON

func (bMessageNewParamsWrapper) MarshalJSON() ([]byte,error)

func (*MessageNewParamsWrapper)UnmarshalJSON

func (b *MessageNewParamsWrapper) UnmarshalJSON(raw []byte)error

func (*MessageNewParamsWrapper)UseStreaming

func (b *MessageNewParamsWrapper) UseStreaming()bool

typeMetadata

type Metadata map[string]any

typeMetricsadded inv0.2.0

type Metrics struct {// Interception-related metrics.InterceptionDuration  *prometheus.HistogramVecInterceptionCount     *prometheus.CounterVecInterceptionsInflight *prometheus.GaugeVecPassthroughCount      *prometheus.CounterVec// Prompt-related metrics.PromptCount *prometheus.CounterVec// Token-related metrics.TokenUseCount *prometheus.CounterVec// Tool-related metrics.InjectedToolUseCount    *prometheus.CounterVecNonInjectedToolUseCount *prometheus.CounterVec}

funcNewMetricsadded inv0.2.0

func NewMetrics(regprometheus.Registerer) *Metrics

NewMetrics creates AND registers metrics. It will panic if a collector has already been registered.Note: we are not specifying namespace in the metrics; the provided registerer may specify a "namespace"usingprometheus.WrapRegistererWithPrefix.

typeOpenAIBlockingChatInterception

type OpenAIBlockingChatInterception struct {OpenAIChatInterceptionBase}

func (*OpenAIBlockingChatInterception)ProcessRequest

func (*OpenAIBlockingChatInterception)Setup

func (s *OpenAIBlockingChatInterception) Setup(loggerslog.Logger, recorderRecorder, mcpProxymcp.ServerProxier)

func (*OpenAIBlockingChatInterception)Streamingadded inv0.2.0

typeOpenAIChatInterceptionBase

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

func (*OpenAIChatInterceptionBase)ID

func (*OpenAIChatInterceptionBase)Model

func (*OpenAIChatInterceptionBase)Setup

func (i *OpenAIChatInterceptionBase) Setup(loggerslog.Logger, recorderRecorder, mcpProxymcp.ServerProxier)

typeOpenAIConfigadded inv0.1.6

type OpenAIConfigProviderConfig

typeOpenAIErrorResponse

type OpenAIErrorResponse struct {ErrorObject *shared.ErrorObject `json:"error"`StatusCodeint                 `json:"-"`}

func (*OpenAIErrorResponse)Error

func (a *OpenAIErrorResponse) Error()string

typeOpenAIProvider

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

OpenAIProvider allows for interactions with the OpenAI API.

funcNewOpenAIProvider

func NewOpenAIProvider(cfgOpenAIConfig) *OpenAIProvider

func (*OpenAIProvider)AuthHeader

func (p *OpenAIProvider) AuthHeader()string

func (*OpenAIProvider)BaseURL

func (p *OpenAIProvider) BaseURL()string

func (*OpenAIProvider)BridgedRoutes

func (p *OpenAIProvider) BridgedRoutes() []string

func (*OpenAIProvider)CreateInterceptor

func (p *OpenAIProvider) CreateInterceptor(whttp.ResponseWriter, r *http.Request) (Interceptor,error)

func (*OpenAIProvider)InjectAuthHeader

func (p *OpenAIProvider) InjectAuthHeader(headers *http.Header)

func (*OpenAIProvider)Name

func (p *OpenAIProvider) Name()string

func (*OpenAIProvider)PassthroughRoutes

func (p *OpenAIProvider) PassthroughRoutes() []string

PassthroughRoutes define the routes which are not currently interceptedbut must be passed through to the upstream.The /v1/completions legacy API is deprecated and will not be passed through.Seehttps://platform.openai.com/docs/api-reference/completions.

typeOpenAIStreamingChatInterception

type OpenAIStreamingChatInterception struct {OpenAIChatInterceptionBase}

func (*OpenAIStreamingChatInterception)ProcessRequest

ProcessRequest handles a request to /v1/chat/completions.Seehttps://platform.openai.com/docs/api-reference/chat-streaming/streaming.

It will inject any tools which have been provided by themcp.ServerProxier.

When a response from the server includes an event indicating that a tool must be invoked, a conditionalflow takes place:

a) if the tool is not injected (i.e. defined by the client), relay the event unmodifiedb) if the tool is injected, it will be invoked by themcp.ServerProxier in the remote MCP server, and itsresults relayed to the SERVER. The response from the server will be handled synchronously, and this loopcan continue until all injected tool invocations are completed and the response is relayed to the client.

func (*OpenAIStreamingChatInterception)Setup

func (i *OpenAIStreamingChatInterception) Setup(loggerslog.Logger, recorderRecorder, mcpProxymcp.ServerProxier)

func (*OpenAIStreamingChatInterception)Streamingadded inv0.2.0

typePromptUsageRecord

type PromptUsageRecord struct {InterceptionIDstringMsgID, PromptstringMetadataMetadataCreatedAttime.Time}

typeProvider

type Provider interface {// Name returns the provider's name.Name()string// BaseURL defines the base URL endpoint for this provider's API.BaseURL()string// CreateInterceptor starts a new [Interceptor] which is responsible for intercepting requests,// communicating with the upstream provider and formulating a response to be sent to the requesting client.CreateInterceptor(whttp.ResponseWriter, r *http.Request) (Interceptor,error)// BridgedRoutes returns a slice of [http.ServeMux]-compatible routes which will have special handling.// Seehttps://pkg.go.dev/net/http#hdr-Patterns-ServeMux.BridgedRoutes() []string// PassthroughRoutes returns a slice of whitelisted [http.ServeMux]-compatible* routes which are// not currently intercepted and must be handled by the upstream directly.//// * only path routes can be specified, not ones containing HTTP methods. (i.e. GET /route).// By default, these passthrough routes will accept any HTTP method.PassthroughRoutes() []string// AuthHeader returns the name of the header which the provider expects to find its authentication// token in.AuthHeader()string// InjectAuthHeader allows [Provider]s to set its authentication header.InjectAuthHeader(*http.Header)}

Provider describes an AI provider client's behaviour.Provider clients are responsible for interacting with upstream AI providers.

typeProviderConfig

type ProviderConfig struct {BaseURL, Keystring}

typeRecorder

type Recorder interface {// RecordInterception records metadata about an interception with an upstream AI provider.RecordInterception(ctxcontext.Context, req *InterceptionRecord)error// RecordInterceptionEnded records that given interception has completed.RecordInterceptionEnded(ctxcontext.Context, req *InterceptionRecordEnded)error// RecordTokenUsage records the tokens used in an interception with an upstream AI provider.RecordTokenUsage(ctxcontext.Context, req *TokenUsageRecord)error// RecordPromptUsage records the prompts used in an interception with an upstream AI provider.RecordPromptUsage(ctxcontext.Context, req *PromptUsageRecord)error// RecordToolUsage records the tools used in an interception with an upstream AI provider.RecordToolUsage(ctxcontext.Context, req *ToolUsageRecord)error}

Recorder describes all the possible usage information we need to capture during interactions with AI providers.Additionally, it introduces the concept of an "Interception", which includes information about which provider/model wasused and by whom. All usage records should reference this Interception by ID.

typeRecorderWrapper

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

RecorderWrapper is a convenience struct which implements RecorderClient and resolves a client before calling each method.It also sets the start/creation time of each record.

funcNewRecorder

func NewRecorder(loggerslog.Logger, clientFn func() (Recorder,error)) *RecorderWrapper

func (*RecorderWrapper)RecordInterception

func (r *RecorderWrapper) RecordInterception(ctxcontext.Context, req *InterceptionRecord)error

func (*RecorderWrapper)RecordInterceptionEndedadded inv0.1.5

func (r *RecorderWrapper) RecordInterceptionEnded(ctxcontext.Context, req *InterceptionRecordEnded)error

func (*RecorderWrapper)RecordPromptUsage

func (r *RecorderWrapper) RecordPromptUsage(ctxcontext.Context, req *PromptUsageRecord)error

func (*RecorderWrapper)RecordTokenUsage

func (r *RecorderWrapper) RecordTokenUsage(ctxcontext.Context, req *TokenUsageRecord)error

func (*RecorderWrapper)RecordToolUsage

func (r *RecorderWrapper) RecordToolUsage(ctxcontext.Context, req *ToolUsageRecord)error

typeRequestBridge

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

RequestBridge is anhttp.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 providea 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 initiatorof each interception.

RequestBridge is safe for concurrent use.

funcNewRequestBridge

func NewRequestBridge(ctxcontext.Context, providers []Provider, recorderRecorder, mcpProxymcp.ServerProxier, metrics *Metrics, loggerslog.Logger) (*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.

ARecorder is also required to record prompt, tool, and token use.

mcpProxy will be closed when theRequestBridge is closed.

func (*RequestBridge)InflightRequests

func (b *RequestBridge) InflightRequests()int32

func (*RequestBridge)ServeHTTP

func (b *RequestBridge) ServeHTTP(rwhttp.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(ctxcontext.Context)error

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

typeSSEEvent

type SSEEvent struct {TypestringDatastringIDstringRetryint}

typeSSEParser

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

funcNewSSEParser

func NewSSEParser() *SSEParser

func (*SSEParser)AllEvents

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

func (*SSEParser)EventsByType

func (p *SSEParser) EventsByType(eventTypestring) []SSEEvent

func (*SSEParser)MessageEvents

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

func (*SSEParser)Parse

func (p *SSEParser) Parse(readerio.Reader)error

typeTokenUsageRecord

type TokenUsageRecord struct {InterceptionIDstringMsgIDstringInput, Outputint64// ExtraTokenTypes holds token types which *may* exist over and above input/output.// These should ultimately get merged into [Metadata], but it's useful to keep these// with their actual type (int64) since [Metadata] is a map[string]any.ExtraTokenTypes map[string]int64MetadataMetadataCreatedAttime.Time}

typeToolArgs

type ToolArgsany

typeToolUsageRecord

type ToolUsageRecord struct {InterceptionIDstringMsgID, ToolstringServerURL       *stringArgsToolArgsInjectedboolInvocationErrorerrorMetadataMetadataCreatedAttime.Time}

Source Files

View all Source files

Directories

PathSynopsis
Package mcpmock is a generated GoMock package.
Package mcpmock is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f orF : Jump to
y orY : Canonical URL
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp