Documentation
¶
Overview ¶
PolicyAllows answers "is this Model reachable through this Policy?" without picking a binding or a key — the question /v1/models needs to answer. Mirrors the allowed-paths logic in Resolve so the two stay in sync. Resolve is binding-aware (legacy + DSL + wildcard match against specific (provider, model, host) triples); PolicyAllows reduces to "is there *any* binding under this policy that would be allowed?"
Package routing resolves an inbound inference request to a fully-typed RequestPlan that the pipeline can consume. All catalog lookups happen here, against the in-memory snapshot. The pipeline itself is ignorant of the snapshot.
Resolution flow:
- Model: caller supplies a snapshot name (from the request body's `model` field); look it up via snapshot.SnapshotByName. The owning Model + the picked Snapshot are carried into the Plan.
- Policy: comes from the authenticated RelayKey's PolicyID. (No "default route" indirection in the new arch — RelayKey → Policy is direct. Anonymous traffic is served by a separate package.)
- Authorization: model must be allowed by the Policy. Allowed if its id is in Spec.ModelIDs, OR Spec.Models (modelref DSL) matches, OR — when both grant fields are empty — the policy is an implicit wildcard: any model reachable via its hostkeys is allowed. The hostkey-coverage check below is the real gate in that case.
- HostBinding: pick one of the model's HostBindings (snapshot. BindingsForModel) the operator has configured. v1 picks the first enabled binding; multi-host failover is a future feature.
- Host: lookup by binding.Spec.HostID for BaseURL.
- Keys: Policy.Spec.HostKeyIDs filtered to those whose Owner.ID is the chosen Host (a key authenticates against one host).
- RateLimit: Policy.Spec.RateLimitID, resolved to []pkgratelimit.Rule.
Each lookup is a snapshot.Get — no PG, no I/O. Resolve() is allocation- conscious where it matters but not micro-optimised; the hot-path budget dominates this.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrModelNotFound = errors.New("routing: model not found") ErrModelDisabled = errors.New("routing: model disabled") ErrPolicyNotFound = errors.New("routing: policy not found") ErrPolicyDisabled = errors.New("routing: policy disabled") ErrModelNotInPolicy = errors.New("routing: model not allowed by policy") ErrNoHostBinding = errors.New("routing: no enabled host binding for model") ErrHostNotFound = errors.New("routing: host not found") ErrNoKeys = errors.New("routing: no host keys available for this host") // ErrPolicyless is returned when a RelayKey has no PolicyID and the // inference settings forbid policy-less traffic. ErrPolicyless = errors.New("routing: relay key has no policy and policy-less traffic is disabled") )
Errors returned by Resolve. Each maps to a distinct HTTP status in the handler; routing keeps them as typed sentinels so handlers can errors.Is() rather than parse strings.
Functions ¶
func PolicyAllows ¶
PolicyAllows reports whether m is reachable through pol given snap's hostkey coverage. Used to enumerate accessible models for inventory endpoints. Single-shot; not optimised for tight loops.
Types ¶
type Plan ¶
type Plan struct {
Model *model.Model
Snapshot *model.Snapshot
Policy *policy.Policy
HostBinding *binding.Binding
Host *host.Host
Provider string
Keys []*hostkey.HostKey
// Pricing is the rate sheet billing against the chosen binding (explicit
// binding ref first, else the host-owned (model, host) cover). Resolved
// here so emit-time cost stamping costs zero extra lookups. Nil = unpriced.
Pricing *pricing.Pricing
// PayloadLoggingEnabled is the resolved opt-in for full request/response
// body capture: true when the matched Policy or the inbound RelayKey
// sets PayloadLoggingEnabled. Read by the inference entry to flag the
// lifecycle Context so the payloadlog observer captures bodies.
PayloadLoggingEnabled bool
// UpstreamOverride, when non-empty, replaces Snapshot.Upstream() as
// the wire model name. Set only by declared-alias resolution: the
// alias string itself (exact match) or the caller's raw request
// string (wildcard match). Consumers read the wire name via
// UpstreamModel(), never Snapshot.Upstream() directly.
UpstreamOverride string
// ResolvedVia tags non-canonical resolution for usage events, e.g.
// "alias:claude-fable-5[1m]" (the declared alias or pattern that
// matched). Empty for normal snapshot-name resolution.
ResolvedVia string
}
Plan is the fully-resolved input the pipeline consumes. The handler converts this to pipeline.Request, dropping fields the pipeline doesn't need.
Snapshot is the resolved checkpoint. The handler rewrites the request body's `model` field to Plan.UpstreamModel() before invoking the adapter.
func (*Plan) UpstreamModel ¶ added in v0.3.0
UpstreamModel returns the wire model name for this plan — the alias verbatim override when resolution went through a declared alias, otherwise the snapshot's upstream name.
type Request ¶
type Request struct {
// ModelName is the slug or upstream-name reference the caller asked
// for (typically from the body's "model" field), possibly with a
// header-derived "@host" pin folded in by the handler.
ModelName string
// RawModelName is the verbatim caller string before any header pin
// folding. Carried upstream as the wire name when the ref matches a
// wildcard alias. Falls back to ModelName when empty.
RawModelName string
// RelayKey is the authenticated key (already validated for auth).
// Its Spec.PolicyID drives policy selection.
RelayKey *relaykey.RelayKey
// SkipKeyCheck, when true, suppresses the Policy.HostKeyIDs → host
// coverage gate. Used by proxy mode: the caller brings their own
// upstream credentials, so the relay's keypool is irrelevant — only
// the (model, binding, host) tuple matters. Plan.Keys is nil in
// this mode.
SkipKeyCheck bool
}
Request carries the inbound resolution inputs.
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver wraps a Catalog snapshot accessor and answers Resolve calls.
func New ¶
func New(cat *appcatalog.Catalog) *Resolver
New constructs a Resolver against the live catalog. The Resolver reads cat.Current() on every Resolve — picking up the latest snapshot after any NOTIFY-driven reload.