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 ¶
- 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 ¶
This section is empty.
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. 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.