feat: conversion-driven extensions — resolvers, DefineTool, hooks, ops controls
Phase 9a (ADR-0014): Registry.RegisterResolver for dynamic tiers; DefineTool[Args] typed tools; Usage cache/reasoning detail fields wired through anthropic/openai/google; WithPromptCaching (Anthropic cache_control); agent supervision hooks (WithMaxStepsFunc, WithSteer, WithCompactor, WithToolErrorLimits + ErrToolLoop); health Bench/Unbench/Snapshot; ChainConfig.Observer failover events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+47
@@ -18,6 +18,7 @@ type Registry struct {
|
||||
mu sync.RWMutex
|
||||
providers map[string]llm.Provider
|
||||
aliases map[string]string
|
||||
resolvers []Resolver
|
||||
schemes map[string]SchemeFactory
|
||||
// envErrs records LLM_* entries that failed to load so the failure
|
||||
// surfaces when (and only when) that provider name is actually used.
|
||||
@@ -28,6 +29,22 @@ type Registry struct {
|
||||
envLookup func(string) string
|
||||
}
|
||||
|
||||
// Resolver dynamically resolves a bare alias token to a spec string —
|
||||
// the hook for DB- or config-backed tiers that change at runtime. Checked
|
||||
// after static aliases, in registration order; the returned spec is
|
||||
// expanded recursively (chains, nested aliases) with cycle detection.
|
||||
type Resolver interface {
|
||||
// Resolve returns the spec for name, or ok=false when this resolver
|
||||
// does not handle it.
|
||||
Resolve(name string) (spec string, ok bool)
|
||||
}
|
||||
|
||||
// ResolverFunc adapts a function to the Resolver interface.
|
||||
type ResolverFunc func(name string) (string, bool)
|
||||
|
||||
// Resolve implements Resolver.
|
||||
func (f ResolverFunc) Resolve(name string) (string, bool) { return f(name) }
|
||||
|
||||
// SchemeFactory builds a provider instance from an env DSN. name is the
|
||||
// registry name the provider will be registered under (e.g. "m1" for
|
||||
// LLM_M1); dsn carries the scheme, credential, and host.
|
||||
@@ -49,6 +66,27 @@ type ChainConfig struct {
|
||||
|
||||
// Classify overrides the default error classifier (llm.Classify).
|
||||
Classify func(error) llm.ErrorClass
|
||||
|
||||
// Observer, when non-nil, receives one event per failover decision
|
||||
// (failed attempt, bench, benched-skip) — the hook for persisting
|
||||
// failover logs. Called synchronously; keep it fast or hand off.
|
||||
Observer func(FailoverEvent)
|
||||
}
|
||||
|
||||
// FailoverEvent describes one failover decision in a chain.
|
||||
type FailoverEvent struct {
|
||||
// Target is the "provider/model" key the event concerns.
|
||||
Target string
|
||||
// Err is the failure (nil for benched-skip events).
|
||||
Err error
|
||||
// Class is the error classification (meaningful when Err != nil).
|
||||
Class llm.ErrorClass
|
||||
// Attempt is the 0-based attempt number on this target in this request.
|
||||
Attempt int
|
||||
// Benched reports that this failure benched the target.
|
||||
Benched bool
|
||||
// Skipped reports the target was skipped because it is benched.
|
||||
Skipped bool
|
||||
}
|
||||
|
||||
// DefaultTransientRetries is the default number of same-target retries
|
||||
@@ -180,6 +218,15 @@ func (r *Registry) RegisterAlias(name, spec string) {
|
||||
r.aliases[name] = spec
|
||||
}
|
||||
|
||||
// RegisterResolver appends a dynamic alias resolver (e.g. database-backed
|
||||
// tiers). Resolvers are consulted in registration order, after static
|
||||
// aliases.
|
||||
func (r *Registry) RegisterResolver(res Resolver) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.resolvers = append(r.resolvers, res)
|
||||
}
|
||||
|
||||
// RegisterScheme adds or replaces an env-DSN scheme factory, letting
|
||||
// consumers wire custom provider kinds into LLM_* definitions.
|
||||
func (r *Registry) RegisterScheme(scheme string, factory SchemeFactory) {
|
||||
|
||||
Reference in New Issue
Block a user