diff --git a/src/agents/model-ref-profile.test.ts b/src/agents/model-ref-profile.test.ts index f85c7d37675..f1eccdc431a 100644 --- a/src/agents/model-ref-profile.test.ts +++ b/src/agents/model-ref-profile.test.ts @@ -66,4 +66,23 @@ describe("splitTrailingAuthProfile", () => { profile: "work", }); }); + + it("keeps @q* quant suffixes in model ids", () => { + expect(splitTrailingAuthProfile("lmstudio-mb-pro/gemma-4-31b-it@q8_0")).toEqual({ + model: "lmstudio-mb-pro/gemma-4-31b-it@q8_0", + }); + }); + + it("supports auth profiles after @q* quant suffixes", () => { + expect(splitTrailingAuthProfile("lmstudio-mb-pro/gemma-4-31b-it@q8_0@work")).toEqual({ + model: "lmstudio-mb-pro/gemma-4-31b-it@q8_0", + profile: "work", + }); + }); + + it("keeps @4bit/@8bit quant suffixes in model ids", () => { + expect(splitTrailingAuthProfile("lmstudio-mb-pro/gemma-4-31b@4bit")).toEqual({ + model: "lmstudio-mb-pro/gemma-4-31b@4bit", + }); + }); }); diff --git a/src/agents/model-ref-profile.ts b/src/agents/model-ref-profile.ts index 7437a554590..519874e69d6 100644 --- a/src/agents/model-ref-profile.ts +++ b/src/agents/model-ref-profile.ts @@ -14,6 +14,9 @@ export function splitTrailingAuthProfile(raw: string): { } const versionSuffix = trimmed.slice(profileDelimiter + 1); + + // Keep well-known "version" suffixes (ex: @20251001) as part of the model id, + // but allow an auth profile suffix *after* them (ex: ...@20251001@work). if (/^\d{8}(?:@|$)/.test(versionSuffix)) { const nextDelimiter = trimmed.indexOf("@", profileDelimiter + 9); if (nextDelimiter < 0) { @@ -22,6 +25,20 @@ export function splitTrailingAuthProfile(raw: string): { profileDelimiter = nextDelimiter; } + // Keep local model quant suffixes (common in LM Studio/Ollama catalogs) as part + // of the model id. These often use '@' (ex: gemma-4-31b-it@q8_0) which would + // otherwise be misinterpreted as an auth profile delimiter. + // + // If an auth profile is needed, it can still be specified as a second suffix: + // lmstudio/foo@q8_0@work + if (/^(?:q\d+(?:_[a-z0-9]+)*|\d+bit)(?:@|$)/i.test(versionSuffix)) { + const nextDelimiter = trimmed.indexOf("@", profileDelimiter + 1); + if (nextDelimiter < 0) { + return { model: trimmed }; + } + profileDelimiter = nextDelimiter; + } + const model = trimmed.slice(0, profileDelimiter).trim(); const profile = trimmed.slice(profileDelimiter + 1).trim(); if (!model || !profile) {