mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-16 05:37:28 +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:
@@ -194,6 +194,14 @@ type adminGroupView struct {
|
||||
MemberCount int `json:"memberCount"`
|
||||
}
|
||||
|
||||
// adminAliasView is one configured model alias's row in GET
|
||||
// /admin/api/overview (spec §5, v0.2): the alias->target pair exactly as
|
||||
// an operator wrote it. No secrets involved.
|
||||
type adminAliasView struct {
|
||||
Alias string `json:"alias"`
|
||||
Target string `json:"target"`
|
||||
}
|
||||
|
||||
// adminOverviewResponse is the full body of GET /admin/api/overview (spec
|
||||
// §4, v0.2). Every slice is sorted and built fresh per request — no
|
||||
// caching of the response itself — so the dashboard's 5s poll always
|
||||
@@ -202,6 +210,7 @@ type adminOverviewResponse struct {
|
||||
Version string `json:"version"`
|
||||
Providers []adminProviderView `json:"providers"`
|
||||
Groups []adminGroupView `json:"groups"`
|
||||
Aliases []adminAliasView `json:"aliases"`
|
||||
Redis adminRedisView `json:"redis"`
|
||||
Cache adminCacheView `json:"cache"`
|
||||
}
|
||||
@@ -237,11 +246,18 @@ func (g *Gateway) buildAdminOverview() adminOverviewResponse {
|
||||
groups[i] = adminGroupView{Name: gs.name, Limits: gs.limits, MemberCount: gs.memberCount}
|
||||
}
|
||||
|
||||
aliasSnaps := g.registry.aliasSnapshot()
|
||||
aliases := make([]adminAliasView, len(aliasSnaps))
|
||||
for i, a := range aliasSnaps {
|
||||
aliases[i] = adminAliasView(a) // identical underlying field shape (alias, target string), differing only in json tags
|
||||
}
|
||||
|
||||
return adminOverviewResponse{
|
||||
Providers: providers,
|
||||
Redis: adminRedisView{Configured: configured, LastErr: redisLastErr, LastErrAt: redisLastErrAt},
|
||||
Cache: adminCacheView{Enabled: cacheEnabled, TTL: ttl},
|
||||
Groups: groups,
|
||||
Aliases: aliases,
|
||||
Version: pluginVersion,
|
||||
}
|
||||
}
|
||||
@@ -443,6 +459,13 @@ const adminPageHTML = `<!doctype html>
|
||||
</tr></thead><tbody></tbody></table>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Model aliases</h2>
|
||||
<table id="aliases"><thead><tr>
|
||||
<th>Alias</th><th>Target</th>
|
||||
</tr></thead><tbody></tbody></table>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Infrastructure</h2>
|
||||
<div id="infra"></div>
|
||||
@@ -542,6 +565,13 @@ const adminPageHTML = `<!doctype html>
|
||||
return tr;
|
||||
});
|
||||
|
||||
setRows("aliases", data.aliases, function (a) {
|
||||
var tr = document.createElement("tr");
|
||||
tr.appendChild(el("td", a.alias));
|
||||
tr.appendChild(el("td", a.target));
|
||||
return tr;
|
||||
});
|
||||
|
||||
var infra = document.getElementById("infra");
|
||||
while (infra.firstChild) infra.removeChild(infra.firstChild);
|
||||
var redisText = "Redis: " + (data.redis.configured ? "configured" : "not configured");
|
||||
|
||||
Reference in New Issue
Block a user