fix(models): repair local model no-auth and overflow handling (#121790)

Preserve the internal null Authorization marker when resolving local no-auth provider headers, avoid classifying LM Studio prompt-template guidance as context overflow, and rebuild ai/llm-core package sources during pnpm dev/watch.
This commit is contained in:
Peter Steinberger
2026-08-10 18:52:00 -07:00
committed by GitHub
parent abb856ece4
commit ba58803997
6 changed files with 28 additions and 1 deletions
+5
View File
@@ -319,6 +319,11 @@ export function resolveAiTransportHeaderSentinels(
const host = getAiTransportHost(); const host = getAiTransportHost();
let resolvedHeaders: Record<string, string> | undefined; let resolvedHeaders: Record<string, string> | undefined;
for (const [name, value] of Object.entries(headers)) { for (const [name, value] of Object.entries(headers)) {
if (value === null) {
// applyLocalNoAuthHeaderOverride marks no-auth local providers with a
// runtime null marker outside the public string-only Model contract.
continue;
}
const resolved = host.resolveSecretSentinel(value); const resolved = host.resolveSecretSentinel(value);
if (resolved !== value) { if (resolved !== value) {
resolvedHeaders ??= { ...headers }; resolvedHeaders ??= { ...headers };
@@ -239,7 +239,7 @@ describe("prepareModelForSimpleCompletion", () => {
expect(result).toBe(model); expect(result).toBe(model);
}); });
it("aliases a provider-owned stream when its wire-format api is already registered", async () => { it("aliases a provider-owned stream while preserving its null auth-header marker", async () => {
const builtInStream = vi.fn(() => createAssistantMessageEventStream()); const builtInStream = vi.fn(() => createAssistantMessageEventStream());
apiRegistry.registerApiProvider( apiRegistry.registerApiProvider(
{ {
@@ -260,6 +260,7 @@ describe("prepareModelForSimpleCompletion", () => {
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 32768, contextWindow: 32768,
maxTokens: 2048, maxTokens: 2048,
headers: { Authorization: null } as never,
}; };
const result = prepareModelForSimpleCompletion({ model }); const result = prepareModelForSimpleCompletion({ model });
+2
View File
@@ -10,9 +10,11 @@ const RUN_NODE_PACKAGE_SOURCE_ROOTS = [
// Root runtime code imports these package sources through tsconfig aliases, // Root runtime code imports these package sources through tsconfig aliases,
// while pnpm dev/watch still runs the root dist entrypoint. Treat them like // while pnpm dev/watch still runs the root dist entrypoint. Treat them like
// src/ so edits restart the same process that consumes them. // src/ so edits restart the same process that consumes them.
"packages/ai/src",
"packages/gateway-client/src", "packages/gateway-client/src",
"packages/gateway-protocol/src", "packages/gateway-protocol/src",
"packages/markdown-core/src", "packages/markdown-core/src",
"packages/llm-core/src",
"packages/media-core/src", "packages/media-core/src",
"packages/media-generation-core/src", "packages/media-generation-core/src",
"packages/media-understanding-common/src", "packages/media-understanding-common/src",
@@ -9,4 +9,12 @@ describe("isLikelyContextOverflowError", () => {
), ),
).toBe(true); ).toBe(true);
}); });
it("does not mistake LM Studio prompt-template override guidance for overflow", () => {
expect(
isLikelyContextOverflowError(
'Error rendering prompt with jinja template: "Cannot apply filter upper to type UndefinedValue". You can override the prompt template in model settings.',
),
).toBe(false);
});
}); });
+3
View File
@@ -133,6 +133,9 @@ export function isLikelyContextOverflowError(errorMessage?: string): boolean {
if (isContextOverflowError(errorMessage)) { if (isContextOverflowError(errorMessage)) {
return true; return true;
} }
if (normalizeLowercaseStringOrEmpty(errorMessage).includes("prompt template")) {
return false;
}
if (RATE_LIMIT_HINT_RE.test(errorMessage)) { if (RATE_LIMIT_HINT_RE.test(errorMessage)) {
return false; return false;
} }
@@ -119,6 +119,14 @@ describe("bundled plugin assets", () => {
expect(isRestartRelevantRunNodePath("extensions/discord/src/activities/http.ts")).toBe(true); expect(isRestartRelevantRunNodePath("extensions/discord/src/activities/http.ts")).toBe(true);
}); });
it.each(["packages/ai/src/host.ts", "packages/llm-core/src/types.ts"])(
"rebuilds the root runtime for %s",
(source) => {
expect(isBuildRelevantRunNodePath(source)).toBe(true);
expect(isRestartRelevantRunNodePath(source)).toBe(true);
},
);
it("refreshes generated output metadata without recreating the watcher", async () => { it("refreshes generated output metadata without recreating the watcher", async () => {
await withPluginAssetFixture(async (rootDir) => { await withPluginAssetFixture(async (rootDir) => {
const packagePath = path.join(rootDir, "extensions", "canvas", "package.json"); const packagePath = path.join(rootDir, "extensions", "canvas", "package.json");