mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
8652ef3e3b
Delegate `aibridge` routing responsibility to the in-memory transport layer. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.9 KiB
Go
71 lines
2.9 KiB
Go
package aibridge
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
)
|
|
|
|
// Source identifies the call site that asked aibridge for a transport. It is
|
|
// attached to the request context so downstream handlers and logs can attribute
|
|
// traffic without changing behavior based on the value.
|
|
type Source string
|
|
|
|
// SourceAgents is chatd traffic originating from a Coder agent.
|
|
const SourceAgents Source = "agents"
|
|
|
|
type sourceCtxKey struct{}
|
|
|
|
// WithSource returns a copy of ctx carrying the given Source. Use this on the
|
|
// request context before invoking a downstream handler so [SourceFromContext]
|
|
// can recover it for logging.
|
|
func WithSource(ctx context.Context, src Source) context.Context {
|
|
return context.WithValue(ctx, sourceCtxKey{}, src)
|
|
}
|
|
|
|
// SourceFromContext returns the Source attached by [WithSource], or the empty
|
|
// string when no Source is set.
|
|
func SourceFromContext(ctx context.Context) Source {
|
|
src, _ := ctx.Value(sourceCtxKey{}).(Source)
|
|
return src
|
|
}
|
|
|
|
type delegatedAPIKeyIDCtxKey struct{}
|
|
|
|
// WithDelegatedAPIKeyID returns a copy of ctx carrying an API key ID on whose
|
|
// behalf the request is being made. The in-process aibridge transport requires
|
|
// this on every RoundTrip and rejects calls whose context lacks it.
|
|
//
|
|
// The caller is responsible for having established that the user owning this
|
|
// key authorized the request: aibridged validates only that the key exists,
|
|
// has not expired, and belongs to a non-deleted, non-system user. It does not
|
|
// verify the key secret, because the caller never has it.
|
|
func WithDelegatedAPIKeyID(ctx context.Context, id string) context.Context {
|
|
return context.WithValue(ctx, delegatedAPIKeyIDCtxKey{}, id)
|
|
}
|
|
|
|
// DelegatedAPIKeyIDFromContext returns the API key ID attached by
|
|
// [WithDelegatedAPIKeyID] and whether a non-empty value was set.
|
|
func DelegatedAPIKeyIDFromContext(ctx context.Context) (string, bool) {
|
|
id, ok := ctx.Value(delegatedAPIKeyIDCtxKey{}).(string)
|
|
return id, ok && id != ""
|
|
}
|
|
|
|
// TransportFactory returns an [http.RoundTripper] that dispatches an aibridge
|
|
// request in-process for a given provider instance name.
|
|
//
|
|
// Implementations live in coderd/aibridged. coderd registers an in-process
|
|
// factory on coderd.API.AIBridgeTransportFactory at startup so callers route
|
|
// traffic through the daemon without going through the gated HTTP route.
|
|
//
|
|
// The returned RoundTripper is responsible for adapting the caller's request
|
|
// to the aibridge daemon's mount path: callers hand it an upstream-shaped
|
|
// request and the transport rewrites URL.Path to "/api/v2/aibridge/<name>/..."
|
|
// before dispatching. Routing keys on the provider's instance name so callers
|
|
// can use the same string the proxy daemon and the bridge mount use.
|
|
//
|
|
// Source is informational: implementations must not gate on it. It is attached
|
|
// to the request context so handlers can include it in logs and metrics.
|
|
type TransportFactory interface {
|
|
TransportFor(providerName string, source Source) (http.RoundTripper, error)
|
|
}
|