adapter

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

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

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

func NewRegistry(specs ...*Spec) *Registry

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

func (r *Registry) AdapterMap() map[adapters.Name]pipeline.Adapter

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

func (r *Registry) AssertWired() error

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

func (r *Registry) PipelineAdapter(name adapters.Name) pipeline.Adapter

PipelineAdapter returns the pipeline.Adapter for the given name, or nil.

func (*Registry) Spec

func (r *Registry) Spec(name adapters.Name) *Spec

Spec returns the Spec for the given name, or nil if unregistered.

func (*Registry) Specs

func (r *Registry) Specs() []*Spec

Specs returns all registered specs in registration order.

func (*Registry) TranslatorMap

func (r *Registry) TranslatorMap() v1.Registry

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.
	Auth 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

func (s *Spec) Build() *Spec

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

func (s *Spec) PipelineAdapter() pipeline.Adapter

PipelineAdapter returns a pipeline.Adapter backed by this spec's upstream path and auth strategy. The returned value is safe for concurrent use.

Jump to

Keyboard shortcuts

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