Documentation
¶
Overview ¶
Package adapter provides the generic adapter framework: a Spec type that bundles wire-shape metadata (inbound URL paths, upstream URL path, auth strategy, canonical translator, token extractor), a generic pipeline.Adapter implementation parameterised by path and auth strategy, a Registry of Specs, and a generic route mounter that iterates registered specs.
This package is the composition root's extension point: to add a new wire shape, add one Spec to the registry in cmd/relay/main.go. No branching in dispatch; no per-shape packages inside app/.
Deliberately out of scope:
- Provider-catalog data (lives in wyolet/relay-catalog)
- The canonical v1.Translator interface (lives in pkg/relay/v1)
- The OLD app/adapters.Translator interface (deleted in PR 5)
Index ¶
- func NewUpstreamTransport(http1 bool) http.RoundTripper
- func SetUpstreamMaxIdleConnsPerHost(n int)
- type AuthStrategy
- type InboundPath
- type Registry
- func (r *Registry) AdapterMap() map[adapters.Name]pipeline.Adapter
- func (r *Registry) AssertWired() error
- func (r *Registry) PipelineAdapter(name adapters.Name) pipeline.Adapter
- func (r *Registry) Spec(name adapters.Name) *Spec
- func (r *Registry) Specs() []*Spec
- func (r *Registry) TranslatorMap() v1.Registry
- type Spec
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewUpstreamTransport ¶ added in v0.9.8
func NewUpstreamTransport(http1 bool) http.RoundTripper
NewUpstreamTransport builds the tuned upstream transport. http1 empties TLSNextProto on the same tuned base to disable HTTP/2 negotiation for shapes that trip Go's HTTP/2 client bugs (see Spec.UseHTTP1). Exported for the composition root, which applies the same pooling to the proxy runner's client (its upstreams are just as hot as the pipeline's).
The returned RoundTripper wraps the transport with connection-reuse accounting (relay_upstream_connections_total) — the tripwire for the MaxIdleConnsPerHost-style churn this pooling exists to prevent.
func SetUpstreamMaxIdleConnsPerHost ¶ added in v0.9.8
func SetUpstreamMaxIdleConnsPerHost(n int)
SetUpstreamMaxIdleConnsPerHost overrides the per-host idle-connection cap used by every Spec built afterwards (the composition root wires the RELAY_UPSTREAM_MAX_IDLE_PER_HOST value here). Values < 1 are ignored so a zero config default leaves the built-in 128. Not safe to call concurrently with Build — invoke once at boot, before specs are constructed.
Types ¶
type AuthStrategy ¶
type AuthStrategy struct {
// Header is the HTTP header to set (e.g. "Authorization", "x-api-key").
Header string
// Scheme is prepended to the key with a space if non-empty
// (e.g. "Bearer" → "Bearer sk-..."). Empty means no prefix.
Scheme string
// ExtraHeaders are static header key/value pairs added unconditionally
// (e.g. {"anthropic-version": "2023-06-01"}). Only applied when the
// header is not already present in the forwarded request headers.
ExtraHeaders map[string]string
}
AuthStrategy configures how an adapter authenticates to the upstream.
type InboundPath ¶
type InboundPath struct {
// Path is the URL path, e.g. "/v1/chat/completions".
Path string
// OperationID is the huma operation ID, e.g. "chat_completions".
OperationID string
// Summary is the huma operation summary.
Summary string
}
InboundPath describes one inbound HTTP route for a spec.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds all registered adapter specs. Built once at boot in cmd/relay/main.go via NewRegistry. Read-only after construction.
func NewRegistry ¶
NewRegistry constructs a Registry from the given specs. Each spec must have had Build() called. Panics if two specs share the same Name.
func (*Registry) AdapterMap ¶
AdapterMap returns a map[adapters.Name]pipeline.Adapter for all registered specs. The returned map is a copy; mutation does not affect the registry.
func (*Registry) AssertWired ¶
AssertWired verifies the composition root registered a coherent set of specs, so the defensive "no spec / no adapter / no translator" 500s in the dispatch path are structurally unreachable. The composition root calls this at boot and fails fast on a violation — a wiring bug must crash the binary, never surface as a runtime request error.
It enforces two invariants:
- Every upstream-binding adapter (the allow-set HostBinding.Adapter is validated against) has a registered spec with a non-nil pipeline adapter to dispatch to.
- Every inbound, non-byte-pass spec has a canonical Translator, so a cross-shape route can always translate.
func (*Registry) PipelineAdapter ¶
PipelineAdapter returns the pipeline.Adapter for the given name, or nil.
func (*Registry) TranslatorMap ¶
TranslatorMap returns a v1.Registry (map[v1.Name]v1.Translator) for all specs that have a non-nil Translator. Specs with BytePass=true and nil Translator are omitted — they have no canonical translator.
type Spec ¶
type Spec struct {
// Name is the wire-protocol identifier, matching adapters.Name values.
// Used as the key in the Registry and as the DispatchInput.Inbound value.
Name adapters.Name
// InboundPaths is the set of inbound HTTP paths this spec owns.
// Each entry is registered as a POST route via the generic mounter.
// Example: ["/v1/chat/completions", "/openai/v1/chat/completions"].
InboundPaths []InboundPath
// UpstreamPath is the path used when calling the upstream host.
// Example: "/v1/chat/completions", "/v1/responses", "/v1/embeddings".
// Ignored when UpstreamPathFn is set.
UpstreamPath string
// UpstreamPathFn resolves the upstream path per request from the upstream
// model name and the sync/stream choice. Set only for shapes that encode
// the model and/or stream selection in the URL rather than the body —
// Gemini's "/v1beta/models/{model}:generateContent" vs
// ":streamGenerateContent". When nil, UpstreamPath is used verbatim.
UpstreamPathFn func(upstreamModel string, stream bool) string
// Auth configures how the upstream is authenticated with an API-key
// credential (the default).
Auth AuthStrategy
// OAuthAuth is the alternate auth used when the resolved credential is an
// OAuth token (HostKey value-kind "oauth") rather than an API key — e.g.
// Anthropic subscription tokens go out as `Authorization: Bearer …` + the
// oauth beta header instead of `x-api-key`. A zero value (empty Header)
// means the spec has no OAuth variant and Auth is used for every credential.
// Selecting it keeps the binding on the same wire shape (so same-shape
// byte-pass still applies); only the upstream auth headers differ.
OAuthAuth AuthStrategy
// Translator is the canonical v1.Translator for this shape. Used by
// the standard dispatch chain (inbound→canonical→upstream and back).
// Nil means this shape has no canonical translator (e.g. byte-pass-only).
Translator v1.Translator
// BytePass signals that this shape is always byte-equivalent with its
// upstream (no cross-shape translation is ever needed). Embeddings is
// the canonical example: it's a direct passthrough to any OpenAI-compat
// host and there is no canonical embeddings type.
//
// When true, Translator is unused even if set.
BytePass bool
// ExtractTokens extracts usage tokens from the upstream response body.
// If nil, the adapter returns nil tokens (no usage tracking).
ExtractTokens func(body []byte) pkgusage.Tokens
// UseHTTP1 disables HTTP/2 negotiation on the upstream transport.
// Necessary for endpoints that trigger Go's HTTP/2 client bugs (e.g.
// OpenAI /v1/responses sends GOAWAY mid-request over HTTP/2).
UseHTTP1 bool
// IsNativePath reports whether the resolved routing plan implies that the
// upstream host natively speaks this inbound shape — making byte-pass to
// this spec's UpstreamPath the correct strategy, regardless of whether
// the host's HostBinding.Adapter matches the inbound shape Name.
//
// The canonical use case: OpenAIResponses inbound. The spec's Name is
// "openai_responses" but the host's HostBinding.Adapter is "openai".
// OpenAI-proper hosts speak the Responses API natively, so when this
// predicate returns true (host Name == "openai") the dispatch byte-passes
// to /v1/responses via this spec's adapter. Non-openai hosts return false
// and dispatch falls through to the canonical cross-shape chain.
//
// For shapes where the inbound Name equals the binding Adapter (CC, Anthropic,
// Embeddings), this field is nil — the standard Name-equality check handles it.
IsNativePath func(plan *routing.Plan) bool
// contains filtered or unexported fields
}
Spec describes one inbound wire shape and its upstream call semantics. One Spec registration = one inbound URL surface + one upstream path.
The three OpenAI shapes (CC, Responses, Embeddings) are three Specs. The Anthropic shape is one Spec. A future Gemini shape would be one Spec.
func (*Spec) Build ¶
Build finalises the Spec by constructing its shared HTTP client. The client pools upstream connections via a tuned transport (per-host idle ceiling from SetUpstreamMaxIdleConnsPerHost) and keeps the 5-minute client timeout for long streamed responses. Must be called once after all fields are set, before the spec is added to a Registry. Returns s for chaining.
func (*Spec) PipelineAdapter ¶
PipelineAdapter returns a pipeline.Adapter backed by this spec's upstream path and auth strategy. The returned value is safe for concurrent use.