mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 13:26:33 +02:00
feat(provider): alias common provider names and clarify Bedrock 403
Map short/alternate provider names (bedrock -> amazon-bedrock, vertex, gemini, azure, copilot, codex, ...) to their canonical ids in Resolve so an alias is never treated as unknown and silently downgraded to anthropic. Add a region-aware hint to Bedrock 403 responses on the bearer route.
This commit is contained in:
parent
ea58887bfa
commit
ec5eb20ce9
3 changed files with 96 additions and 2 deletions
|
|
@ -213,6 +213,51 @@ func isKnownProvider(name string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// providerAliases maps common short / alternate provider names to the
|
||||
// canonical id in knownProviders. Users (and other agents) reach for
|
||||
// "bedrock" or "vertex" far more naturally than the fully-qualified
|
||||
// "amazon-bedrock" / "google-vertex"; without this mapping an alias is
|
||||
// treated as an unknown provider and Resolve silently falls back to
|
||||
// anthropic, producing a misleading "no credential for anthropic" error
|
||||
// after the user explicitly picked, say, bedrock.
|
||||
var providerAliases = map[string]string{
|
||||
"bedrock": "amazon-bedrock",
|
||||
"aws-bedrock": "amazon-bedrock",
|
||||
"amazon": "amazon-bedrock",
|
||||
"vertex": "google-vertex",
|
||||
"gcp-vertex": "google-vertex",
|
||||
"gemini": "google",
|
||||
"googleai": "google",
|
||||
"google-ai": "google",
|
||||
"azure": "azure-openai-responses",
|
||||
"azure-openai": "azure-openai-responses",
|
||||
"copilot": "github-copilot",
|
||||
"github": "github-copilot",
|
||||
"codex": "openai-codex",
|
||||
"moonshot": "moonshotai",
|
||||
"kimi-code": "kimi",
|
||||
"ai-gateway": "vercel-ai-gateway",
|
||||
"vercel": "vercel-ai-gateway",
|
||||
"cloudflare": "cloudflare-workers-ai",
|
||||
"workers-ai": "cloudflare-workers-ai",
|
||||
"hf": "huggingface",
|
||||
}
|
||||
|
||||
// canonicalProvider normalises a user-supplied provider name: trims
|
||||
// surrounding whitespace, lower-cases it, and resolves any known alias
|
||||
// to its canonical id. Unknown names are returned trimmed/lower-cased
|
||||
// and unchanged so the existing unknown-provider handling still runs.
|
||||
func canonicalProvider(name string) string {
|
||||
n := strings.ToLower(strings.TrimSpace(name))
|
||||
if n == "" {
|
||||
return n
|
||||
}
|
||||
if canon, ok := providerAliases[n]; ok {
|
||||
return canon
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Resolve merges args, config, and env into a Resolved set.
|
||||
//
|
||||
// Unlike the earlier version, Resolve NEVER returns an error for
|
||||
|
|
@ -223,7 +268,11 @@ func Resolve(args Args, requireCred bool) (Resolved, error) {
|
|||
cfg, _ := LoadConfig()
|
||||
|
||||
// User-requested provider (explicit > config > default).
|
||||
provName := firstNonEmpty(args.Provider, cfg.Provider, "anthropic")
|
||||
// Normalise common aliases (e.g. "bedrock" -> "amazon-bedrock")
|
||||
// before validation so an alias is never mistaken for an unknown
|
||||
// provider and silently downgraded to anthropic.
|
||||
argProvider := canonicalProvider(args.Provider)
|
||||
provName := firstNonEmpty(argProvider, canonicalProvider(cfg.Provider), "anthropic")
|
||||
if !isKnownProvider(provName) {
|
||||
// Unknown provider (maybe removed or renamed). Fall back to
|
||||
// the first provider that has credentials, or anthropic.
|
||||
|
|
|
|||
|
|
@ -108,3 +108,37 @@ func TestResolveExplicitFlagStaleDoesNotRepairConfig(t *testing.T) {
|
|||
t.Errorf("config.json was clobbered (was %q; now %q)", good, cfg.Model)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalProviderResolvesAliases(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"bedrock": "amazon-bedrock",
|
||||
"AWS-Bedrock": "amazon-bedrock",
|
||||
" bedrock ": "amazon-bedrock",
|
||||
"vertex": "google-vertex",
|
||||
"gemini": "google",
|
||||
"azure": "azure-openai-responses",
|
||||
"copilot": "github-copilot",
|
||||
"codex": "openai-codex",
|
||||
"moonshot": "moonshotai",
|
||||
"vercel": "vercel-ai-gateway",
|
||||
"hf": "huggingface",
|
||||
"anthropic": "anthropic", // canonical passes through
|
||||
"amazon-bedrock": "amazon-bedrock", // already canonical
|
||||
"totally-unknown": "totally-unknown", // unknown returned unchanged (lowered)
|
||||
"Totally-UNKNOWN": "totally-unknown",
|
||||
"": "",
|
||||
}
|
||||
for in, want := range cases {
|
||||
if got := canonicalProvider(in); got != want {
|
||||
t.Errorf("canonicalProvider(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalProviderAliasesAreKnown(t *testing.T) {
|
||||
for alias, canon := range providerAliases {
|
||||
if !isKnownProvider(canon) {
|
||||
t.Errorf("alias %q maps to %q which is not a known provider", alias, canon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,7 +317,18 @@ func (c *bedrockClient) Stream(ctx context.Context, req Request) (<-chan Event,
|
|||
if resp.StatusCode != http.StatusOK {
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("bedrock: http %d: %s", resp.StatusCode, strings.TrimSpace(string(b)))
|
||||
msg := strings.TrimSpace(string(b))
|
||||
// A 403 on the bearer route is almost always a region mismatch:
|
||||
// short-term Bedrock API keys are scoped to the region of the
|
||||
// console session that minted them, but zot defaults to
|
||||
// us-east-1. Surface the resolved region and the fix so the user
|
||||
// is not left guessing why a freshly-copied key is "invalid".
|
||||
if resp.StatusCode == http.StatusForbidden && c.bearerToken != "" {
|
||||
return nil, fmt.Errorf(
|
||||
"bedrock: http 403 (region=%s): %s\nhint: Bedrock API keys are region-scoped. If your key was created in another region, set AWS_REGION (e.g. AWS_REGION=eu-central-1) or pass --base-url https://bedrock-runtime.<region>.amazonaws.com",
|
||||
c.region, msg)
|
||||
}
|
||||
return nil, fmt.Errorf("bedrock: http %d: %s", resp.StatusCode, msg)
|
||||
}
|
||||
out := make(chan Event, 16)
|
||||
go c.runStream(ctx, resp, req, out)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue