Compare commits

..

3 Commits

Author SHA1 Message Date
95b233fc85 kernel: re-enable DRM/FB stuff (caused issues)
Some checks failed
Build and Deploy / mreow (push) Successful in 1h34m33s
Build and Deploy / yarn (push) Successful in 1m14s
Build and Deploy / muffin (push) Failing after 32s
2026-04-25 17:19:37 -04:00
d55743a9e7 revert: roll back flake.lock pre-update (niri 8ed0da4 black-screens on amdgpu) 2026-04-25 16:21:28 -04:00
8ab4924948 omp: add patch that fixes deepseek 2026-04-25 15:38:39 -04:00
4 changed files with 156 additions and 33 deletions

48
flake.lock generated
View File

@@ -222,11 +222,11 @@
]
},
"locked": {
"lastModified": 1777138175,
"narHash": "sha256-UrexPU1xQ/qB0qCjuTeljQOCDmjeCNuipZMBv3FyoJM=",
"lastModified": 1777083982,
"narHash": "sha256-O44P8qcFEv0PYQd+9vFAgCu/e9RclHIAyAmRDJ8qR5s=",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "d7d0c87d15148472eef847dfe298095ef4298dc1",
"rev": "42711d50137a45b8065c3e329946e2d4525235d0",
"type": "github"
},
"original": {
@@ -484,11 +484,11 @@
]
},
"locked": {
"lastModified": 1777138498,
"narHash": "sha256-mZdL0akv+PiA9h4DXNVGCqUeV5NiODy5lzRWoDsYhtI=",
"lastModified": 1777086106,
"narHash": "sha256-hlNpIN18pw3xo34Lsrp6vAMUPn0aB/zFBqL0QXI1Pmk=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "026e21038902970e54226133e718e8c197fac799",
"rev": "5826802354a74af18540aef0b01bc1320f82cc17",
"type": "github"
},
"original": {
@@ -564,11 +564,11 @@
]
},
"locked": {
"lastModified": 1777132364,
"narHash": "sha256-qK6A0xRDAgLf8DUHpDWpVL6NcWi4IhoVClcov+GjLP0=",
"lastModified": 1776962372,
"narHash": "sha256-Y2imW4kyIhupx8myNSeNCzDbEx2X+h+AmhNjWXA/7Yw=",
"owner": "Jovian-Experiments",
"repo": "Jovian-NixOS",
"rev": "7ae8615cc307c282555b025f88e0c8d7c185bcbf",
"rev": "ee3a1184a978e311194a2d3d352c5e6aba67a4b5",
"type": "github"
},
"original": {
@@ -657,11 +657,11 @@
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1777143457,
"narHash": "sha256-mGvWYLxSaJwHv2ndcaHj1FrLnRFKqcBEo/lcm+Sz7aQ=",
"lastModified": 1777093284,
"narHash": "sha256-tBvsFPJy0/2gocc6QGYFXJF44TvJ8PC726NsdTpFJ44=",
"owner": "numtide",
"repo": "llm-agents.nix",
"rev": "4aaa2a28b09897b1858eb8db4cb3cf509e95cd14",
"rev": "6b4673fddbbe1f2656b3fa8d2a32666570aafbfa",
"type": "github"
},
"original": {
@@ -704,11 +704,11 @@
"xwayland-satellite-unstable": "xwayland-satellite-unstable"
},
"locked": {
"lastModified": 1777130270,
"narHash": "sha256-AgOIR3O+hLkTe/spgYjp0knc37iy/A5DqGRY+8DP3LE=",
"lastModified": 1777068473,
"narHash": "sha256-atEzEdMgJMRPm/yxOiBvOSEcjSUgU20ieXYQeDfxhTo=",
"owner": "sodiboo",
"repo": "niri-flake",
"rev": "e43ef13f23c2c7ae5b10e842745cb345faff4f40",
"rev": "d543523b5cd4c1f10e41ad8801c49808198b9ca5",
"type": "github"
},
"original": {
@@ -737,11 +737,11 @@
"niri-unstable": {
"flake": false,
"locked": {
"lastModified": 1777115961,
"narHash": "sha256-ehSMsSpE+0k8r+2Vseu8kangsYxToZv3vinynsDp9zs=",
"lastModified": 1777045529,
"narHash": "sha256-EeAwmrvONsovL2qPwKGXF2xGhbo7MySesY3fW2pNLpM=",
"owner": "YaLTeR",
"repo": "niri",
"rev": "8ed0da44d974c32c6877d2f4630c314da0717ecb",
"rev": "9438f59e2b9d8deb6fcec5922f8aca18162b673c",
"type": "github"
},
"original": {
@@ -761,11 +761,11 @@
]
},
"locked": {
"lastModified": 1777140538,
"narHash": "sha256-2y5SwHxTOwEdr8WZv1IGBVoJM47YcomfoxFnZj9TgN0=",
"lastModified": 1777054238,
"narHash": "sha256-qaqHPZO3oQJiIZgD6sp5HKwvYAVyMtHVJiXVwPSEkx0=",
"owner": "xddxdd",
"repo": "nix-cachyos-kernel",
"rev": "ce6083d35e50516dd6eb6156d0cbda67baed9117",
"rev": "acb94409639d6d6d64bea140f939ac34938560b1",
"type": "github"
},
"original": {
@@ -1524,11 +1524,11 @@
]
},
"locked": {
"lastModified": 1777138694,
"narHash": "sha256-yjAFuyqQyOtQ5entLYmSRf/1L0kuSDWQndS2QNBLQlc=",
"lastModified": 1777084302,
"narHash": "sha256-qHE5XpgtRedzND5xzaqzbSOw4amse0aA4/BaVI4ONcU=",
"owner": "0xc000022070",
"repo": "zen-browser-flake",
"rev": "5ceb2bfc5671bfca6b1b363669309d6871043d66",
"rev": "f6bab88f8566ddc13fb5e5500bd6c720b61d5321",
"type": "github"
},
"original": {

View File

@@ -38,7 +38,12 @@ in
{
home.packages = [
(inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.omp.overrideAttrs (old: {
patches = (old.patches or [ ]) ++ [ ];
patches = (old.patches or [ ]) ++ [
# Retry without strict tools when DeepSeek (via OpenRouter) rejects strict-mode `anyOf`
# nullable unions with `Invalid tool parameters schema : field \`anyOf\`: missing field \`type\``.
# Upstream PR: pending; applies cleanly against v14.2.1.
../../patches/omp/0001-openai-completions-retry-without-strict-on-deepseek-openrouter.patch
];
}))
];

View File

@@ -182,14 +182,6 @@
DRM_AMDGPU_CIK = lib.mkForce no; # Sea Islands / GCN 2 (2013): R9 290/290X/390, Kaveri APUs (A10-7850K), Steam Machine Bonaire
DRM_AMD_SECURE_DISPLAY = lib.mkForce no; # HDCP region-CRC debugfs helper, needs custom DMCU firmware
# early-boot framebuffer chain: drop every alternative to amdgpu so
# the console never transitions simpledrm -> dummy -> amdgpu (visible
# as a flash + scrolled dmesg). amdgpu owns the display from initrd
# onward; pre-amdgpu kernel output stays in the printk ring buffer.
DRM_SIMPLEDRM = lib.mkForce no;
FB_EFI = lib.mkForce no;
FB_VESA = lib.mkForce no;
# intel cpu / platform
INTEL_IOMMU = lib.mkForce no;
INTEL_IDLE = lib.mkForce no;

View File

@@ -0,0 +1,126 @@
Subject: [PATCH] fix(openai-completions): retry without strict tools for DeepSeek-via-OpenRouter anyOf rejections
The retry-on-strict-tool-error path in openai-completions failed to recover when
DeepSeek (and similar backends fronted by OpenRouter) reject strict-mode tool
schemas with errors of the form:
Invalid tool parameters schema : field `anyOf`: missing field `type`
Two reasons:
1. Retry only triggered in "all_strict" mode. OpenRouter defaults to "mixed"
(per-tool strict), so the early return prevented retry.
2. The error-message regex required "strict" near "tool". DeepSeek's message
never mentions "strict".
Fix:
- Allow retry whenever any tool was sent with strict (i.e. mode != "none").
- Recognize "Invalid tool parameters" in the regex.
Includes a regression test reproducing the exact DeepSeek error body via
OpenRouter mixed-strict mode.
Applies cleanly against v14.2.1.
---
diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts
index e58189607..3c20631c1 100644
--- a/packages/ai/src/providers/openai-completions.ts
+++ b/packages/ai/src/providers/openai-completions.ts
@@ -1245,7 +1245,10 @@ function shouldRetryWithoutStrictTools(
toolStrictMode: AppliedToolStrictMode,
tools: Tool[] | undefined,
): boolean {
- if (!tools || tools.length === 0 || toolStrictMode !== "all_strict") {
+ // Retry whenever any tool was sent with `strict: true`. OpenRouter routes to underlying
+ // providers (e.g. DeepSeek) whose schema validators reject the strict-mode `anyOf` shape
+ // even when omp emitted strict per-tool ("mixed"), not just provider-wide ("all_strict").
+ if (!tools || tools.length === 0 || toolStrictMode === "none") {
return false;
}
const status = extractHttpStatusFromError(error) ?? capturedErrorResponse?.status;
@@ -1255,7 +1258,14 @@ function shouldRetryWithoutStrictTools(
const messageParts = [error instanceof Error ? error.message : undefined, capturedErrorResponse?.bodyText]
.filter((value): value is string => typeof value === "string" && value.trim().length > 0)
.join("\n");
- return /wrong_api_format|mixed values for 'strict'|tool[s]?\b.*strict|\bstrict\b.*tool/i.test(messageParts);
+ // Patterns:
+ // - `wrong_api_format`, `mixed values for 'strict'`: OpenAI rejecting mixed strict flags.
+ // - `tool ... strict` / `strict ... tool`: generic strict-tool complaints.
+ // - `Invalid tool parameters schema`: DeepSeek (via OpenRouter) rejecting strict-mode
+ // nullable unions because their validator demands `type` alongside `anyOf`.
+ return /wrong_api_format|mixed values for 'strict'|tool[s]?\b.*strict|\bstrict\b.*tool|invalid tool parameters/i.test(
+ messageParts,
+ );
}
function mapStopReason(reason: ChatCompletionChunk.Choice["finish_reason"] | string): {
diff --git a/packages/ai/test/openai-tool-strict-mode.test.ts b/packages/ai/test/openai-tool-strict-mode.test.ts
index 2bf17e6d8..24d5a09d5 100644
--- a/packages/ai/test/openai-tool-strict-mode.test.ts
+++ b/packages/ai/test/openai-tool-strict-mode.test.ts
@@ -231,6 +231,64 @@ describe("OpenAI tool strict mode", () => {
expect(result.content).toContainEqual({ type: "text", text: "Hello" });
expect(strictFlags).toEqual([[true], [false]]);
});
+ it("retries with non-strict tool schemas when OpenRouter backend rejects strict anyOf nullable unions", async () => {
+ // Reproduces deepseek/deepseek-v4-pro via OpenRouter rejecting the strict-mode schema with:
+ // 400 Provider returned error
+ // {"error":{"message":"Invalid tool parameters schema : field `anyOf`: missing field `type`",...}}
+ // OpenRouter is in mixed-strict mode by default (per-tool strict), so the original retry condition
+ // (only "all_strict") prevented recovery. The retry now triggers whenever any tool sent strict=true.
+ const model = getBundledModel("openrouter", "deepseek/deepseek-v3.2") as Model<"openai-completions">;
+ const strictFlags: boolean[][] = [];
+ global.fetch = Object.assign(
+ async (_input: string | URL | Request, init?: RequestInit): Promise<Response> => {
+ const bodyText = typeof init?.body === "string" ? init.body : "";
+ const payload = JSON.parse(bodyText) as {
+ tools?: Array<{ function?: { strict?: boolean } }>;
+ };
+ strictFlags.push((payload.tools ?? []).map(tool => tool.function?.strict === true));
+ if (strictFlags.length === 1) {
+ return new Response(
+ JSON.stringify({
+ error: {
+ message: "Invalid tool parameters schema : field `anyOf`: missing field `type`",
+ type: "invalid_request_error",
+ param: null,
+ code: "invalid_request_error",
+ },
+ }),
+ {
+ status: 400,
+ headers: { "content-type": "application/json" },
+ },
+ );
+ }
+ return createSseResponse([
+ {
+ id: "chatcmpl-or",
+ object: "chat.completion.chunk",
+ created: 0,
+ model: model.id,
+ choices: [{ index: 0, delta: { content: "Hello" } }],
+ },
+ {
+ id: "chatcmpl-or",
+ object: "chat.completion.chunk",
+ created: 0,
+ model: model.id,
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
+ },
+ "[DONE]",
+ ]);
+ },
+ { preconnect: originalFetch.preconnect },
+ );
+
+ const result = await streamOpenAICompletions(model, testContext, { apiKey: "test-key" }).result();
+ expect(result.stopReason).toBe("stop");
+ expect(result.content).toContainEqual({ type: "text", text: "Hello" });
+ expect(strictFlags).toEqual([[true], [false]]);
+ });
+
it("sends strict=true for openai-responses tool schemas on OpenAI", async () => {
const model = getBundledModel("openai", "gpt-5-mini") as Model<"openai-responses">;