Compare commits
7 Commits
8dc3ae88b3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
95b233fc85
|
|||
| d55743a9e7 | |||
|
8ab4924948
|
|||
|
8bd148dc96
|
|||
|
2ab1c855ec
|
|||
|
f67ec5bde6
|
|||
|
112b85f3fb
|
108
flake.lock
generated
108
flake.lock
generated
@@ -109,11 +109,11 @@
|
|||||||
"cachyos-kernel": {
|
"cachyos-kernel": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776608760,
|
"lastModified": 1776881435,
|
||||||
"narHash": "sha256-ehDv8bF7k/2Kf4b8CCoSm51U/MOoFuLsRXqe5wZ57sE=",
|
"narHash": "sha256-j8AobLjMzeKJugseObrVC4O5k7/aZCWoft2sCS3jWYs=",
|
||||||
"owner": "CachyOS",
|
"owner": "CachyOS",
|
||||||
"repo": "linux-cachyos",
|
"repo": "linux-cachyos",
|
||||||
"rev": "7e06e29005853bbaaa3b1c1067f915d6e0db728a",
|
"rev": "1c61dfd1c3ad7762faa0db8b06c6af6c59cc4340",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -125,11 +125,11 @@
|
|||||||
"cachyos-kernel-patches": {
|
"cachyos-kernel-patches": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776792814,
|
"lastModified": 1777002108,
|
||||||
"narHash": "sha256-39dlIhz9KxUNQFxGpE9SvCviaOWAivdW0XJM8RnPNmg=",
|
"narHash": "sha256-PIZCIf6xUTOUqLFbEGH0mSwu2O/YfeAmYlgdAbP4dhs=",
|
||||||
"owner": "CachyOS",
|
"owner": "CachyOS",
|
||||||
"repo": "kernel-patches",
|
"repo": "kernel-patches",
|
||||||
"rev": "d7d558d0b2e239e27b40bcf1af6fe12e323aa391",
|
"rev": "46476ae2538db486462aef8a9de37d19030cdaf2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -222,11 +222,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776967792,
|
"lastModified": 1777083982,
|
||||||
"narHash": "sha256-O3YfkXQz8P2kec6Ani8fmuXvuXRAyl5/qPdt0kDNFWk=",
|
"narHash": "sha256-O44P8qcFEv0PYQd+9vFAgCu/e9RclHIAyAmRDJ8qR5s=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "emacs-overlay",
|
"repo": "emacs-overlay",
|
||||||
"rev": "0041dd571ebebe8fa779b940fb13b6d447a48b87",
|
"rev": "42711d50137a45b8065c3e329946e2d4525235d0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -266,11 +266,11 @@
|
|||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "pkgs/firefox-addons",
|
"dir": "pkgs/firefox-addons",
|
||||||
"lastModified": 1776916994,
|
"lastModified": 1777089773,
|
||||||
"narHash": "sha256-FgqUwRZ2bwbE5w1bCUv9MB3gvwqZ4oEyCgZ6z/6jdTY=",
|
"narHash": "sha256-ZIlNuebeWTncyl7mcV9VbceSLAaZki+UeXLPQG959xI=",
|
||||||
"owner": "rycee",
|
"owner": "rycee",
|
||||||
"repo": "nur-expressions",
|
"repo": "nur-expressions",
|
||||||
"rev": "a2236006e5c70e2fc06e9acb016b1ac9c0fd5935",
|
"rev": "402ba229617a12d918c2a887a4c83a9a24f9a36c",
|
||||||
"type": "gitlab"
|
"type": "gitlab"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -484,11 +484,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776964438,
|
"lastModified": 1777086106,
|
||||||
"narHash": "sha256-AF0cby9Xuijr5qaFpYKbm1mExV956Hk233bel6QxpFw=",
|
"narHash": "sha256-hlNpIN18pw3xo34Lsrp6vAMUPn0aB/zFBqL0QXI1Pmk=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "e09259dd2e147d35ef889784b51e89b0a10ffe15",
|
"rev": "5826802354a74af18540aef0b01bc1320f82cc17",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -631,11 +631,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776955347,
|
"lastModified": 1777066729,
|
||||||
"narHash": "sha256-VCPA/1RWMZggfXjpMcEMC2QfDrYp6eHgqvsPfDSKGSI=",
|
"narHash": "sha256-f+a+ikbq0VS6RQFf+A6EuVnsWYn2RR3ggRJNkzZgMto=",
|
||||||
"owner": "TheTom",
|
"owner": "TheTom",
|
||||||
"repo": "llama-cpp-turboquant",
|
"repo": "llama-cpp-turboquant",
|
||||||
"rev": "67559e580b10e4e47e9a6fd6218873997976886d",
|
"rev": "11a241d0db78a68e0a5b99fe6f36de6683100f6a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -657,11 +657,11 @@
|
|||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776966087,
|
"lastModified": 1777093284,
|
||||||
"narHash": "sha256-P+39paxTvpYiMv5wqGKte7YbmxJKoihcXssV1IhkSAo=",
|
"narHash": "sha256-tBvsFPJy0/2gocc6QGYFXJF44TvJ8PC726NsdTpFJ44=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "llm-agents.nix",
|
"repo": "llm-agents.nix",
|
||||||
"rev": "547d51c282c15a7c9b86c8388a1adb1695b1df59",
|
"rev": "6b4673fddbbe1f2656b3fa8d2a32666570aafbfa",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -704,11 +704,11 @@
|
|||||||
"xwayland-satellite-unstable": "xwayland-satellite-unstable"
|
"xwayland-satellite-unstable": "xwayland-satellite-unstable"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776879043,
|
"lastModified": 1777068473,
|
||||||
"narHash": "sha256-M9RjuowtoqQbFRdQAm2P6GjFwgHjRcnWYcB7ChSjDms=",
|
"narHash": "sha256-atEzEdMgJMRPm/yxOiBvOSEcjSUgU20ieXYQeDfxhTo=",
|
||||||
"owner": "sodiboo",
|
"owner": "sodiboo",
|
||||||
"repo": "niri-flake",
|
"repo": "niri-flake",
|
||||||
"rev": "535ebbe038039215a5d1c6c0c67f833409a5be96",
|
"rev": "d543523b5cd4c1f10e41ad8801c49808198b9ca5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -737,11 +737,11 @@
|
|||||||
"niri-unstable": {
|
"niri-unstable": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776853441,
|
"lastModified": 1777045529,
|
||||||
"narHash": "sha256-mSxfoEs7DiDhMCBzprI/1K7UXzMISuGq0b7T06LVJXE=",
|
"narHash": "sha256-EeAwmrvONsovL2qPwKGXF2xGhbo7MySesY3fW2pNLpM=",
|
||||||
"owner": "YaLTeR",
|
"owner": "YaLTeR",
|
||||||
"repo": "niri",
|
"repo": "niri",
|
||||||
"rev": "74d2b18603366b98ec9045ecf4a632422f472365",
|
"rev": "9438f59e2b9d8deb6fcec5922f8aca18162b673c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -761,11 +761,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776796985,
|
"lastModified": 1777054238,
|
||||||
"narHash": "sha256-cNFg3H09sBZl1v9ds6PDHfLCUTDJbefGMSv+WxFs+9c=",
|
"narHash": "sha256-qaqHPZO3oQJiIZgD6sp5HKwvYAVyMtHVJiXVwPSEkx0=",
|
||||||
"owner": "xddxdd",
|
"owner": "xddxdd",
|
||||||
"repo": "nix-cachyos-kernel",
|
"repo": "nix-cachyos-kernel",
|
||||||
"rev": "ac5956bbceb022998fc1dd0001322f10ef1e6dda",
|
"rev": "acb94409639d6d6d64bea140f939ac34938560b1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -846,11 +846,11 @@
|
|||||||
"systems": "systems_7"
|
"systems": "systems_7"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776915193,
|
"lastModified": 1777001712,
|
||||||
"narHash": "sha256-bYyOT3OIWIKvDV+pOVd0hdCEG8orf85QX4b21LWUSEs=",
|
"narHash": "sha256-9JX9msZU1NvHzjKM24PRorP76Ge8GBy6LAkJKA21mlY=",
|
||||||
"owner": "Infinidoge",
|
"owner": "Infinidoge",
|
||||||
"repo": "nix-minecraft",
|
"repo": "nix-minecraft",
|
||||||
"rev": "40c972ce0f45b8c05bf245d5065647b17552312c",
|
"rev": "394d3bfd943458baf29e4798bc9b256d824a3bb9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -861,11 +861,11 @@
|
|||||||
},
|
},
|
||||||
"nixos-hardware": {
|
"nixos-hardware": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776830795,
|
"lastModified": 1776983936,
|
||||||
"narHash": "sha256-PAfvLwuHc1VOvsLcpk6+HDKgMEibvZjCNvbM1BJOA7o=",
|
"narHash": "sha256-ZOQyNqSvJ8UdrrqU1p7vaFcdL53idK+LOM8oRWEWh6o=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "72674a6b5599e844c045ae7449ba91f803d44ebc",
|
"rev": "2096f3f411ce46e88a79ae4eafcfc9df8ed41c61",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -877,11 +877,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776548001,
|
"lastModified": 1776877367,
|
||||||
"narHash": "sha256-ZSK0NL4a1BwVbbTBoSnWgbJy9HeZFXLYQizjb2DPF24=",
|
"narHash": "sha256-EHq1/OX139R1RvBzOJ0aMRT3xnWyqtHBRUBuO1gFzjI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b12141ef619e0a9c1c84dc8c684040326f27cdcc",
|
"rev": "0726a0ecb6d4e08f6adced58726b95db924cef57",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -991,11 +991,11 @@
|
|||||||
"noctalia-qs": "noctalia-qs"
|
"noctalia-qs": "noctalia-qs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776888984,
|
"lastModified": 1777079905,
|
||||||
"narHash": "sha256-Up2F/eoMuPUsZnPVYdH5TMHe1TBP2Ue1QuWd0vWZoxY=",
|
"narHash": "sha256-TvYEXwkZnRFQRuFyyqTNSfPnU2tMdhtiBOXSk2AWLJA=",
|
||||||
"owner": "noctalia-dev",
|
"owner": "noctalia-dev",
|
||||||
"repo": "noctalia-shell",
|
"repo": "noctalia-shell",
|
||||||
"rev": "2c1808f9f8937fc0b82c54af513f7620fec56d71",
|
"rev": "a50c92167c8d438000270f7eca36f6eea74f388e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1133,11 +1133,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776914043,
|
"lastModified": 1777086717,
|
||||||
"narHash": "sha256-qug5r56yW1qOsjSI99l3Jm15JNT9CvS2otkXNRNtrPI=",
|
"narHash": "sha256-vEl3cGHRxEFdVNuP9PbrhAWnmU98aPOLGy9/1JXzSuM=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "2d35c4358d7de3a0e606a6e8b27925d981c01cc3",
|
"rev": "3be56bd430bfd65d3c468a50626c3a601c7dee03",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1190,11 +1190,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776912132,
|
"lastModified": 1777000965,
|
||||||
"narHash": "sha256-UDR6PtHacMhAQJ8SPNbPROaxbtl2Pgjww0TzipTsTZE=",
|
"narHash": "sha256-xcrhVgfI13s1WH4hg5MLL83zAp6/htfF8Pjw4RPiKM8=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "srvos",
|
"repo": "srvos",
|
||||||
"rev": "e9ff039a72ff2c06271d5002eb431c443abf69fa",
|
"rev": "7ae6f096b2ffbd25d17da8a4d0fe299a164c4eac",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1356,11 +1356,11 @@
|
|||||||
"trackerlist": {
|
"trackerlist": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776895782,
|
"lastModified": 1777068584,
|
||||||
"narHash": "sha256-iHdp9lRoV3ejsTC96z7Pns/JvQKWyp+V0fdVcVOv8Xw=",
|
"narHash": "sha256-UZr6mQfauhIUo8n3SDYnBWeq11xs5lTAoc9onh2MHBc=",
|
||||||
"owner": "ngosang",
|
"owner": "ngosang",
|
||||||
"repo": "trackerslist",
|
"repo": "trackerslist",
|
||||||
"rev": "e1a89caab7d4c5af3870a49ddc494cda745b236e",
|
"rev": "747c048c604c8d12b9d20cfccea4800a32382a66",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1524,11 +1524,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1776922304,
|
"lastModified": 1777084302,
|
||||||
"narHash": "sha256-T1r7GWzeqX0C6YauIMN6D0sdr5voDAPMg8jvn59Wm7g=",
|
"narHash": "sha256-qHE5XpgtRedzND5xzaqzbSOw4amse0aA4/BaVI4ONcU=",
|
||||||
"owner": "0xc000022070",
|
"owner": "0xc000022070",
|
||||||
"repo": "zen-browser-flake",
|
"repo": "zen-browser-flake",
|
||||||
"rev": "91cc9ed57a893b2e944de60812511f05fd408ce6",
|
"rev": "f6bab88f8566ddc13fb5e5500bd6c720b61d5321",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -38,7 +38,12 @@ in
|
|||||||
{
|
{
|
||||||
home.packages = [
|
home.packages = [
|
||||||
(inputs.llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.omp.overrideAttrs (old: {
|
(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
|
||||||
|
];
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -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_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
|
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 cpu / platform
|
||||||
INTEL_IOMMU = lib.mkForce no;
|
INTEL_IOMMU = lib.mkForce no;
|
||||||
INTEL_IDLE = lib.mkForce no;
|
INTEL_IDLE = lib.mkForce no;
|
||||||
|
|||||||
@@ -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">;
|
||||||
Reference in New Issue
Block a user