Compare commits

...
Author SHA1 Message Date
Vasanthdev2004 2799cd0280 fix(tui): keep the profile name on /model switch so the stored key resolves
normalizeProfileForProvider rewrote profile.Name to the catalog provider.ID
whenever normalizeIdentity was true — which includes any first-party provider
whose saved baseURL matches the catalog default. The credential store is keyed
by profile.Name, and this rewrite runs before profileWithCredential, so the
stored-key lookup missed and the rebuilt provider went out with no key: a 401 on
every /model switch for a profile named differently from its catalog id (e.g.
"my-openai").

Only canonicalize an empty or generic placeholder name now; a real user-provided
name is preserved. CatalogID canonicalization is unchanged (it is not the store
key). Adds regression tests covering a custom-named first-party profile surviving
normalization and placeholder names still canonicalizing.

Fixes #440
2026-07-03 15:34:44 +05:30
2 changed files with 55 additions and 1 deletions

No files matched your search

+9 -1
View File
@@ -619,7 +619,15 @@ func (m model) normalizeProfileForProvider(provider providercatalog.Descriptor)
strings.TrimSpace(profile.Name) == "" ||
strings.TrimSpace(profile.CatalogID) == ""
if normalizeIdentity {
profile.Name = provider.ID
// Only canonicalize an empty or generic placeholder name — never clobber a
// real user-provided profile name. The credential store is keyed by
// profile.Name (SecureProviderProfile stores under Name), so rewriting it
// here made the later profileWithCredential lookup miss the saved key and
// rebuild a keyless provider — a 401 on every /model switch for any profile
// named differently from its catalog id (e.g. "my-openai").
if strings.TrimSpace(profile.Name) == "" || genericProviderCatalogID(profile.Name) {
profile.Name = provider.ID
}
profile.CatalogID = provider.ID
}
if strings.TrimSpace(profile.BaseURL) == "" {
+46
View File
@@ -976,3 +976,49 @@ func TestSwitchProviderModelWarmsDiscoveryForTheNewProvider(t *testing.T) {
t.Fatal("switching to ollama should warm both the generic and ollama-specific discovery commands")
}
}
func TestNormalizeProfileForProviderPreservesCustomName(t *testing.T) {
openai, ok := providercatalog.Get("openai")
if !ok {
t.Fatal("openai descriptor missing from catalog")
}
// A first-party provider set up with a custom profile name and the baseURL at
// the catalog default. normalizeIdentity is true here (baseURL matches), but
// the real name must survive: it is the credential-store key, so clobbering it
// dropped the saved key and 401'd every /model switch (issue #440).
m := model{providerProfile: config.ProviderProfile{
Name: "my-openai",
CatalogID: "openai",
BaseURL: openai.DefaultBaseURL,
APIKeyStored: true,
}}
got := m.normalizeProfileForProvider(openai)
if got.Name != "my-openai" {
t.Fatalf("Name = %q, want it preserved as %q (credential store is keyed by Name)", got.Name, "my-openai")
}
if got.CatalogID != "openai" {
t.Fatalf("CatalogID = %q, want it canonicalized to %q", got.CatalogID, "openai")
}
}
func TestNormalizeProfileForProviderCanonicalizesPlaceholderName(t *testing.T) {
openai, ok := providercatalog.Get("openai")
if !ok {
t.Fatal("openai descriptor missing from catalog")
}
// Empty and generic placeholder names still get canonicalized to the catalog
// id — those carry no meaningful credential-store key to preserve.
for _, name := range []string{"", "custom-openai-compatible"} {
m := model{providerProfile: config.ProviderProfile{
Name: name,
BaseURL: openai.DefaultBaseURL,
CatalogID: name,
}}
got := m.normalizeProfileForProvider(openai)
if got.Name != "openai" {
t.Fatalf("Name = %q for placeholder %q, want canonicalized to %q", got.Name, name, "openai")
}
}
}