mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-21 05:57:53 +00:00
feat: operator-defined model aliases
Spec §5 (v0.2): Config.ModelAliases map[string]string, alias-first resolution precedence ahead of provider-prefix splitting, alias-OR-target authorization, lazy target resolution with a 404 naming both ids, /v1/models listing, admin overview alias table (JSON + dashboard), and construction-time validation (shape, prefix-shadow, explicit-model collision, chain, empty target). routes_unified.go/routes_media.go needed no resolution-call-site changes - both already pass the client's raw requested id through to registry.resolve, so aliases inherit the __alias echo and multipart rewrite machinery for free. cache.go needed no changes either - cacheKey already hashes requestedModel, so two aliases resolving to the same target already produce distinct cache entries. Adds registry_test.go validation table + precedence/authz/lazy-404/ listing coverage, a dedicated cache-key split test, chat/embeddings/ images/audio-transcriptions end-to-end tests, an extended admin overview test, and an integration test covering the openai-type vs anthropic-type echo asymmetry under real Traefik+Yaegi.
This commit is contained in:
+12
-3
@@ -255,10 +255,19 @@ func unifiedCostMicros(canonical, bare string, u usage, overrides map[string]*Mo
|
||||
// writeModelResolveError maps a modelRegistry.resolve error to its HTTP
|
||||
// envelope: errModelUnknown is a 404 (no configured provider knows this
|
||||
// model), errModelDenied is a 403 (a real model the caller's group cannot
|
||||
// use). Any other error is a defensive 500 — resolve's own contract
|
||||
// promises only these two sentinels, so reaching this branch would be a
|
||||
// programming error, not a client mistake.
|
||||
// use). A *aliasTargetError (spec §5, v0.2) is also a 404, but with its
|
||||
// own message naming both the alias and its unresolved target, checked
|
||||
// first via a plain type assertion — not errors.As, matching this
|
||||
// package's established yaegi-safe convention for a pointer error type
|
||||
// (registry.go's aliasTargetError doc comment). Any other error is a
|
||||
// defensive 500 — resolve's own contract promises only these sentinels/
|
||||
// types, so reaching this branch would be a programming error, not a
|
||||
// client mistake.
|
||||
func writeModelResolveError(w http.ResponseWriter, err error) {
|
||||
if aerr, ok := err.(*aliasTargetError); ok {
|
||||
writeOAIError(w, http.StatusNotFound, "invalid_request_error", aerr.Error())
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case errors.Is(err, errModelUnknown):
|
||||
writeOAIError(w, http.StatusNotFound, "invalid_request_error", "unknown model")
|
||||
|
||||
Reference in New Issue
Block a user