routing

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jul 1, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

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:

  1. 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.
  2. 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.)
  3. 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.
  4. 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.
  5. Host: lookup by binding.Spec.HostID for BaseURL.
  6. Keys: Policy.Spec.HostKeyIDs filtered to those whose Owner.ID is the chosen Host (a key authenticates against one host).
  7. 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

View Source
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

func PolicyAllows(snap *appcatalog.Snapshot, pol *policy.Policy, m *model.Model) bool

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

func (p *Plan) UpstreamModel() string

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.

func (*Resolver) Resolve

func (r *Resolver) Resolve(req Request) (*Plan, error)

Resolve maps the inbound request to a Plan. Errors are typed; handlers pick the appropriate HTTP status.

Jump to

Keyboard shortcuts

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