Documentation
¶
Overview ¶
Package virtualmodels unifies model aliases (redirects) and model access overrides (policies) behind one entity, the virtual model, persisted in a single virtual_models table.
A row with Targets is a REDIRECT: Source is a new addressable name that rewrites to one or more real models. A redirect with a single target is a plain alias; a redirect with several targets is load balanced: Strategy picks the target a request is sent to first, and the remaining available targets are its failover chain when that attempt fails. A target may name another virtual model (a chain) or the redirect's own Source, which stands for the concrete model the redirect shadows. A row without Targets is an ACCESS POLICY: Source is a scoped selector over existing models, gated by UserPaths.
The Service is a single native engine: it operates directly on VirtualModel rows behind one in-memory snapshot, serving both redirect resolution and policy authorization without composing other engines.
Index ¶
- Constants
- Variables
- func IsValidationError(err error) bool
- type AccessPolicy
- type BatchPreparer
- type Catalog
- type ChatExecutor
- type ChatExecutorProvider
- type EffectiveState
- type MongoDBStore
- func (s *MongoDBStore) Close() error
- func (s *MongoDBStore) Delete(ctx context.Context, source string) error
- func (s *MongoDBStore) Get(ctx context.Context, source string) (*VirtualModel, error)
- func (s *MongoDBStore) List(ctx context.Context) ([]VirtualModel, error)
- func (s *MongoDBStore) Upsert(ctx context.Context, vm VirtualModel) error
- type Option
- type Resolution
- type Result
- type RouteResolver
- type SQLStore
- func (s *SQLStore) Close() error
- func (s *SQLStore) Delete(ctx context.Context, source string) error
- func (s *SQLStore) Get(ctx context.Context, source string) (*VirtualModel, error)
- func (s *SQLStore) List(ctx context.Context) ([]VirtualModel, error)
- func (s *SQLStore) Upsert(ctx context.Context, vm VirtualModel) error
- type Service
- func (s *Service) AllowsModel(ctx context.Context, selector core.ModelSelector) bool
- func (s *Service) Delete(ctx context.Context, source string) error
- func (s *Service) EffectiveState(selector core.ModelSelector) EffectiveState
- func (s *Service) EnabledByDefault() bool
- func (s *Service) ExposedModels() []core.Model
- func (s *Service) ExposedModelsFiltered(allow func(core.ModelSelector) bool) []core.Model
- func (s *Service) ExposedModelsForUserPath(userPath string, allow func(core.ModelSelector) bool) []core.Model
- func (s *Service) FilterPublicModels(ctx context.Context, models []core.Model) []core.Model
- func (s *Service) Get(source string) (*VirtualModel, bool)
- func (s *Service) List() []VirtualModel
- func (s *Service) ListViews() []View
- func (s *Service) Refresh(ctx context.Context) error
- func (s *Service) Rename(ctx context.Context, oldSource string, vm VirtualModel) error
- func (s *Service) ResolveFailovers(resolution *core.RequestModelResolution, _ core.Operation) []core.ModelSelector
- func (s *Service) ResolveModel(requested core.RequestedModelSelector) (core.ModelSelector, bool, error)
- func (s *Service) ResolveModelForUserPath(ctx context.Context, requested core.RequestedModelSelector) (core.ModelSelector, bool, error)
- func (s *Service) ResolveRefreshTarget(requested core.RequestedModelSelector) (core.ModelSelector, bool, error)
- func (s *Service) ResolveSlowdown(ctx context.Context, requested core.RequestedModelSelector, ...) float64
- func (s *Service) ResolveUpsertEnabled(source, oldSource string, requested *bool) bool
- func (s *Service) SetAccessPolicy(policy AccessPolicy)
- func (s *Service) SetConfigModels(models []VirtualModel)
- func (s *Service) SetRouteResolver(resolver RouteResolver)
- func (s *Service) SetRouteSelector(selector ext.RouteSelector)
- func (s *Service) SetTargetCapacity(capacity func(qualifiedModel string) bool)
- func (s *Service) StartBackgroundRefresh(interval time.Duration) func()
- func (s *Service) Upsert(ctx context.Context, vm VirtualModel) error
- func (s *Service) ValidateManagedConfig(declaredProviders []string) error
- func (s *Service) ValidateModelAccess(ctx context.Context, selector core.ModelSelector) error
- type Store
- type Target
- type View
- type VirtualModel
Constants ¶
const ( // StrategyRoundRobin rotates across targets, honoring per-target Weight. It is // the default when Strategy is empty. StrategyRoundRobin = "round_robin" // StrategyCost always routes to the cheapest currently-available target, ranked // by the model registry's per-token pricing. StrategyCost = "cost" // StrategyAdaptive delegates target choice to a route selector registered // through the ext package (a pro extension). Without a registered selector // it behaves exactly like round_robin, so configs stay portable between // core and extended builds. StrategyAdaptive = "adaptive" // StrategyFailover always routes to the first currently-available target // in declared order: the target list is a priority list, and lower legs // serve only while every leg above them is unavailable or fails. StrategyFailover = "failover" // StrategyPlugin delegates target choice to the routing-strategy plugin // named by VirtualModel.StrategyPlugin, configured per virtual model // through StrategyConfig. Target weights are ignored: the plugin decides. // When the plugin is missing, misconfigured, declines, panics, or times // out, the redirect falls back to weighted round robin. StrategyPlugin = "plugin" )
Load-balancing strategies for multi-target redirects.
const ( KindRedirect = "redirect" KindPolicy = "policy" )
Role kinds for the admin view.
const ( MinSlowdownFactor = 0.1 MaxSlowdownFactor = 10.0 )
const MaxChainDepth = 8
MaxChainDepth caps how many virtual models a redirect may pass through before reaching a concrete model. A redirect whose targets are all concrete has depth 1; each virtual model hop adds one.
Variables ¶
var ErrNotFound = errors.New("virtual model not found")
ErrNotFound indicates a requested virtual model was not found.
Functions ¶
func IsValidationError ¶
IsValidationError reports whether err is a validation error.
Types ¶
type AccessPolicy ¶ added in v0.1.84
type AccessPolicy interface {
AllowsModel(ctx context.Context, selector core.ModelSelector) bool
}
AccessPolicy narrows model access per request from the subject side.
type BatchPreparer ¶
type BatchPreparer struct {
// contains filtered or unexported fields
}
BatchPreparer rewrites redirect (alias) sources for native batch subrequests and validates model access before provider submission. It is the combined replacement for the two old preparers (alias rewrite then access validation).
func NewBatchPreparer ¶
func NewBatchPreparer(provider core.RoutableProvider, service *Service) *BatchPreparer
NewBatchPreparer creates the combined redirect-rewrite + access-validation batch preparer.
func (*BatchPreparer) PrepareBatchRequest ¶
func (p *BatchPreparer) PrepareBatchRequest(ctx context.Context, providerType string, req *core.BatchRequest) (*core.BatchRewriteResult, error)
PrepareBatchRequest rewrites redirect sources for inline and file-backed batch items and validates model access for each resolved selector.
type Catalog ¶
type Catalog interface {
Supports(model string) bool
// ModelAvailable is Supports narrowed to providers whose inventory is
// fresh: target selection uses it so load balancing routes around a
// provider whose latest model refresh failed.
ModelAvailable(model string) bool
GetProviderType(model string) string
LookupModel(model string) (*core.Model, bool)
ProviderNames() []string
}
Catalog is the combined catalog surface the native engine needs.
type ChatExecutor ¶
type ChatExecutor struct {
// contains filtered or unexported fields
}
ChatExecutor applies virtual-model redirects to transport-free chat completions before delegating to the wrapped provider. It is the narrow replacement for the former redirect-aware Provider decorator, whose only production role was serving as the guardrail auxiliary-LLM executor (guardrails.ChatCompletionExecutor is a single-method interface).
func NewChatExecutor ¶
func NewChatExecutor(inner ChatExecutorProvider, service *Service) *ChatExecutor
NewChatExecutor creates a redirect-aware chat executor over inner.
func (*ChatExecutor) ChatCompletion ¶
func (e *ChatExecutor) ChatCompletion(ctx context.Context, req *core.ChatRequest) (*core.ChatResponse, error)
ChatCompletion resolves the request's redirect (user-path aware) and delegates the rewritten request to the wrapped provider.
type ChatExecutorProvider ¶
type ChatExecutorProvider interface {
ChatCompletion(ctx context.Context, req *core.ChatRequest) (*core.ChatResponse, error)
// contains filtered or unexported methods
}
ChatExecutorProvider is the slice of the router the executor needs: model support checks for redirect validation and chat dispatch.
type EffectiveState ¶
type EffectiveState struct {
Selector string `json:"selector"`
ProviderName string `json:"provider_name,omitempty"`
Model string `json:"model,omitempty"`
DefaultEnabled bool `json:"default_enabled"`
Enabled bool `json:"enabled"`
UserPaths []string `json:"user_paths,omitempty"`
}
EffectiveState is the compiled access decision for one concrete selector.
type MongoDBStore ¶
type MongoDBStore struct {
// contains filtered or unexported fields
}
MongoDBStore stores virtual models in MongoDB.
func NewMongoDBStore ¶
func NewMongoDBStore(database *mongo.Database) (*MongoDBStore, error)
NewMongoDBStore creates collection indexes if needed.
func (*MongoDBStore) Close ¶
func (s *MongoDBStore) Close() error
func (*MongoDBStore) Delete ¶
func (s *MongoDBStore) Delete(ctx context.Context, source string) error
func (*MongoDBStore) Get ¶
func (s *MongoDBStore) Get(ctx context.Context, source string) (*VirtualModel, error)
func (*MongoDBStore) List ¶
func (s *MongoDBStore) List(ctx context.Context) ([]VirtualModel, error)
func (*MongoDBStore) Upsert ¶
func (s *MongoDBStore) Upsert(ctx context.Context, vm VirtualModel) error
type Option ¶ added in v0.1.90
type Option func(*Service)
Option customizes the service New builds.
func WithRouteResolver ¶ added in v0.1.90
func WithRouteResolver(resolver RouteResolver) Option
WithRouteResolver installs the routing-strategy plugin resolver before the declarative virtual models are validated, so a managed redirect with an invalid strategy_config fails startup loudly.
type Resolution ¶
type Resolution struct {
Requested core.ModelSelector
Resolved core.ModelSelector
Source string
}
Resolution captures the requested selector and the concrete selector chosen after redirect resolution. Source is the redirect name that matched, if any.
type Result ¶
Result holds the initialized virtual models service and any owned resources.
type RouteResolver ¶ added in v0.1.90
type RouteResolver interface {
// Strategy returns the initialized strategy registered under name, or an
// error when no such route plugin is loaded or its instance failed to
// initialize.
Strategy(name string) (pluginapi.RouteStrategy, *plugins.Instance, error)
// Release hands back the instance Strategy returned once the call that
// used it is over, so a replaced instance can be closed.
Release(inst *plugins.Instance)
// ValidateRouteConfig checks a virtual model's strategy_config against
// the plugin's route-scoped fields and returns the canonical JSON with
// defaults applied.
ValidateRouteConfig(name string, cfg map[string]any) (json.RawMessage, error)
}
RouteResolver serves routing-strategy plugins to redirects using the plugin strategy. plugins.RouteResolver implements it.
type SQLStore ¶ added in v0.1.60
type SQLStore struct {
// contains filtered or unexported fields
}
SQLStore stores virtual models in a SQL database.
func NewSQLStore ¶ added in v0.1.60
NewSQLStore creates the virtual_models table and indexes if needed.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the single native engine over the virtual_models store. It serves both redirect resolution (alias behavior) and policy authorization (access override behavior) from one atomically swapped in-memory snapshot.
func NewService ¶
NewService creates a virtual models service backed by the store and catalog. defaultEnabled is the process-wide model availability default consulted when no policy matches.
func (*Service) AllowsModel ¶
AllowsModel reports whether selector is available for the effective request user path.
func (*Service) EffectiveState ¶
func (s *Service) EffectiveState(selector core.ModelSelector) EffectiveState
EffectiveState resolves the compiled access state for one concrete selector.
func (*Service) EnabledByDefault ¶
EnabledByDefault reports the process-wide model availability default.
func (*Service) ExposedModels ¶
ExposedModels returns enabled redirects projected as model-list entries.
func (*Service) ExposedModelsFiltered ¶
ExposedModelsFiltered returns enabled redirects projected as model-list entries, filtered by the concrete target selector.
func (*Service) ExposedModelsForUserPath ¶
func (s *Service) ExposedModelsForUserPath(userPath string, allow func(core.ModelSelector) bool) []core.Model
ExposedModelsForUserPath is ExposedModelsFiltered plus per-redirect user_path scoping: a redirect carrying user_paths is hidden from callers it would not apply to, so a scoped alias is not listed (its name exposed) to callers outside its scope even though resolution would fall through for them.
func (*Service) FilterPublicModels ¶
FilterPublicModels removes models that are unavailable for the effective request user path.
func (*Service) Get ¶
func (s *Service) Get(source string) (*VirtualModel, bool)
Get returns one cached virtual model by source.
func (*Service) List ¶
func (s *Service) List() []VirtualModel
List returns all cached virtual models sorted by source.
func (*Service) ListViews ¶
ListViews returns all virtual models (redirects and policies) for the admin UI.
func (*Service) Refresh ¶
Refresh reloads virtual models from storage and atomically swaps the snapshot.
func (*Service) Rename ¶
Rename moves an existing virtual model to a new source: it stores the row under the new source and removes the old one, validating and refreshing like Upsert with rollback on failure. A no-op rename (old == new after normalization) delegates to Upsert. The new source must be free — renaming onto an existing row is rejected rather than silently overwriting it, since source is the primary key on every store backend.
func (*Service) ResolveFailovers ¶ added in v0.1.84
func (s *Service) ResolveFailovers(resolution *core.RequestModelResolution, _ core.Operation) []core.ModelSelector
ResolveFailovers returns the failover chain for a request that resolved through a redirect: the concrete models behind the redirect's remaining available targets, in declared order and descending chained virtual models, minus the model the request was sent to first. The redirect's strategy only chooses that first target; every other available target is a failover leg, so a load balancer and a priority list fail over the same way. A redirect with failover switched off, and requests that did not go through a redirect, have no chain; a redirect whose single target is a chained redirect keeps that subtree's leaves as its chain — except a request that named its provider explicitly for a model shadowed by a redirect listing that model among its targets (see snapshot.failoverEntry).
func (*Service) ResolveModel ¶
func (s *Service) ResolveModel(requested core.RequestedModelSelector) (core.ModelSelector, bool, error)
ResolveModel resolves a requested selector and returns the concrete selector chosen for execution. It does not consult user_paths; scoped redirects are applied by ResolveModelForUserPath on the request path.
func (*Service) ResolveModelForUserPath ¶
func (s *Service) ResolveModelForUserPath(ctx context.Context, requested core.RequestedModelSelector) (core.ModelSelector, bool, error)
ResolveModelForUserPath resolves a requested selector honoring per-redirect user_paths against the effective request user path. A redirect scoped to user_paths the caller does not match falls through to the literal model name.
func (*Service) ResolveRefreshTarget ¶
func (s *Service) ResolveRefreshTarget(requested core.RequestedModelSelector) (core.ModelSelector, bool, error)
ResolveRefreshTarget returns a redirect target without consulting the current catalog so callers can refresh an unavailable target provider before normal resolution is retried.
func (*Service) ResolveSlowdown ¶ added in v0.1.74
func (s *Service) ResolveSlowdown( ctx context.Context, requested core.RequestedModelSelector, resolved core.ModelSelector, ) float64
ResolveSlowdown returns the request-scoped extra-time factor for a resolved model. A matching alias setting takes precedence over its concrete target; otherwise the normal exact/provider/model/global policy precedence applies. The context makes the lookup ready for user-path-specific settings without changing the request execution interface later.
func (*Service) ResolveUpsertEnabled ¶
ResolveUpsertEnabled returns the enabled flag an upsert should persist when the request may omit it: the requested value when present; otherwise the stored value for source (or, on a rename, for oldSource, since the new source does not exist yet); defaulting to true for new rows.
func (*Service) SetAccessPolicy ¶ added in v0.1.84
func (s *Service) SetAccessPolicy(policy AccessPolicy)
SetAccessPolicy installs the subject-side access policy consulted by AllowsModel and ValidateModelAccess. Must be called before serving.
func (*Service) SetConfigModels ¶
func (s *Service) SetConfigModels(models []VirtualModel)
SetConfigModels installs the declarative (config.yaml / VIRTUAL_MODELS) virtual models that override store rows of the same source. Call it before the first Refresh, then ValidateManagedConfig to reject invalid declarations at startup.
func (*Service) SetRouteResolver ¶ added in v0.1.90
func (s *Service) SetRouteResolver(resolver RouteResolver)
SetRouteResolver installs the routing-strategy plugin resolver consulted by redirects using the plugin strategy. Must be called before the service starts resolving requests.
func (*Service) SetRouteSelector ¶ added in v0.1.65
func (s *Service) SetRouteSelector(selector ext.RouteSelector)
SetRouteSelector installs the extension route selector consulted by redirects using the adaptive strategy. Must be called before the service starts resolving requests.
func (*Service) SetTargetCapacity ¶
SetTargetCapacity installs the rate-limit capacity probe consulted by load balancing. Must be called before the service starts resolving requests.
func (*Service) StartBackgroundRefresh ¶
StartBackgroundRefresh periodically reloads virtual models until stopped.
func (*Service) Upsert ¶
func (s *Service) Upsert(ctx context.Context, vm VirtualModel) error
Upsert validates and stores one virtual model, replacing any existing row at the same source even when its kind changes. A policy with no metadata whose access state is identical to what it would inherit is deleted instead of persisting a redundant row.
func (*Service) ValidateManagedConfig ¶
ValidateManagedConfig checks that every declarative config redirect satisfies the catalog-independent redirect invariants (valid selector, not solely a self-target, no misspelled target provider), so a malformed IaC entry fails startup loudly. Chain cycles and depth are rejected earlier, by the initial Refresh. declaredProviders lists the names present in the providers configuration even when they did not register — e.g. their credentials are unset in this environment — so a config shared across environments still boots; such targets only warn and stay unavailable. Call it once after the initial Refresh.
It deliberately does NOT require targets to be catalog-supported: the provider model catalog loads asynchronously and may still be warming when this runs, and an unavailable target is skipped at resolve time like any other redirect target (the background ticker also skips this gate so a transient provider-catalog gap cannot freeze the snapshot). Gating startup on availability would abort an otherwise-valid declaration on a cold cache or a momentarily-unreachable provider — availability is runtime state, not a property of the declaration.
func (*Service) ValidateModelAccess ¶
ValidateModelAccess returns a typed request error when selector is not available.
type Store ¶
type Store interface {
List(ctx context.Context) ([]VirtualModel, error)
Get(ctx context.Context, source string) (*VirtualModel, error)
Upsert(ctx context.Context, vm VirtualModel) error
Delete(ctx context.Context, source string) error
Close() error
}
Store defines persistence operations for virtual models.
type Target ¶
type Target struct {
Provider string `json:"provider,omitempty" bson:"provider,omitempty"`
Model string `json:"model" bson:"model"`
Weight float64 `json:"weight,omitempty" bson:"weight,omitempty"`
}
Target is one concrete (provider, model) destination of a redirect.
Weight biases the round-robin strategy: a target with weight 2 receives twice the share of a target with weight 1. A non-positive or unset weight is treated as 1, so single-target and unweighted redirects behave identically to before. Weight is ignored by the cost strategy, which always picks the cheapest target.
type View ¶
type View struct {
Source string `json:"source"`
Kind string `json:"kind"`
Targets []Target `json:"targets,omitempty"`
Strategy string `json:"strategy,omitempty"`
StrategyPlugin string `json:"strategy_plugin,omitempty"`
StrategyConfig map[string]any `json:"strategy_config,omitempty"`
SessionAffinity *bool `json:"session_affinity,omitempty"`
Failover *bool `json:"failover,omitempty"`
ProviderName string `json:"provider_name,omitempty"`
Model string `json:"model,omitempty"`
UserPaths []string `json:"user_paths,omitempty"`
Description string `json:"description,omitempty"`
// Slowdown is an extra-time factor from 0.1 to 10; zero disables it.
Slowdown *float64 `json:"slowdown,omitempty"`
Enabled bool `json:"enabled"`
Managed bool `json:"managed,omitempty"`
ResolvedModel string `json:"resolved_model,omitempty"`
ProviderType string `json:"provider_type,omitempty"`
Valid bool `json:"valid,omitempty"`
ScopeKind string `json:"scope_kind,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
View is the admin-facing representation of one virtual model.
type VirtualModel ¶
type VirtualModel struct {
Source string `json:"source" bson:"_id"`
Targets []Target `json:"targets,omitempty" bson:"targets,omitempty"`
Strategy string `json:"strategy,omitempty" bson:"strategy,omitempty"`
// StrategyPlugin names the routing-strategy plugin consulted when Strategy
// is "plugin"; StrategyConfig is that plugin's per-virtual-model
// configuration, validated against its route-scoped fields. Both are
// cleared for every other strategy.
StrategyPlugin string `json:"strategy_plugin,omitempty" bson:"strategy_plugin,omitempty"`
StrategyConfig map[string]any `json:"strategy_config,omitempty" bson:"strategy_config,omitempty"`
ProviderName string `json:"provider_name,omitempty" bson:"provider_name,omitempty"`
Model string `json:"model,omitempty" bson:"model,omitempty"`
UserPaths []string `json:"user_paths,omitempty" bson:"user_paths,omitempty"`
Description string `json:"description,omitempty" bson:"description,omitempty"`
// Slowdown is an extra-time factor from 0.1 to 10; zero disables it. Nil
// leaves the setting unspecified so an alias can inherit its target model.
Slowdown *float64 `json:"slowdown,omitempty" bson:"slowdown,omitempty"`
Enabled bool `json:"enabled" bson:"enabled"`
// SessionAffinity keeps requests of one detected session on the target that
// served it before, while that target stays available. Tri-state: nil means
// enabled (the default); explicit false restores stateless balancing.
SessionAffinity *bool `json:"session_affinity,omitempty" bson:"session_affinity,omitempty"`
// Failover retries a failed request against the redirect's remaining
// targets. Tri-state: nil means enabled (the default); explicit false
// serves the chosen target only. The failover strategy always fails over.
Failover *bool `json:"failover,omitempty" bson:"failover,omitempty"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
// Managed marks a virtual model supplied declaratively through config.yaml or
// the VIRTUAL_MODELS env var rather than the admin store. It is an in-memory
// flag only: stores never read or write it. Managed rows override store rows
// of the same Source and are read-only to the admin API.
Managed bool `json:"managed,omitempty" bson:"-"`
}
VirtualModel is one operator-defined model entry.
func ConfigModels ¶
func ConfigModels(entries []config.VirtualModelConfig) []VirtualModel
ConfigModels converts declarative config.yaml / VIRTUAL_MODELS entries into virtual model rows marked as managed. The rows are fully validated when the service builds its snapshot, so an invalid declaration fails startup loudly.
func FailoverConfigModels ¶ added in v0.1.84
func FailoverConfigModels(cfg config.FailoverConfig, declared, stored []VirtualModel) []VirtualModel
FailoverConfigModels translates the deprecated `failover` rules block (failover.rules, manual_rules_path, FAILOVER_RULES_JSON) into managed failover-strategy virtual models, so a configuration written for the standalone failover feature keeps routing the same way. Each rule becomes a redirect that shadows its primary model: the primary is the first target and the fallbacks follow in order. A primary listed in disabled_models is skipped. Sources already declared under virtual_models are left to that declaration, and sources that exist in the store are left to the stored virtual model, with a warning that names it, so the rule's fallbacks can be moved into it by hand.
func (VirtualModel) IsRedirect ¶
func (v VirtualModel) IsRedirect() bool
IsRedirect reports whether this row redirects (has at least one target).
func (VirtualModel) Kind ¶
func (v VirtualModel) Kind() string
Kind returns the derived role: "redirect" or "policy".