artificial-code 1.17.13 → 1.17.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (749) hide show
  1. package/bin/ac +56 -31
  2. package/bin/ac.js +224 -0
  3. package/package.json +18 -148
  4. package/AGENTS.md +0 -131
  5. package/Dockerfile +0 -18
  6. package/bunfig.toml +0 -7
  7. package/git +0 -0
  8. package/migration/20260511173437_session-metadata/migration.sql +0 -1
  9. package/migration/20260511173437_session-metadata/snapshot.json +0 -1500
  10. package/parsers-config.ts +0 -1
  11. package/script/bench-search.ts +0 -94
  12. package/script/bench-test-suite.ts +0 -52
  13. package/script/build.ts +0 -243
  14. package/script/generate.ts +0 -14
  15. package/script/httpapi-exercise.ts +0 -1
  16. package/script/postinstall.mjs +0 -189
  17. package/script/profile-test-files.ts +0 -42
  18. package/script/publish.ts +0 -213
  19. package/script/run-workspace-server +0 -106
  20. package/script/schema.ts +0 -77
  21. package/script/time.ts +0 -6
  22. package/script/trace-imports.ts +0 -153
  23. package/specs/effect/error-boundaries-plan.md +0 -235
  24. package/specs/effect/errors.md +0 -207
  25. package/specs/effect/facades.md +0 -218
  26. package/specs/effect/guide.md +0 -247
  27. package/specs/effect/instance-context.md +0 -13
  28. package/specs/effect/loose-ends.md +0 -30
  29. package/specs/effect/migration.md +0 -62
  30. package/specs/effect/routes.md +0 -61
  31. package/specs/effect/schema.md +0 -88
  32. package/specs/effect/server-package.md +0 -58
  33. package/specs/effect/todo.md +0 -241
  34. package/specs/effect/tools.md +0 -88
  35. package/specs/openapi-translation-cleanup.md +0 -204
  36. package/specs/tui-plugins.md +0 -544
  37. package/specs/v2/api.ts +0 -67
  38. package/specs/v2/message-shape.md +0 -136
  39. package/specs/v2/notifications.md +0 -13
  40. package/specs/v2/tui-command-shim.md +0 -67
  41. package/src/account/account.ts +0 -461
  42. package/src/account/repo.ts +0 -171
  43. package/src/account/schema.ts +0 -99
  44. package/src/account/url.ts +0 -8
  45. package/src/acp/agent.ts +0 -95
  46. package/src/acp/config-option.ts +0 -203
  47. package/src/acp/content.ts +0 -269
  48. package/src/acp/directory.ts +0 -212
  49. package/src/acp/error.ts +0 -97
  50. package/src/acp/event.ts +0 -342
  51. package/src/acp/permission.ts +0 -254
  52. package/src/acp/profile.ts +0 -42
  53. package/src/acp/service.ts +0 -1097
  54. package/src/acp/session.ts +0 -232
  55. package/src/acp/tool.ts +0 -364
  56. package/src/acp/usage.ts +0 -239
  57. package/src/agent/agent.ts +0 -453
  58. package/src/agent/generate.txt +0 -75
  59. package/src/agent/prompt/compaction.txt +0 -9
  60. package/src/agent/prompt/explore.txt +0 -18
  61. package/src/agent/prompt/summary.txt +0 -11
  62. package/src/agent/prompt/title.txt +0 -44
  63. package/src/agent/subagent-permissions.ts +0 -27
  64. package/src/audio.d.ts +0 -14
  65. package/src/auth/index.ts +0 -97
  66. package/src/background/job.ts +0 -37
  67. package/src/bus/global.ts +0 -22
  68. package/src/cli/bootstrap.ts +0 -11
  69. package/src/cli/cmd/account.ts +0 -264
  70. package/src/cli/cmd/acp.ts +0 -73
  71. package/src/cli/cmd/agent.ts +0 -259
  72. package/src/cli/cmd/attach.ts +0 -148
  73. package/src/cli/cmd/cmd.ts +0 -7
  74. package/src/cli/cmd/db.ts +0 -62
  75. package/src/cli/cmd/debug/agent.handler.ts +0 -193
  76. package/src/cli/cmd/debug/agent.ts +0 -27
  77. package/src/cli/cmd/debug/config.ts +0 -14
  78. package/src/cli/cmd/debug/file.ts +0 -73
  79. package/src/cli/cmd/debug/index.ts +0 -87
  80. package/src/cli/cmd/debug/lsp.ts +0 -50
  81. package/src/cli/cmd/debug/ripgrep.ts +0 -79
  82. package/src/cli/cmd/debug/scrap.ts +0 -16
  83. package/src/cli/cmd/debug/skill.ts +0 -15
  84. package/src/cli/cmd/debug/snapshot.ts +0 -50
  85. package/src/cli/cmd/debug/startup.ts +0 -11
  86. package/src/cli/cmd/debug/v2.ts +0 -42
  87. package/src/cli/cmd/export.ts +0 -292
  88. package/src/cli/cmd/generate.ts +0 -54
  89. package/src/cli/cmd/github.handler.ts +0 -1593
  90. package/src/cli/cmd/github.shared.ts +0 -30
  91. package/src/cli/cmd/github.ts +0 -42
  92. package/src/cli/cmd/import.ts +0 -224
  93. package/src/cli/cmd/mcp.ts +0 -840
  94. package/src/cli/cmd/models.ts +0 -66
  95. package/src/cli/cmd/plug.ts +0 -230
  96. package/src/cli/cmd/pr.ts +0 -115
  97. package/src/cli/cmd/prompt-display.ts +0 -1
  98. package/src/cli/cmd/providers.ts +0 -534
  99. package/src/cli/cmd/run/demo.ts +0 -1274
  100. package/src/cli/cmd/run/entry.body.ts +0 -205
  101. package/src/cli/cmd/run/footer.command.tsx +0 -1064
  102. package/src/cli/cmd/run/footer.menu.tsx +0 -351
  103. package/src/cli/cmd/run/footer.permission.tsx +0 -472
  104. package/src/cli/cmd/run/footer.prompt.tsx +0 -1306
  105. package/src/cli/cmd/run/footer.question.tsx +0 -573
  106. package/src/cli/cmd/run/footer.subagent.tsx +0 -173
  107. package/src/cli/cmd/run/footer.ts +0 -1129
  108. package/src/cli/cmd/run/footer.view.tsx +0 -943
  109. package/src/cli/cmd/run/footer.width.ts +0 -27
  110. package/src/cli/cmd/run/permission.shared.ts +0 -256
  111. package/src/cli/cmd/run/prompt.editor.ts +0 -157
  112. package/src/cli/cmd/run/prompt.shared.ts +0 -153
  113. package/src/cli/cmd/run/question.shared.ts +0 -340
  114. package/src/cli/cmd/run/runtime.boot.ts +0 -202
  115. package/src/cli/cmd/run/runtime.lifecycle.ts +0 -406
  116. package/src/cli/cmd/run/runtime.queue.ts +0 -349
  117. package/src/cli/cmd/run/runtime.shared.ts +0 -17
  118. package/src/cli/cmd/run/runtime.stdin.ts +0 -37
  119. package/src/cli/cmd/run/runtime.ts +0 -814
  120. package/src/cli/cmd/run/scrollback.shared.ts +0 -92
  121. package/src/cli/cmd/run/scrollback.surface.ts +0 -431
  122. package/src/cli/cmd/run/scrollback.writer.tsx +0 -352
  123. package/src/cli/cmd/run/session-data.ts +0 -1113
  124. package/src/cli/cmd/run/session-replay.ts +0 -374
  125. package/src/cli/cmd/run/session.shared.ts +0 -196
  126. package/src/cli/cmd/run/splash.ts +0 -280
  127. package/src/cli/cmd/run/stream.transport.ts +0 -1462
  128. package/src/cli/cmd/run/stream.ts +0 -175
  129. package/src/cli/cmd/run/subagent-data.ts +0 -876
  130. package/src/cli/cmd/run/theme.ts +0 -690
  131. package/src/cli/cmd/run/tool.ts +0 -1486
  132. package/src/cli/cmd/run/trace.ts +0 -94
  133. package/src/cli/cmd/run/turn-summary.ts +0 -47
  134. package/src/cli/cmd/run/types.ts +0 -350
  135. package/src/cli/cmd/run/variant.shared.ts +0 -216
  136. package/src/cli/cmd/run.ts +0 -1011
  137. package/src/cli/cmd/serve.ts +0 -24
  138. package/src/cli/cmd/session.ts +0 -147
  139. package/src/cli/cmd/stats.ts +0 -393
  140. package/src/cli/cmd/tui.ts +0 -305
  141. package/src/cli/cmd/uninstall.ts +0 -353
  142. package/src/cli/cmd/upgrade.ts +0 -74
  143. package/src/cli/cmd/web.ts +0 -84
  144. package/src/cli/effect/prompt.ts +0 -37
  145. package/src/cli/effect-cmd.ts +0 -96
  146. package/src/cli/error.ts +0 -130
  147. package/src/cli/heap.ts +0 -45
  148. package/src/cli/logo.ts +0 -1
  149. package/src/cli/network.ts +0 -80
  150. package/src/cli/tui/layer.ts +0 -8
  151. package/src/cli/tui/validate-session.ts +0 -29
  152. package/src/cli/tui/worker.ts +0 -80
  153. package/src/cli/ui.ts +0 -132
  154. package/src/cli/upgrade.ts +0 -53
  155. package/src/command/index.ts +0 -177
  156. package/src/command/template/initialize.txt +0 -66
  157. package/src/command/template/review.txt +0 -101
  158. package/src/config/agent.ts +0 -59
  159. package/src/config/command.ts +0 -39
  160. package/src/config/config.ts +0 -680
  161. package/src/config/entry-name.ts +0 -19
  162. package/src/config/managed.ts +0 -69
  163. package/src/config/markdown.ts +0 -36
  164. package/src/config/parse.ts +0 -79
  165. package/src/config/paths.ts +0 -45
  166. package/src/config/plugin.ts +0 -79
  167. package/src/config/tui-cwd.ts +0 -5
  168. package/src/config/tui-host-attention.ts +0 -21
  169. package/src/config/tui-migrate.ts +0 -132
  170. package/src/config/tui.ts +0 -276
  171. package/src/config/variable.ts +0 -91
  172. package/src/control-plane/adapters/index.ts +0 -41
  173. package/src/control-plane/adapters/worktree.ts +0 -96
  174. package/src/control-plane/dev/README.md +0 -19
  175. package/src/control-plane/dev/debug-workspace-plugin.ts +0 -73
  176. package/src/control-plane/types.ts +0 -59
  177. package/src/control-plane/util.ts +0 -39
  178. package/src/control-plane/workspace-adapter-runtime.ts +0 -51
  179. package/src/control-plane/workspace-context.ts +0 -26
  180. package/src/control-plane/workspace.ts +0 -964
  181. package/src/effect/app-node-builder-v1.ts +0 -12
  182. package/src/effect/app-runtime.ts +0 -135
  183. package/src/effect/bootstrap-runtime.ts +0 -19
  184. package/src/effect/bridge.ts +0 -84
  185. package/src/effect/config-service.ts +0 -67
  186. package/src/effect/instance-ref.ts +0 -11
  187. package/src/effect/instance-registry.ts +0 -12
  188. package/src/effect/instance-state.ts +0 -69
  189. package/src/effect/promise.ts +0 -17
  190. package/src/effect/run-service.ts +0 -47
  191. package/src/effect/runner.ts +0 -217
  192. package/src/effect/runtime-flags.ts +0 -77
  193. package/src/env/index.ts +0 -41
  194. package/src/event-manifest.ts +0 -3
  195. package/src/event-v2-bridge.ts +0 -71
  196. package/src/format/formatter.ts +0 -404
  197. package/src/format/index.ts +0 -203
  198. package/src/git/index.ts +0 -348
  199. package/src/id/id.ts +0 -80
  200. package/src/ide/index.ts +0 -54
  201. package/src/image/image.ts +0 -172
  202. package/src/index.ts +0 -142
  203. package/src/installation/index.ts +0 -336
  204. package/src/lsp/client.ts +0 -650
  205. package/src/lsp/diagnostic.ts +0 -29
  206. package/src/lsp/language.ts +0 -121
  207. package/src/lsp/launch.ts +0 -21
  208. package/src/lsp/lsp.ts +0 -507
  209. package/src/lsp/server.ts +0 -1983
  210. package/src/markdown.d.ts +0 -4
  211. package/src/mcp/auth.ts +0 -163
  212. package/src/mcp/catalog.ts +0 -170
  213. package/src/mcp/index.ts +0 -1012
  214. package/src/mcp/oauth-callback.ts +0 -194
  215. package/src/mcp/oauth-provider.ts +0 -259
  216. package/src/node.ts +0 -4
  217. package/src/patch/index.ts +0 -686
  218. package/src/permission/arity.ts +0 -163
  219. package/src/permission/evaluate.ts +0 -1
  220. package/src/permission/index.ts +0 -218
  221. package/src/plugin/azure.ts +0 -26
  222. package/src/plugin/cloudflare.ts +0 -76
  223. package/src/plugin/digitalocean.ts +0 -325
  224. package/src/plugin/github-copilot/copilot.ts +0 -414
  225. package/src/plugin/github-copilot/models.ts +0 -246
  226. package/src/plugin/index.ts +0 -314
  227. package/src/plugin/install.ts +0 -439
  228. package/src/plugin/loader.ts +0 -237
  229. package/src/plugin/meta.ts +0 -188
  230. package/src/plugin/openai/README.md +0 -31
  231. package/src/plugin/openai/codex.ts +0 -556
  232. package/src/plugin/openai/ws-pool.ts +0 -270
  233. package/src/plugin/openai/ws.ts +0 -381
  234. package/src/plugin/pty-environment.ts +0 -24
  235. package/src/plugin/shared.ts +0 -323
  236. package/src/plugin/snowflake-cortex.ts +0 -507
  237. package/src/plugin/tui/internal.ts +0 -10
  238. package/src/plugin/tui/runtime.ts +0 -1131
  239. package/src/plugin/xai.ts +0 -626
  240. package/src/project/bootstrap-service.ts +0 -9
  241. package/src/project/bootstrap.ts +0 -58
  242. package/src/project/instance-context.ts +0 -24
  243. package/src/project/instance-runtime.ts +0 -16
  244. package/src/project/instance-store.ts +0 -213
  245. package/src/project/project.ts +0 -483
  246. package/src/project/vcs.ts +0 -423
  247. package/src/provider/auth.ts +0 -229
  248. package/src/provider/error.ts +0 -188
  249. package/src/provider/model-status.ts +0 -8
  250. package/src/provider/provider.ts +0 -1977
  251. package/src/provider/transform.ts +0 -1561
  252. package/src/question/index.ts +0 -161
  253. package/src/question/schema.ts +0 -4
  254. package/src/server/auth.ts +0 -48
  255. package/src/server/event.ts +0 -10
  256. package/src/server/global-lifecycle.ts +0 -28
  257. package/src/server/init-projectors.ts +0 -3
  258. package/src/server/mdns.ts +0 -47
  259. package/src/server/projectors.ts +0 -1
  260. package/src/server/proxy-util.ts +0 -48
  261. package/src/server/routes/instance/httpapi/AGENTS.md +0 -39
  262. package/src/server/routes/instance/httpapi/api.ts +0 -97
  263. package/src/server/routes/instance/httpapi/errors.ts +0 -193
  264. package/src/server/routes/instance/httpapi/groups/config.ts +0 -65
  265. package/src/server/routes/instance/httpapi/groups/control-plane.ts +0 -35
  266. package/src/server/routes/instance/httpapi/groups/control.ts +0 -76
  267. package/src/server/routes/instance/httpapi/groups/event.ts +0 -29
  268. package/src/server/routes/instance/httpapi/groups/experimental.ts +0 -275
  269. package/src/server/routes/instance/httpapi/groups/file.ts +0 -185
  270. package/src/server/routes/instance/httpapi/groups/global.ts +0 -136
  271. package/src/server/routes/instance/httpapi/groups/instance.ts +0 -206
  272. package/src/server/routes/instance/httpapi/groups/mcp.ts +0 -156
  273. package/src/server/routes/instance/httpapi/groups/metadata.ts +0 -18
  274. package/src/server/routes/instance/httpapi/groups/permission.ts +0 -61
  275. package/src/server/routes/instance/httpapi/groups/project-copy.ts +0 -32
  276. package/src/server/routes/instance/httpapi/groups/project.ts +0 -93
  277. package/src/server/routes/instance/httpapi/groups/provider.ts +0 -101
  278. package/src/server/routes/instance/httpapi/groups/pty.ts +0 -172
  279. package/src/server/routes/instance/httpapi/groups/query.ts +0 -12
  280. package/src/server/routes/instance/httpapi/groups/question.ts +0 -74
  281. package/src/server/routes/instance/httpapi/groups/session.ts +0 -462
  282. package/src/server/routes/instance/httpapi/groups/sync.ts +0 -113
  283. package/src/server/routes/instance/httpapi/groups/tui.ts +0 -208
  284. package/src/server/routes/instance/httpapi/groups/workspace.ts +0 -141
  285. package/src/server/routes/instance/httpapi/handlers/config.ts +0 -34
  286. package/src/server/routes/instance/httpapi/handlers/control-plane.ts +0 -37
  287. package/src/server/routes/instance/httpapi/handlers/control.ts +0 -43
  288. package/src/server/routes/instance/httpapi/handlers/event.ts +0 -99
  289. package/src/server/routes/instance/httpapi/handlers/experimental.ts +0 -193
  290. package/src/server/routes/instance/httpapi/handlers/file.ts +0 -139
  291. package/src/server/routes/instance/httpapi/handlers/global.ts +0 -156
  292. package/src/server/routes/instance/httpapi/handlers/instance.ts +0 -110
  293. package/src/server/routes/instance/httpapi/handlers/mcp.ts +0 -111
  294. package/src/server/routes/instance/httpapi/handlers/permission.ts +0 -41
  295. package/src/server/routes/instance/httpapi/handlers/project-copy.ts +0 -83
  296. package/src/server/routes/instance/httpapi/handlers/project.ts +0 -63
  297. package/src/server/routes/instance/httpapi/handlers/provider.ts +0 -113
  298. package/src/server/routes/instance/httpapi/handlers/pty.ts +0 -273
  299. package/src/server/routes/instance/httpapi/handlers/question.ts +0 -54
  300. package/src/server/routes/instance/httpapi/handlers/session-errors.ts +0 -21
  301. package/src/server/routes/instance/httpapi/handlers/session.ts +0 -442
  302. package/src/server/routes/instance/httpapi/handlers/sync.ts +0 -89
  303. package/src/server/routes/instance/httpapi/handlers/tui.ts +0 -131
  304. package/src/server/routes/instance/httpapi/handlers/workspace.ts +0 -102
  305. package/src/server/routes/instance/httpapi/lifecycle.ts +0 -54
  306. package/src/server/routes/instance/httpapi/middleware/authorization.ts +0 -150
  307. package/src/server/routes/instance/httpapi/middleware/compression.ts +0 -64
  308. package/src/server/routes/instance/httpapi/middleware/cors-vary.ts +0 -29
  309. package/src/server/routes/instance/httpapi/middleware/error.ts +0 -43
  310. package/src/server/routes/instance/httpapi/middleware/fence.ts +0 -25
  311. package/src/server/routes/instance/httpapi/middleware/instance-context.ts +0 -43
  312. package/src/server/routes/instance/httpapi/middleware/proxy.ts +0 -108
  313. package/src/server/routes/instance/httpapi/middleware/schema-error.ts +0 -41
  314. package/src/server/routes/instance/httpapi/middleware/workspace-routing.ts +0 -250
  315. package/src/server/routes/instance/httpapi/public.ts +0 -537
  316. package/src/server/routes/instance/httpapi/server.ts +0 -322
  317. package/src/server/routes/instance/httpapi/websocket-tracker.ts +0 -60
  318. package/src/server/server.ts +0 -226
  319. package/src/server/shared/fence.ts +0 -60
  320. package/src/server/shared/pty-ticket.ts +0 -15
  321. package/src/server/shared/public-ui.ts +0 -12
  322. package/src/server/shared/tui-control.ts +0 -28
  323. package/src/server/shared/ui.ts +0 -108
  324. package/src/server/shared/workspace-routing.ts +0 -38
  325. package/src/server/tui-event.ts +0 -1
  326. package/src/session/compaction.ts +0 -562
  327. package/src/session/instruction.ts +0 -237
  328. package/src/session/llm/AGENTS.md +0 -90
  329. package/src/session/llm/ai-sdk.ts +0 -288
  330. package/src/session/llm/native-request.ts +0 -196
  331. package/src/session/llm/native-runtime.ts +0 -195
  332. package/src/session/llm/request.ts +0 -226
  333. package/src/session/llm.ts +0 -404
  334. package/src/session/message-error.ts +0 -14
  335. package/src/session/message-v2.ts +0 -734
  336. package/src/session/message.ts +0 -148
  337. package/src/session/overflow.ts +0 -34
  338. package/src/session/processor.ts +0 -716
  339. package/src/session/prompt/anthropic.txt +0 -105
  340. package/src/session/prompt/beast.txt +0 -147
  341. package/src/session/prompt/build-switch.txt +0 -5
  342. package/src/session/prompt/codex.txt +0 -79
  343. package/src/session/prompt/copilot-gpt-5.txt +0 -143
  344. package/src/session/prompt/default.txt +0 -95
  345. package/src/session/prompt/gemini.txt +0 -155
  346. package/src/session/prompt/gpt.txt +0 -107
  347. package/src/session/prompt/kimi.txt +0 -95
  348. package/src/session/prompt/plan-mode.txt +0 -70
  349. package/src/session/prompt/plan-reminder-anthropic.txt +0 -67
  350. package/src/session/prompt/plan.txt +0 -26
  351. package/src/session/prompt/trinity.txt +0 -97
  352. package/src/session/prompt.ts +0 -1630
  353. package/src/session/reminders.ts +0 -92
  354. package/src/session/retry.ts +0 -201
  355. package/src/session/revert.ts +0 -146
  356. package/src/session/run-state.ts +0 -151
  357. package/src/session/schema.ts +0 -26
  358. package/src/session/session.ts +0 -1018
  359. package/src/session/status.ts +0 -56
  360. package/src/session/summary.ts +0 -160
  361. package/src/session/system.ts +0 -143
  362. package/src/session/todo.ts +0 -74
  363. package/src/session/tools.ts +0 -583
  364. package/src/share/session.ts +0 -58
  365. package/src/share/share-next.ts +0 -371
  366. package/src/skill/discovery.ts +0 -140
  367. package/src/skill/index.ts +0 -354
  368. package/src/snapshot/index.ts +0 -807
  369. package/src/sql.d.ts +0 -4
  370. package/src/storage/schema.ts +0 -5
  371. package/src/storage/storage.ts +0 -327
  372. package/src/sync/README.md +0 -179
  373. package/src/sync/schema.ts +0 -11
  374. package/src/temporary.ts +0 -31
  375. package/src/tool/apply_patch.ts +0 -313
  376. package/src/tool/apply_patch.txt +0 -33
  377. package/src/tool/edit.ts +0 -737
  378. package/src/tool/edit.txt +0 -10
  379. package/src/tool/external-directory.ts +0 -49
  380. package/src/tool/glob.ts +0 -76
  381. package/src/tool/glob.txt +0 -6
  382. package/src/tool/grep.ts +0 -112
  383. package/src/tool/grep.txt +0 -8
  384. package/src/tool/invalid.ts +0 -21
  385. package/src/tool/json-schema.ts +0 -164
  386. package/src/tool/lsp.ts +0 -113
  387. package/src/tool/lsp.txt +0 -24
  388. package/src/tool/mcp-websearch.ts +0 -96
  389. package/src/tool/plan-enter.txt +0 -14
  390. package/src/tool/plan-exit.txt +0 -13
  391. package/src/tool/plan.ts +0 -79
  392. package/src/tool/question.ts +0 -44
  393. package/src/tool/question.txt +0 -10
  394. package/src/tool/read.ts +0 -386
  395. package/src/tool/read.txt +0 -14
  396. package/src/tool/registry.ts +0 -420
  397. package/src/tool/schema.ts +0 -14
  398. package/src/tool/shell/id.ts +0 -19
  399. package/src/tool/shell/prompt.ts +0 -293
  400. package/src/tool/shell/shell.txt +0 -21
  401. package/src/tool/shell.ts +0 -645
  402. package/src/tool/skill.ts +0 -70
  403. package/src/tool/skill.txt +0 -5
  404. package/src/tool/task.ts +0 -346
  405. package/src/tool/task.txt +0 -19
  406. package/src/tool/todo.ts +0 -46
  407. package/src/tool/todowrite.txt +0 -44
  408. package/src/tool/tool.ts +0 -183
  409. package/src/tool/truncate.ts +0 -156
  410. package/src/tool/truncation-dir.ts +0 -4
  411. package/src/tool/webfetch.ts +0 -192
  412. package/src/tool/webfetch.txt +0 -13
  413. package/src/tool/websearch.ts +0 -143
  414. package/src/tool/websearch.txt +0 -14
  415. package/src/tool/write.ts +0 -104
  416. package/src/tool/write.txt +0 -8
  417. package/src/util/archive.ts +0 -17
  418. package/src/util/bom.ts +0 -27
  419. package/src/util/data-url.ts +0 -9
  420. package/src/util/defer.ts +0 -10
  421. package/src/util/effect-http-client.ts +0 -11
  422. package/src/util/error.ts +0 -1
  423. package/src/util/filesystem.ts +0 -251
  424. package/src/util/html.ts +0 -8
  425. package/src/util/iife.ts +0 -3
  426. package/src/util/lazy.ts +0 -20
  427. package/src/util/local-context.ts +0 -25
  428. package/src/util/locale.ts +0 -2
  429. package/src/util/media.ts +0 -26
  430. package/src/util/process.ts +0 -177
  431. package/src/util/proxy-env.ts +0 -72
  432. package/src/util/queue.ts +0 -32
  433. package/src/util/record.ts +0 -1
  434. package/src/util/repository.ts +0 -232
  435. package/src/util/rpc.ts +0 -66
  436. package/src/util/signal.ts +0 -12
  437. package/src/util/timeout.ts +0 -13
  438. package/src/util/token.ts +0 -1
  439. package/src/util/wildcard.ts +0 -59
  440. package/src/worktree/index.ts +0 -623
  441. package/test/AGENTS.md +0 -204
  442. package/test/EFFECT_TEST_MIGRATION.md +0 -169
  443. package/test/account/repo.test.ts +0 -355
  444. package/test/account/service.test.ts +0 -456
  445. package/test/acp/config-option.test.ts +0 -229
  446. package/test/acp/content.test.ts +0 -235
  447. package/test/acp/directory.test.ts +0 -188
  448. package/test/acp/error.test.ts +0 -67
  449. package/test/acp/event.test.ts +0 -751
  450. package/test/acp/permission.test.ts +0 -401
  451. package/test/acp/service-session.test.ts +0 -1248
  452. package/test/acp/session.test.ts +0 -201
  453. package/test/acp/tool.test.ts +0 -298
  454. package/test/acp/usage.test.ts +0 -318
  455. package/test/agent/agent.test.ts +0 -755
  456. package/test/agent/plan-mode-subagent-bypass.test.ts +0 -160
  457. package/test/agent/plugin-agent-regression.test.ts +0 -51
  458. package/test/auth/auth.test.ts +0 -75
  459. package/test/background/job.test.ts +0 -244
  460. package/test/cli/account.test.ts +0 -30
  461. package/test/cli/acp/acp-test-client.ts +0 -97
  462. package/test/cli/acp/config-options.test.ts +0 -103
  463. package/test/cli/acp/helpers.ts +0 -96
  464. package/test/cli/acp/initialize-auth.test.ts +0 -61
  465. package/test/cli/acp/lifecycle.test.ts +0 -118
  466. package/test/cli/acp/prompt-content.test.ts +0 -97
  467. package/test/cli/acp/skills.test.ts +0 -38
  468. package/test/cli/cmd/tui/attention.test.ts +0 -484
  469. package/test/cli/effect-cmd-instance-als.test.ts +0 -40
  470. package/test/cli/error.test.ts +0 -95
  471. package/test/cli/github-action.test.ts +0 -199
  472. package/test/cli/github-remote.test.ts +0 -90
  473. package/test/cli/help/__snapshots__/help-snapshots.test.ts.snap +0 -623
  474. package/test/cli/help/help-snapshots.test.ts +0 -140
  475. package/test/cli/import.test.ts +0 -54
  476. package/test/cli/mcp-add.test.ts +0 -74
  477. package/test/cli/plugin-auth-picker.test.ts +0 -120
  478. package/test/cli/run/entry.body.test.ts +0 -536
  479. package/test/cli/run/footer.menu.test.ts +0 -43
  480. package/test/cli/run/footer.view.test.tsx +0 -1375
  481. package/test/cli/run/footer.width.test.ts +0 -35
  482. package/test/cli/run/permission.shared.test.ts +0 -144
  483. package/test/cli/run/prompt.editor.test.ts +0 -101
  484. package/test/cli/run/prompt.shared.test.ts +0 -101
  485. package/test/cli/run/question.shared.test.ts +0 -115
  486. package/test/cli/run/run-process.test.ts +0 -331
  487. package/test/cli/run/runtime.boot.test.ts +0 -283
  488. package/test/cli/run/runtime.queue.test.ts +0 -481
  489. package/test/cli/run/runtime.stdin.test.ts +0 -71
  490. package/test/cli/run/runtime.test.ts +0 -238
  491. package/test/cli/run/scrollback.surface.test.ts +0 -1092
  492. package/test/cli/run/session-data.test.ts +0 -593
  493. package/test/cli/run/session-replay.test.ts +0 -691
  494. package/test/cli/run/session.shared.test.ts +0 -247
  495. package/test/cli/run/stream.test.ts +0 -56
  496. package/test/cli/run/stream.transport.test.ts +0 -2363
  497. package/test/cli/run/subagent-data.test.ts +0 -547
  498. package/test/cli/run/theme.test.ts +0 -177
  499. package/test/cli/run/variant.shared.test.ts +0 -218
  500. package/test/cli/serve/serve-process.test.ts +0 -61
  501. package/test/cli/smokes/read-only.test.ts +0 -115
  502. package/test/cli/tui/attach.test.ts +0 -11
  503. package/test/cli/tui/editor-context-zed.test.ts +0 -379
  504. package/test/cli/tui/editor-context.test.tsx +0 -297
  505. package/test/cli/tui/plugin-add.test.ts +0 -110
  506. package/test/cli/tui/plugin-install.test.ts +0 -87
  507. package/test/cli/tui/plugin-lifecycle.test.ts +0 -224
  508. package/test/cli/tui/plugin-loader-entrypoint.test.ts +0 -485
  509. package/test/cli/tui/plugin-loader-pure.test.ts +0 -72
  510. package/test/cli/tui/plugin-loader.test.ts +0 -1332
  511. package/test/cli/tui/plugin-toggle.test.ts +0 -264
  512. package/test/cli/tui/thread.test.ts +0 -95
  513. package/test/config/agent-color.test.ts +0 -47
  514. package/test/config/config.test.ts +0 -2027
  515. package/test/config/entry-name.test.ts +0 -57
  516. package/test/config/fixtures/empty-frontmatter.md +0 -4
  517. package/test/config/fixtures/frontmatter.md +0 -28
  518. package/test/config/fixtures/markdown-header.md +0 -11
  519. package/test/config/fixtures/no-frontmatter.md +0 -1
  520. package/test/config/fixtures/weird-model-id.md +0 -13
  521. package/test/config/lsp.test.ts +0 -69
  522. package/test/config/markdown.test.ts +0 -228
  523. package/test/config/plugin.test.ts +0 -0
  524. package/test/config/tui.test.ts +0 -894
  525. package/test/control-plane/adapters.test.ts +0 -71
  526. package/test/control-plane/workspace.test.ts +0 -1701
  527. package/test/effect/app-runtime-logger.test.ts +0 -101
  528. package/test/effect/config-service.test.ts +0 -65
  529. package/test/effect/instance-state.test.ts +0 -392
  530. package/test/effect/run-service.test.ts +0 -89
  531. package/test/effect/runner.test.ts +0 -514
  532. package/test/effect/runtime-flags.test.ts +0 -374
  533. package/test/event-manifest.test.ts +0 -24
  534. package/test/fake/account.ts +0 -9
  535. package/test/fake/auth.ts +0 -8
  536. package/test/fake/npm.ts +0 -8
  537. package/test/fake/provider.ts +0 -82
  538. package/test/fake/skill.ts +0 -8
  539. package/test/filesystem/filesystem.test.ts +0 -318
  540. package/test/fixture/agent-plugin.constants.ts +0 -6
  541. package/test/fixture/agent-plugin.ts +0 -12
  542. package/test/fixture/config.ts +0 -23
  543. package/test/fixture/db.ts +0 -11
  544. package/test/fixture/fixture.test.ts +0 -26
  545. package/test/fixture/fixture.ts +0 -228
  546. package/test/fixture/flag.ts +0 -20
  547. package/test/fixture/flock-worker.ts +0 -72
  548. package/test/fixture/lsp/fake-lsp-server.js +0 -249
  549. package/test/fixture/mcp-session-recovery.ts +0 -50
  550. package/test/fixture/plug-worker.ts +0 -93
  551. package/test/fixture/plugin-meta-worker.ts +0 -19
  552. package/test/fixture/plugin.ts +0 -10
  553. package/test/fixture/skills/agents-sdk/SKILL.md +0 -152
  554. package/test/fixture/skills/agents-sdk/references/callable.md +0 -92
  555. package/test/fixture/skills/cloudflare/SKILL.md +0 -211
  556. package/test/fixture/skills/index.json +0 -6
  557. package/test/fixture/tui-environment.tsx +0 -32
  558. package/test/fixture/tui-plugin.ts +0 -355
  559. package/test/fixture/tui-runtime.ts +0 -56
  560. package/test/fixture/tui-sdk.ts +0 -82
  561. package/test/fixture/workspace.ts +0 -34
  562. package/test/fixtures/recordings/session/native-anthropic-tool-loop.json +0 -49
  563. package/test/fixtures/recordings/session/native-openai-oauth-tool-loop.json +0 -45
  564. package/test/fixtures/recordings/session/native-zen-tool-loop.json +0 -49
  565. package/test/format/format.test.ts +0 -235
  566. package/test/git/git.test.ts +0 -179
  567. package/test/ide/ide.test.ts +0 -82
  568. package/test/image/fixtures/picture-5mb-base64.png +0 -0
  569. package/test/image/image.test.ts +0 -121
  570. package/test/installation/installation.test.ts +0 -240
  571. package/test/lib/cli-process.ts +0 -535
  572. package/test/lib/effect.ts +0 -177
  573. package/test/lib/filesystem.ts +0 -10
  574. package/test/lib/llm-server.ts +0 -779
  575. package/test/lib/snapshot.ts +0 -73
  576. package/test/lib/test-provider.ts +0 -37
  577. package/test/lib/websocket.ts +0 -46
  578. package/test/lsp/client.test.ts +0 -488
  579. package/test/lsp/index.test.ts +0 -231
  580. package/test/lsp/jdtls-root.test.ts +0 -459
  581. package/test/lsp/launch.test.ts +0 -22
  582. package/test/lsp/lifecycle.test.ts +0 -160
  583. package/test/mcp/auth.test.ts +0 -78
  584. package/test/mcp/catalog.test.ts +0 -47
  585. package/test/mcp/headers.test.ts +0 -127
  586. package/test/mcp/lifecycle.test.ts +0 -1304
  587. package/test/mcp/oauth-auto-connect.test.ts +0 -367
  588. package/test/mcp/oauth-browser.test.ts +0 -243
  589. package/test/mcp/oauth-callback.test.ts +0 -114
  590. package/test/mcp/oauth-provider.test.ts +0 -102
  591. package/test/mcp/session-recovery.test.ts +0 -27
  592. package/test/patch/patch.test.ts +0 -384
  593. package/test/permission/arity.test.ts +0 -33
  594. package/test/permission/next.test.ts +0 -1174
  595. package/test/permission-task.test.ts +0 -319
  596. package/test/plugin/auth-override.test.ts +0 -101
  597. package/test/plugin/cloudflare.test.ts +0 -68
  598. package/test/plugin/codex.test.ts +0 -256
  599. package/test/plugin/github-copilot-models.test.ts +0 -332
  600. package/test/plugin/install-concurrency.test.ts +0 -140
  601. package/test/plugin/install.test.ts +0 -570
  602. package/test/plugin/loader-shared.test.ts +0 -1306
  603. package/test/plugin/meta.test.ts +0 -137
  604. package/test/plugin/openai-rollout.test.ts +0 -17
  605. package/test/plugin/openai-ws.test.ts +0 -884
  606. package/test/plugin/shared.test.ts +0 -88
  607. package/test/plugin/snowflake-cortex.test.ts +0 -278
  608. package/test/plugin/trigger.test.ts +0 -108
  609. package/test/plugin/workspace-adapter.test.ts +0 -111
  610. package/test/plugin/xai.test.ts +0 -620
  611. package/test/preload.ts +0 -92
  612. package/test/project/instance-bootstrap.test.ts +0 -115
  613. package/test/project/instance.test.ts +0 -248
  614. package/test/project/migrate-global.test.ts +0 -168
  615. package/test/project/project-directory.test.ts +0 -202
  616. package/test/project/project.test.ts +0 -808
  617. package/test/project/vcs.test.ts +0 -335
  618. package/test/project/worktree-remove.test.ts +0 -128
  619. package/test/project/worktree.test.ts +0 -324
  620. package/test/provider/amazon-bedrock.test.ts +0 -361
  621. package/test/provider/cf-ai-gateway-e2e.test.ts +0 -132
  622. package/test/provider/digitalocean.test.ts +0 -124
  623. package/test/provider/gitlab-duo.test.ts +0 -412
  624. package/test/provider/header-timeout.test.ts +0 -234
  625. package/test/provider/model-status.test.ts +0 -61
  626. package/test/provider/provider.test.ts +0 -1912
  627. package/test/provider/transform.test.ts +0 -4742
  628. package/test/question/question.test.ts +0 -459
  629. package/test/server/AGENTS.md +0 -15
  630. package/test/server/auth.test.ts +0 -59
  631. package/test/server/global-bus.ts +0 -31
  632. package/test/server/global-session-list.test.ts +0 -108
  633. package/test/server/httpapi-authorization.test.ts +0 -174
  634. package/test/server/httpapi-compression.test.ts +0 -151
  635. package/test/server/httpapi-config.test.ts +0 -110
  636. package/test/server/httpapi-control-plane.test.ts +0 -63
  637. package/test/server/httpapi-cors-vary.test.ts +0 -63
  638. package/test/server/httpapi-cors.test.ts +0 -122
  639. package/test/server/httpapi-error-middleware.test.ts +0 -101
  640. package/test/server/httpapi-event.test.ts +0 -94
  641. package/test/server/httpapi-exercise/assertions.ts +0 -64
  642. package/test/server/httpapi-exercise/backend.ts +0 -144
  643. package/test/server/httpapi-exercise/dsl.ts +0 -210
  644. package/test/server/httpapi-exercise/environment.ts +0 -40
  645. package/test/server/httpapi-exercise/index.ts +0 -1814
  646. package/test/server/httpapi-exercise/report.ts +0 -66
  647. package/test/server/httpapi-exercise/routing.ts +0 -96
  648. package/test/server/httpapi-exercise/runner.ts +0 -267
  649. package/test/server/httpapi-exercise/runtime.ts +0 -52
  650. package/test/server/httpapi-exercise/types.ts +0 -127
  651. package/test/server/httpapi-experimental.test.ts +0 -298
  652. package/test/server/httpapi-file.test.ts +0 -83
  653. package/test/server/httpapi-global.test.ts +0 -66
  654. package/test/server/httpapi-instance-context.test.ts +0 -337
  655. package/test/server/httpapi-instance-route-auth.test.ts +0 -81
  656. package/test/server/httpapi-instance.test.ts +0 -265
  657. package/test/server/httpapi-layer.ts +0 -33
  658. package/test/server/httpapi-listen.test.ts +0 -465
  659. package/test/server/httpapi-mcp-oauth.test.ts +0 -73
  660. package/test/server/httpapi-mcp.test.ts +0 -223
  661. package/test/server/httpapi-mdns.test.ts +0 -79
  662. package/test/server/httpapi-promptasync-context.test.ts +0 -212
  663. package/test/server/httpapi-provider.test.ts +0 -401
  664. package/test/server/httpapi-pty.test.ts +0 -299
  665. package/test/server/httpapi-public-openapi.test.ts +0 -350
  666. package/test/server/httpapi-query-schema-drift.test.ts +0 -330
  667. package/test/server/httpapi-reference.test.ts +0 -63
  668. package/test/server/httpapi-schema-error-body.test.ts +0 -166
  669. package/test/server/httpapi-sdk.test.ts +0 -913
  670. package/test/server/httpapi-session.test.ts +0 -1090
  671. package/test/server/httpapi-sync.test.ts +0 -149
  672. package/test/server/httpapi-ui.test.ts +0 -456
  673. package/test/server/httpapi-v2-location.test.ts +0 -126
  674. package/test/server/httpapi-v2-pty.test.ts +0 -250
  675. package/test/server/httpapi-workspace-routing.test.ts +0 -552
  676. package/test/server/httpapi-workspace.test.ts +0 -506
  677. package/test/server/negative-tokens-regression.test.ts +0 -84
  678. package/test/server/project-copy.test.ts +0 -127
  679. package/test/server/project-init-git.test.ts +0 -118
  680. package/test/server/proxy-util.test.ts +0 -113
  681. package/test/server/sdk-error-shape.test.ts +0 -81
  682. package/test/server/sdk-v1-smoke.test.ts +0 -57
  683. package/test/server/session-actions.test.ts +0 -110
  684. package/test/server/session-diff-missing-patch.test.ts +0 -97
  685. package/test/server/session-list.test.ts +0 -302
  686. package/test/server/session-messages.test.ts +0 -180
  687. package/test/server/session-select.test.ts +0 -67
  688. package/test/server/workspace-proxy.test.ts +0 -181
  689. package/test/server/workspace-routing.test.ts +0 -94
  690. package/test/server/worktree-endpoint-repro.test.ts +0 -307
  691. package/test/session/compaction.test.ts +0 -1819
  692. package/test/session/instruction.test.ts +0 -264
  693. package/test/session/llm-native-recorded.test.ts +0 -413
  694. package/test/session/llm-native.test.ts +0 -761
  695. package/test/session/llm.test.ts +0 -2003
  696. package/test/session/message-v2.test.ts +0 -1661
  697. package/test/session/messages-pagination.test.ts +0 -1057
  698. package/test/session/processor-effect.test.ts +0 -1067
  699. package/test/session/prompt.test.ts +0 -2402
  700. package/test/session/retry.test.ts +0 -440
  701. package/test/session/revert-compact.test.ts +0 -638
  702. package/test/session/schema-decoding.test.ts +0 -313
  703. package/test/session/session-schema.test.ts +0 -78
  704. package/test/session/session.test.ts +0 -253
  705. package/test/session/snapshot-tool-race.test.ts +0 -189
  706. package/test/session/structured-output-integration.test.ts +0 -235
  707. package/test/session/structured-output.test.ts +0 -387
  708. package/test/session/system.test.ts +0 -142
  709. package/test/share/share-next.test.ts +0 -324
  710. package/test/skill/discovery.test.ts +0 -186
  711. package/test/skill/skill.test.ts +0 -585
  712. package/test/snapshot/snapshot.test.ts +0 -1218
  713. package/test/storage/storage.test.ts +0 -297
  714. package/test/tool/__snapshots__/parameters.test.ts.snap +0 -466
  715. package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
  716. package/test/tool/apply_patch.test.ts +0 -529
  717. package/test/tool/edit.test.ts +0 -574
  718. package/test/tool/external-directory.test.ts +0 -156
  719. package/test/tool/fixtures/large-image.png +0 -0
  720. package/test/tool/fixtures/models-api.json +0 -117299
  721. package/test/tool/glob.test.ts +0 -132
  722. package/test/tool/grep.test.ts +0 -221
  723. package/test/tool/lsp.test.ts +0 -184
  724. package/test/tool/parameters.test.ts +0 -290
  725. package/test/tool/question.test.ts +0 -133
  726. package/test/tool/read.test.ts +0 -608
  727. package/test/tool/registry.test.ts +0 -493
  728. package/test/tool/shell.test.ts +0 -1199
  729. package/test/tool/skill.test.ts +0 -134
  730. package/test/tool/task.test.ts +0 -905
  731. package/test/tool/tool-define.test.ts +0 -154
  732. package/test/tool/truncation.test.ts +0 -264
  733. package/test/tool/webfetch.test.ts +0 -119
  734. package/test/tool/websearch.test.ts +0 -99
  735. package/test/tool/write.test.ts +0 -279
  736. package/test/util/data-url.test.ts +0 -14
  737. package/test/util/error.test.ts +0 -16
  738. package/test/util/filesystem.test.ts +0 -656
  739. package/test/util/glob.test.ts +0 -164
  740. package/test/util/html.test.ts +0 -15
  741. package/test/util/iife.test.ts +0 -36
  742. package/test/util/lazy.test.ts +0 -50
  743. package/test/util/module.test.ts +0 -59
  744. package/test/util/process.test.ts +0 -128
  745. package/test/util/repository.test.ts +0 -93
  746. package/test/util/timeout.test.ts +0 -21
  747. package/test/util/wildcard.test.ts +0 -90
  748. package/test/v2/session-message-updater.test.ts +0 -269
  749. package/tsconfig.json +0 -16
@@ -1,4742 +0,0 @@
1
- import { describe, expect, test } from "bun:test"
2
- import { Effect } from "effect"
3
- import { ProviderTransform } from "@/provider/transform"
4
- import { LLMRequestPrep } from "@/session/llm/request"
5
- import { ProviderV2 } from "@ac/core/provider"
6
- import { ModelV2 } from "@ac/core/model"
7
- import { jsonSchema } from "ai"
8
-
9
- describe("ProviderTransform.options - setCacheKey", () => {
10
- const sessionID = "test-session-123"
11
-
12
- const mockModel = {
13
- id: "anthropic/claude-3-5-sonnet",
14
- providerID: "anthropic",
15
- api: {
16
- id: "claude-3-5-sonnet-20241022",
17
- url: "https://api.anthropic.com",
18
- npm: "@ai-sdk/anthropic",
19
- },
20
- name: "Claude 3.5 Sonnet",
21
- capabilities: {
22
- temperature: true,
23
- reasoning: false,
24
- attachment: true,
25
- toolcall: true,
26
- input: { text: true, audio: false, image: true, video: false, pdf: true },
27
- output: { text: true, audio: false, image: false, video: false, pdf: false },
28
- interleaved: false,
29
- },
30
- cost: {
31
- input: 0.003,
32
- output: 0.015,
33
- cache: { read: 0.0003, write: 0.00375 },
34
- },
35
- limit: {
36
- context: 200000,
37
- output: 8192,
38
- },
39
- status: "active",
40
- options: {},
41
- headers: {},
42
- } as any
43
-
44
- test("should set promptCacheKey when providerOptions.setCacheKey is true", () => {
45
- const result = ProviderTransform.options({
46
- model: mockModel,
47
- sessionID,
48
- providerOptions: { setCacheKey: true },
49
- })
50
- expect(result.promptCacheKey).toBe(sessionID)
51
- })
52
-
53
- test("should not set promptCacheKey when providerOptions.setCacheKey is false", () => {
54
- const result = ProviderTransform.options({
55
- model: mockModel,
56
- sessionID,
57
- providerOptions: { setCacheKey: false },
58
- })
59
- expect(result.promptCacheKey).toBeUndefined()
60
- })
61
-
62
- test("should not set promptCacheKey when providerOptions is undefined", () => {
63
- const result = ProviderTransform.options({
64
- model: mockModel,
65
- sessionID,
66
- providerOptions: undefined,
67
- })
68
- expect(result.promptCacheKey).toBeUndefined()
69
- })
70
-
71
- test("should not set promptCacheKey when providerOptions does not have setCacheKey", () => {
72
- const result = ProviderTransform.options({ model: mockModel, sessionID, providerOptions: {} })
73
- expect(result.promptCacheKey).toBeUndefined()
74
- })
75
-
76
- test("should set promptCacheKey for openai provider regardless of setCacheKey", () => {
77
- const openaiModel = {
78
- ...mockModel,
79
- providerID: "openai",
80
- api: {
81
- id: "gpt-4",
82
- url: "https://api.openai.com",
83
- npm: "@ai-sdk/openai",
84
- },
85
- }
86
- const result = ProviderTransform.options({ model: openaiModel, sessionID, providerOptions: {} })
87
- expect(result.promptCacheKey).toBe(sessionID)
88
- })
89
-
90
- test("should set store=false for openai provider", () => {
91
- const openaiModel = {
92
- ...mockModel,
93
- providerID: "openai",
94
- api: {
95
- id: "gpt-4",
96
- url: "https://api.openai.com",
97
- npm: "@ai-sdk/openai",
98
- },
99
- }
100
- const result = ProviderTransform.options({
101
- model: openaiModel,
102
- sessionID,
103
- providerOptions: {},
104
- })
105
- expect(result.store).toBe(false)
106
- })
107
-
108
- test("should set store=false for azure provider by default", () => {
109
- const azureModel = {
110
- ...mockModel,
111
- providerID: "azure",
112
- api: {
113
- id: "gpt-4",
114
- url: "https://azure.com",
115
- npm: "@ai-sdk/azure",
116
- },
117
- }
118
- const result = ProviderTransform.options({
119
- model: azureModel,
120
- sessionID,
121
- providerOptions: {},
122
- })
123
- expect(result.store).toBe(false)
124
- })
125
- })
126
-
127
- describe("ProviderTransform.options - zai/zhipuai thinking", () => {
128
- const sessionID = "test-session-123"
129
-
130
- const createModel = (providerID: string) =>
131
- ({
132
- id: `${providerID}/glm-4.6`,
133
- providerID,
134
- api: {
135
- id: "glm-4.6",
136
- url: "https://open.bigmodel.cn/api/paas/v4",
137
- npm: "@ai-sdk/openai-compatible",
138
- },
139
- name: "GLM 4.6",
140
- capabilities: {
141
- temperature: true,
142
- reasoning: true,
143
- attachment: true,
144
- toolcall: true,
145
- input: { text: true, audio: false, image: true, video: false, pdf: true },
146
- output: { text: true, audio: false, image: false, video: false, pdf: false },
147
- interleaved: false,
148
- },
149
- cost: {
150
- input: 0.001,
151
- output: 0.002,
152
- cache: { read: 0.0001, write: 0.0002 },
153
- },
154
- limit: {
155
- context: 128000,
156
- output: 8192,
157
- },
158
- status: "active",
159
- options: {},
160
- headers: {},
161
- }) as any
162
-
163
- for (const providerID of ["zai-coding-plan", "zai", "zhipuai-coding-plan", "zhipuai"]) {
164
- test(`${providerID} should set thinking cfg`, () => {
165
- const result = ProviderTransform.options({
166
- model: createModel(providerID),
167
- sessionID,
168
- providerOptions: {},
169
- })
170
-
171
- expect(result.thinking).toEqual({
172
- type: "enabled",
173
- clear_thinking: false,
174
- })
175
- })
176
- }
177
- })
178
-
179
- describe("ProviderTransform.options - minimax m3 thinking", () => {
180
- const createModel = (npm: string) =>
181
- ({
182
- id: "minimax/minimax-m3",
183
- providerID: "minimax",
184
- api: {
185
- id: "minimax-m3",
186
- url: "https://api.minimax.com",
187
- npm,
188
- },
189
- capabilities: { reasoning: true },
190
- limit: { output: 64_000 },
191
- }) as any
192
-
193
- test("explicitly enables adaptive thinking with the anthropic SDK", () => {
194
- expect(
195
- ProviderTransform.options({
196
- model: createModel("@ai-sdk/anthropic"),
197
- sessionID: "test-session-123",
198
- }).thinking,
199
- ).toEqual({ type: "adaptive" })
200
- })
201
-
202
- test("uses the native default with the openai-compatible SDK", () => {
203
- expect(
204
- ProviderTransform.options({
205
- model: createModel("@ai-sdk/openai-compatible"),
206
- sessionID: "test-session-123",
207
- }).thinking,
208
- ).toBeUndefined()
209
- })
210
- })
211
-
212
- describe("ProviderTransform.options - google thinkingConfig gating", () => {
213
- const sessionID = "test-session-123"
214
-
215
- const createGoogleModel = (reasoning: boolean, npm: "@ai-sdk/google" | "@ai-sdk/google-vertex") =>
216
- ({
217
- id: `${npm === "@ai-sdk/google" ? "google" : "google-vertex"}/gemini-2.0-flash`,
218
- providerID: npm === "@ai-sdk/google" ? "google" : "google-vertex",
219
- api: {
220
- id: "gemini-2.0-flash",
221
- url: npm === "@ai-sdk/google" ? "https://generativelanguage.googleapis.com" : "https://vertexai.googleapis.com",
222
- npm,
223
- },
224
- name: "Gemini 2.0 Flash",
225
- capabilities: {
226
- temperature: true,
227
- reasoning,
228
- attachment: true,
229
- toolcall: true,
230
- input: { text: true, audio: false, image: true, video: false, pdf: true },
231
- output: { text: true, audio: false, image: false, video: false, pdf: false },
232
- interleaved: false,
233
- },
234
- cost: {
235
- input: 0.001,
236
- output: 0.002,
237
- cache: { read: 0.0001, write: 0.0002 },
238
- },
239
- limit: {
240
- context: 1_000_000,
241
- output: 8192,
242
- },
243
- status: "active",
244
- options: {},
245
- headers: {},
246
- }) as any
247
-
248
- test("does not set thinkingConfig for google models without reasoning capability", () => {
249
- const result = ProviderTransform.options({
250
- model: createGoogleModel(false, "@ai-sdk/google"),
251
- sessionID,
252
- providerOptions: {},
253
- })
254
- expect(result.thinkingConfig).toBeUndefined()
255
- })
256
-
257
- test("sets thinkingConfig for google models with reasoning capability", () => {
258
- const result = ProviderTransform.options({
259
- model: createGoogleModel(true, "@ai-sdk/google"),
260
- sessionID,
261
- providerOptions: {},
262
- })
263
- expect(result.thinkingConfig).toEqual({
264
- includeThoughts: true,
265
- })
266
- })
267
-
268
- test("does not set thinkingConfig for vertex models without reasoning capability", () => {
269
- const result = ProviderTransform.options({
270
- model: createGoogleModel(false, "@ai-sdk/google-vertex"),
271
- sessionID,
272
- providerOptions: {},
273
- })
274
- expect(result.thinkingConfig).toBeUndefined()
275
- })
276
- })
277
-
278
- describe("ProviderTransform.options - gpt-5 textVerbosity", () => {
279
- const sessionID = "test-session-123"
280
-
281
- const createGpt5Model = (apiId: string) =>
282
- ({
283
- id: `openai/${apiId}`,
284
- providerID: "openai",
285
- api: {
286
- id: apiId,
287
- url: "https://api.openai.com",
288
- npm: "@ai-sdk/openai",
289
- },
290
- name: apiId,
291
- capabilities: {
292
- temperature: true,
293
- reasoning: true,
294
- attachment: true,
295
- toolcall: true,
296
- input: { text: true, audio: false, image: true, video: false, pdf: false },
297
- output: { text: true, audio: false, image: false, video: false, pdf: false },
298
- interleaved: false,
299
- },
300
- cost: { input: 0.03, output: 0.06, cache: { read: 0.001, write: 0.002 } },
301
- limit: { context: 128000, output: 4096 },
302
- status: "active",
303
- options: {},
304
- headers: {},
305
- }) as any
306
-
307
- test("gpt-5.2 should have textVerbosity set to low", () => {
308
- const model = createGpt5Model("gpt-5.2")
309
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
310
- expect(result.textVerbosity).toBe("low")
311
- expect(result.include).toEqual(["reasoning.encrypted_content"])
312
- })
313
-
314
- test("Bedrock Mantle gpt-5.5 uses OpenAI Responses defaults", () => {
315
- const model = {
316
- ...createGpt5Model("openai.gpt-5.5"),
317
- id: "amazon-bedrock/openai.gpt-5.5",
318
- providerID: "amazon-bedrock",
319
- api: {
320
- id: "openai.gpt-5.5",
321
- url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
322
- npm: "@ai-sdk/amazon-bedrock/mantle",
323
- },
324
- }
325
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
326
- expect(result.store).toBe(false)
327
- expect(result.reasoningEffort).toBe("medium")
328
- expect(result.reasoningSummary).toBe("auto")
329
- expect(result.include).toEqual(["reasoning.encrypted_content"])
330
- expect(result.textVerbosity).toBe("low")
331
- })
332
-
333
- test("openai-compatible gpt-5 models omit Responses-only reasoningSummary", () => {
334
- const model = {
335
- ...createGpt5Model("gpt-5.4"),
336
- id: "cortecs/gpt-5.4",
337
- providerID: "cortecs",
338
- api: {
339
- id: "gpt-5.4",
340
- url: "https://api.cortecs.ai/v1",
341
- npm: "@ai-sdk/openai-compatible",
342
- },
343
- }
344
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
345
- expect(result.reasoningEffort).toBe("medium")
346
- expect(result.reasoningSummary).toBeUndefined()
347
- expect(result.include).toBeUndefined()
348
- })
349
-
350
- test("azure chat completions omit Responses-only reasoning options after variants merge", async () => {
351
- const model = {
352
- ...createGpt5Model("gpt-5.4"),
353
- id: "azure/gpt-5.4",
354
- providerID: "azure",
355
- api: {
356
- id: "gpt-5.4",
357
- url: "https://azure.com",
358
- npm: "@ai-sdk/azure",
359
- },
360
- variants: {
361
- high: {
362
- reasoningEffort: "high",
363
- reasoningSummary: "auto",
364
- include: ["reasoning.encrypted_content"],
365
- },
366
- },
367
- }
368
- const result = await Effect.runPromise(
369
- LLMRequestPrep.prepare({
370
- user: {
371
- id: "msg_user-test",
372
- sessionID,
373
- role: "user",
374
- time: { created: Date.now() },
375
- agent: "test",
376
- model: { providerID: "azure", modelID: "gpt-5.4", variant: "high" },
377
- } as any,
378
- sessionID,
379
- model,
380
- agent: {
381
- name: "test",
382
- mode: "primary",
383
- options: {},
384
- permission: [],
385
- } as any,
386
- system: [],
387
- messages: [{ role: "user", content: "Hello" }],
388
- tools: {
389
- lookup: {
390
- description: "Look up a value",
391
- inputSchema: jsonSchema({ type: "object", properties: {} }),
392
- },
393
- },
394
- provider: { id: "azure", options: { useCompletionUrls: true } } as any,
395
- auth: undefined,
396
- plugin: {
397
- trigger: (_name: string, _input: unknown, output: unknown) => Effect.succeed(output),
398
- list: () => Effect.succeed([]),
399
- init: () => Effect.void,
400
- } as any,
401
- flags: { outputTokenMax: 32_000, client: "test" } as any,
402
- isWorkflow: false,
403
- }),
404
- )
405
- expect(result.params.options.reasoningEffort).toBe("high")
406
- expect(result.params.options.reasoningSummary).toBeUndefined()
407
- expect(result.params.options.include).toBeUndefined()
408
- expect(result.tools.lookup.strict).toBe(false)
409
- })
410
-
411
- test("gpt-5.1 should have textVerbosity set to low", () => {
412
- const model = createGpt5Model("gpt-5.1")
413
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
414
- expect(result.textVerbosity).toBe("low")
415
- })
416
-
417
- test("gpt-5.2-chat-latest should NOT have textVerbosity set (only supports medium)", () => {
418
- const model = createGpt5Model("gpt-5.2-chat-latest")
419
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
420
- expect(result.textVerbosity).toBeUndefined()
421
- })
422
-
423
- test("gpt-5.1-chat-latest should NOT have textVerbosity set (only supports medium)", () => {
424
- const model = createGpt5Model("gpt-5.1-chat-latest")
425
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
426
- expect(result.textVerbosity).toBeUndefined()
427
- })
428
-
429
- test("gpt-5.2-chat should NOT have textVerbosity set", () => {
430
- const model = createGpt5Model("gpt-5.2-chat")
431
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
432
- expect(result.textVerbosity).toBeUndefined()
433
- })
434
-
435
- test("gpt-5-chat should NOT have textVerbosity set", () => {
436
- const model = createGpt5Model("gpt-5-chat")
437
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
438
- expect(result.textVerbosity).toBeUndefined()
439
- })
440
-
441
- test("gpt-5.2-codex should NOT have textVerbosity set (codex models excluded)", () => {
442
- const model = createGpt5Model("gpt-5.2-codex")
443
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
444
- expect(result.textVerbosity).toBeUndefined()
445
- })
446
- })
447
-
448
- describe("ProviderTransform.options - gpt-5 reasoningEffort", () => {
449
- const sessionID = "test-session-123"
450
-
451
- const createModel = (apiId: string) =>
452
- ({
453
- id: `azure/${apiId}`,
454
- providerID: "azure",
455
- api: {
456
- id: apiId,
457
- url: "https://azure.com",
458
- npm: "@ai-sdk/azure",
459
- },
460
- name: apiId,
461
- capabilities: {
462
- temperature: true,
463
- reasoning: true,
464
- attachment: true,
465
- toolcall: true,
466
- input: {
467
- text: true,
468
- audio: false,
469
- image: true,
470
- video: false,
471
- pdf: false,
472
- },
473
- output: {
474
- text: true,
475
- audio: false,
476
- image: false,
477
- video: false,
478
- pdf: false,
479
- },
480
- interleaved: false,
481
- },
482
- cost: {
483
- input: 0.03,
484
- output: 0.06,
485
- cache: { read: 0.001, write: 0.002 },
486
- },
487
- limit: {
488
- context: 128000,
489
- output: 4096,
490
- },
491
- status: "active",
492
- options: {},
493
- headers: {},
494
- }) as any
495
-
496
- test("gpt-5-chat should NOT set reasoningEffort", () => {
497
- const result = ProviderTransform.options({
498
- model: createModel("gpt-5-chat"),
499
- sessionID,
500
- providerOptions: {},
501
- })
502
-
503
- expect(result.reasoningEffort).toBeUndefined()
504
- })
505
-
506
- test("gpt-5.5 should NOT set reasoningEffort", () => {
507
- const result = ProviderTransform.options({
508
- model: createModel("gpt-5.5"),
509
- sessionID,
510
- providerOptions: {},
511
- })
512
-
513
- expect(result.reasoningEffort).toBeUndefined()
514
- })
515
- })
516
-
517
- describe("ProviderTransform.options - gateway", () => {
518
- const sessionID = "test-session-123"
519
-
520
- const createModel = (id: string) =>
521
- ({
522
- id,
523
- providerID: "vercel",
524
- api: {
525
- id,
526
- url: "https://ai-gateway.vercel.sh/v3/ai",
527
- npm: "@ai-sdk/gateway",
528
- },
529
- name: id,
530
- capabilities: {
531
- temperature: true,
532
- reasoning: true,
533
- attachment: true,
534
- toolcall: true,
535
- input: { text: true, audio: false, image: true, video: false, pdf: true },
536
- output: { text: true, audio: false, image: false, video: false, pdf: false },
537
- interleaved: false,
538
- },
539
- cost: {
540
- input: 0.001,
541
- output: 0.002,
542
- cache: { read: 0.0001, write: 0.0002 },
543
- },
544
- limit: {
545
- context: 200_000,
546
- output: 8192,
547
- },
548
- status: "active",
549
- options: {},
550
- headers: {},
551
- release_date: "2024-01-01",
552
- }) as any
553
-
554
- test("puts gateway defaults under gateway key", () => {
555
- const model = createModel("anthropic/claude-sonnet-4")
556
- const result = ProviderTransform.options({ model, sessionID, providerOptions: {} })
557
- expect(result).toEqual({
558
- gateway: {
559
- caching: "auto",
560
- },
561
- })
562
- })
563
- })
564
-
565
- describe("ProviderTransform.providerOptions", () => {
566
- const createModel = (overrides: Partial<any> = {}) =>
567
- ({
568
- id: "test/test-model",
569
- providerID: "test",
570
- api: {
571
- id: "test-model",
572
- url: "https://api.test.com",
573
- npm: "@ai-sdk/openai",
574
- },
575
- name: "Test Model",
576
- capabilities: {
577
- temperature: true,
578
- reasoning: true,
579
- attachment: true,
580
- toolcall: true,
581
- input: { text: true, audio: false, image: true, video: false, pdf: false },
582
- output: { text: true, audio: false, image: false, video: false, pdf: false },
583
- interleaved: false,
584
- },
585
- cost: {
586
- input: 0.001,
587
- output: 0.002,
588
- cache: { read: 0.0001, write: 0.0002 },
589
- },
590
- limit: {
591
- context: 200_000,
592
- output: 64_000,
593
- },
594
- status: "active",
595
- options: {},
596
- headers: {},
597
- release_date: "2024-01-01",
598
- ...overrides,
599
- }) as any
600
-
601
- test("uses sdk key for non-gateway models", () => {
602
- const model = createModel({
603
- providerID: "my-bedrock",
604
- api: {
605
- id: "anthropic.claude-sonnet-4",
606
- url: "https://bedrock.aws",
607
- npm: "@ai-sdk/amazon-bedrock",
608
- },
609
- })
610
-
611
- expect(ProviderTransform.providerOptions(model, { cachePoint: { type: "default" } })).toEqual({
612
- bedrock: { cachePoint: { type: "default" } },
613
- })
614
- })
615
-
616
- test("forces reasoning for custom OpenAI package models with explicit effort", () => {
617
- const model = createModel({
618
- providerID: "meta",
619
- api: {
620
- id: "muse-spark",
621
- url: "https://api.ai.meta.com/v1",
622
- npm: "@ai-sdk/openai",
623
- },
624
- })
625
-
626
- expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh", reasoningSummary: "auto" })).toEqual({
627
- openai: { forceReasoning: true, reasoningEffort: "xhigh", reasoningSummary: "auto" },
628
- })
629
- })
630
-
631
- test("forces reasoning for OpenAI package models marked reasoning-capable", () => {
632
- expect(ProviderTransform.providerOptions(createModel(), { store: false })).toEqual({
633
- openai: { forceReasoning: true, store: false },
634
- })
635
- })
636
-
637
- test("forces reasoning for explicit effort even when model is not marked reasoning-capable", () => {
638
- const model = createModel({
639
- capabilities: {
640
- temperature: true,
641
- reasoning: false,
642
- attachment: true,
643
- toolcall: true,
644
- input: { text: true, audio: false, image: true, video: false, pdf: false },
645
- output: { text: true, audio: false, image: false, video: false, pdf: false },
646
- interleaved: false,
647
- },
648
- })
649
-
650
- expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh" })).toEqual({
651
- openai: { forceReasoning: true, reasoningEffort: "xhigh" },
652
- })
653
- })
654
-
655
- test("forces reasoning for Azure OpenAI models with explicit effort", () => {
656
- const model = createModel({
657
- providerID: "azure",
658
- api: {
659
- id: "custom-gpt-5-deployment",
660
- url: "https://azure.openai.example.com/openai/v1",
661
- npm: "@ai-sdk/azure",
662
- },
663
- })
664
-
665
- expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh" })).toEqual({
666
- openai: { forceReasoning: true, reasoningEffort: "xhigh" },
667
- azure: { forceReasoning: true, reasoningEffort: "xhigh" },
668
- })
669
- })
670
-
671
- test("forces reasoning for Bedrock Mantle OpenAI models with explicit effort", () => {
672
- const model = createModel({
673
- providerID: "amazon-bedrock",
674
- api: {
675
- id: "openai.gpt-5-custom",
676
- url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
677
- npm: "@ai-sdk/amazon-bedrock/mantle",
678
- },
679
- })
680
-
681
- expect(ProviderTransform.providerOptions(model, { reasoningEffort: "xhigh" })).toEqual({
682
- openai: { forceReasoning: true, reasoningEffort: "xhigh" },
683
- })
684
- })
685
-
686
- test("overrides forceReasoning false when reasoning should be forced", () => {
687
- expect(
688
- ProviderTransform.providerOptions(createModel(), { forceReasoning: false, reasoningEffort: "xhigh" }),
689
- ).toEqual({
690
- openai: { forceReasoning: true, reasoningEffort: "xhigh" },
691
- })
692
- })
693
-
694
- test("uses gateway model provider slug for gateway models", () => {
695
- const model = createModel({
696
- providerID: "vercel",
697
- api: {
698
- id: "anthropic/claude-sonnet-4",
699
- url: "https://ai-gateway.vercel.sh/v3/ai",
700
- npm: "@ai-sdk/gateway",
701
- },
702
- })
703
-
704
- expect(ProviderTransform.providerOptions(model, { thinking: { type: "enabled", budgetTokens: 12_000 } })).toEqual({
705
- anthropic: { thinking: { type: "enabled", budgetTokens: 12_000 } },
706
- })
707
- })
708
-
709
- test("falls back to gateway key when gateway api id is unscoped", () => {
710
- const model = createModel({
711
- id: "anthropic/claude-sonnet-4",
712
- providerID: "vercel",
713
- api: {
714
- id: "claude-sonnet-4",
715
- url: "https://ai-gateway.vercel.sh/v3/ai",
716
- npm: "@ai-sdk/gateway",
717
- },
718
- })
719
-
720
- expect(ProviderTransform.providerOptions(model, { thinking: { type: "enabled", budgetTokens: 12_000 } })).toEqual({
721
- gateway: { thinking: { type: "enabled", budgetTokens: 12_000 } },
722
- })
723
- })
724
-
725
- test("splits gateway routing options from provider-specific options", () => {
726
- const model = createModel({
727
- providerID: "vercel",
728
- api: {
729
- id: "anthropic/claude-sonnet-4",
730
- url: "https://ai-gateway.vercel.sh/v3/ai",
731
- npm: "@ai-sdk/gateway",
732
- },
733
- })
734
-
735
- expect(
736
- ProviderTransform.providerOptions(model, {
737
- gateway: { order: ["vertex", "anthropic"] },
738
- thinking: { type: "enabled", budgetTokens: 12_000 },
739
- }),
740
- ).toEqual({
741
- gateway: { order: ["vertex", "anthropic"] },
742
- anthropic: { thinking: { type: "enabled", budgetTokens: 12_000 } },
743
- } as any)
744
- })
745
-
746
- test("falls back to gateway key when model id has no provider slug", () => {
747
- const model = createModel({
748
- id: "claude-sonnet-4",
749
- providerID: "vercel",
750
- api: {
751
- id: "claude-sonnet-4",
752
- url: "https://ai-gateway.vercel.sh/v3/ai",
753
- npm: "@ai-sdk/gateway",
754
- },
755
- })
756
-
757
- expect(ProviderTransform.providerOptions(model, { reasoningEffort: "high" })).toEqual({
758
- gateway: { reasoningEffort: "high" },
759
- })
760
- })
761
-
762
- test("maps amazon slug to bedrock for provider options", () => {
763
- const model = createModel({
764
- providerID: "vercel",
765
- api: {
766
- id: "amazon/nova-2-lite",
767
- url: "https://ai-gateway.vercel.sh/v3/ai",
768
- npm: "@ai-sdk/gateway",
769
- },
770
- })
771
-
772
- expect(ProviderTransform.providerOptions(model, { reasoningConfig: { type: "enabled" } })).toEqual({
773
- bedrock: { reasoningConfig: { type: "enabled" } },
774
- })
775
- })
776
-
777
- test("maps Bedrock Mantle provider options to OpenAI namespace", () => {
778
- const model = createModel({
779
- providerID: "amazon-bedrock",
780
- api: {
781
- id: "openai.gpt-5.5",
782
- url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
783
- npm: "@ai-sdk/amazon-bedrock/mantle",
784
- },
785
- })
786
-
787
- expect(ProviderTransform.providerOptions(model, { reasoningEffort: "medium" })).toEqual({
788
- openai: { forceReasoning: true, reasoningEffort: "medium" },
789
- })
790
- })
791
-
792
- test("uses groq slug for groq models", () => {
793
- const model = createModel({
794
- providerID: "vercel",
795
- api: {
796
- id: "groq/llama-3.3-70b-versatile",
797
- url: "https://ai-gateway.vercel.sh/v3/ai",
798
- npm: "@ai-sdk/gateway",
799
- },
800
- })
801
-
802
- expect(ProviderTransform.providerOptions(model, { reasoningFormat: "parsed" })).toEqual({
803
- groq: { reasoningFormat: "parsed" },
804
- })
805
- })
806
- })
807
-
808
- describe("ProviderTransform.schema - gemini array items", () => {
809
- test("adds missing items for array properties", () => {
810
- const geminiModel = {
811
- providerID: "google",
812
- api: {
813
- id: "gemini-3-pro",
814
- },
815
- } as any
816
-
817
- const schema = {
818
- type: "object",
819
- properties: {
820
- nodes: { type: "array" },
821
- edges: { type: "array", items: { type: "string" } },
822
- },
823
- } as any
824
-
825
- const result = ProviderTransform.schema(geminiModel, schema) as any
826
-
827
- expect(result.properties.nodes.items).toBeDefined()
828
- expect(result.properties.edges.items.type).toBe("string")
829
- })
830
- })
831
-
832
- describe("ProviderTransform.schema - gemini nested array items", () => {
833
- const geminiModel = {
834
- providerID: "google",
835
- api: {
836
- id: "gemini-3-pro",
837
- },
838
- } as any
839
-
840
- test("adds type to 2D array with empty inner items", () => {
841
- const schema = {
842
- type: "object",
843
- properties: {
844
- values: {
845
- type: "array",
846
- items: {
847
- type: "array",
848
- items: {}, // Empty items object
849
- },
850
- },
851
- },
852
- } as any
853
-
854
- const result = ProviderTransform.schema(geminiModel, schema) as any
855
-
856
- // Inner items should have a default type
857
- expect(result.properties.values.items.items.type).toBe("string")
858
- })
859
-
860
- test("adds items and type to 2D array with missing inner items", () => {
861
- const schema = {
862
- type: "object",
863
- properties: {
864
- data: {
865
- type: "array",
866
- items: { type: "array" }, // No items at all
867
- },
868
- },
869
- } as any
870
-
871
- const result = ProviderTransform.schema(geminiModel, schema) as any
872
-
873
- expect(result.properties.data.items.items).toBeDefined()
874
- expect(result.properties.data.items.items.type).toBe("string")
875
- })
876
-
877
- test("handles deeply nested arrays (3D)", () => {
878
- const schema = {
879
- type: "object",
880
- properties: {
881
- matrix: {
882
- type: "array",
883
- items: {
884
- type: "array",
885
- items: {
886
- type: "array",
887
- // No items
888
- },
889
- },
890
- },
891
- },
892
- } as any
893
-
894
- const result = ProviderTransform.schema(geminiModel, schema) as any
895
-
896
- expect(result.properties.matrix.items.items.items).toBeDefined()
897
- expect(result.properties.matrix.items.items.items.type).toBe("string")
898
- })
899
-
900
- test("preserves existing item types in nested arrays", () => {
901
- const schema = {
902
- type: "object",
903
- properties: {
904
- numbers: {
905
- type: "array",
906
- items: {
907
- type: "array",
908
- items: { type: "number" }, // Has explicit type
909
- },
910
- },
911
- },
912
- } as any
913
-
914
- const result = ProviderTransform.schema(geminiModel, schema) as any
915
-
916
- // Should preserve the explicit type
917
- expect(result.properties.numbers.items.items.type).toBe("number")
918
- })
919
-
920
- test("handles mixed nested structures with objects and arrays", () => {
921
- const schema = {
922
- type: "object",
923
- properties: {
924
- spreadsheetData: {
925
- type: "object",
926
- properties: {
927
- rows: {
928
- type: "array",
929
- items: {
930
- type: "array",
931
- items: {}, // Empty items
932
- },
933
- },
934
- },
935
- },
936
- },
937
- } as any
938
-
939
- const result = ProviderTransform.schema(geminiModel, schema) as any
940
-
941
- expect(result.properties.spreadsheetData.properties.rows.items.items.type).toBe("string")
942
- })
943
- })
944
-
945
- describe("ProviderTransform.schema - gemini type arrays", () => {
946
- // Mirrors @ai-sdk/google's convertJSONSchemaToOpenAPISchema: JSON Schema type
947
- // arrays (e.g. `["number","string"]`, common in MCP tool schemas) become an
948
- // `anyOf` of single-type schemas, with `null` lifted into `nullable`. Plain
949
- // @ai-sdk/google rewrites these, but OpenAI-compatible transports such as
950
- // GitHub Copilot (proxying to Gemini) forward them verbatim and the backend
951
- // rejects the array form.
952
- const geminiModel = {
953
- providerID: "google",
954
- api: {
955
- id: "gemini-3-pro",
956
- },
957
- } as any
958
-
959
- test("splits a multi-type array into anyOf and drops the type array", () => {
960
- const schema = {
961
- type: "object",
962
- properties: {
963
- status: { type: ["number", "string"], description: "status filter" },
964
- },
965
- } as any
966
-
967
- const result = ProviderTransform.schema(geminiModel, schema) as any
968
-
969
- expect(result.properties.status.type).toBeUndefined()
970
- expect(result.properties.status.anyOf).toEqual([{ type: "number" }, { type: "string" }])
971
- expect(result.properties.status.nullable).toBeUndefined()
972
- // Sibling keywords stay alongside the generated anyOf.
973
- expect(result.properties.status.description).toBe("status filter")
974
- })
975
-
976
- test("lifts null into nullable for a nullable type array", () => {
977
- const schema = {
978
- type: "object",
979
- properties: {
980
- maybe: { type: ["string", "null"], description: "nullable string" },
981
- },
982
- } as any
983
-
984
- const result = ProviderTransform.schema(geminiModel, schema) as any
985
-
986
- expect(result.properties.maybe.type).toBeUndefined()
987
- expect(result.properties.maybe.anyOf).toEqual([{ type: "string" }])
988
- expect(result.properties.maybe.nullable).toBe(true)
989
- })
990
-
991
- test("collapses an all-null type array to type null", () => {
992
- const schema = {
993
- type: "object",
994
- properties: {
995
- nothing: { type: ["null"] },
996
- },
997
- } as any
998
-
999
- const result = ProviderTransform.schema(geminiModel, schema) as any
1000
-
1001
- expect(result.properties.nothing.type).toBe("null")
1002
- expect(result.properties.nothing.anyOf).toBeUndefined()
1003
- })
1004
-
1005
- test("rewrites type arrays for gemini served through github-copilot", () => {
1006
- const copilotGeminiModel = {
1007
- providerID: "github-copilot",
1008
- api: {
1009
- id: "gemini-3.5-flash",
1010
- npm: "@ai-sdk/github-copilot",
1011
- },
1012
- } as any
1013
-
1014
- const schema = {
1015
- type: "object",
1016
- properties: {
1017
- hook_id: { type: "number", description: "ID of the webhook" },
1018
- status: { type: ["number", "string"], description: "Filter by response status code" },
1019
- },
1020
- required: ["hook_id"],
1021
- additionalProperties: false,
1022
- } as any
1023
-
1024
- const result = ProviderTransform.schema(copilotGeminiModel, schema) as any
1025
-
1026
- expect(result.properties.status.anyOf).toEqual([{ type: "number" }, { type: "string" }])
1027
- expect(result.properties.status.type).toBeUndefined()
1028
- expect(result.properties.hook_id.type).toBe("number")
1029
- })
1030
- })
1031
-
1032
- describe("ProviderTransform.schema - gemini combiner nodes", () => {
1033
- const geminiModel = {
1034
- providerID: "google",
1035
- api: {
1036
- id: "gemini-3-pro",
1037
- },
1038
- } as any
1039
-
1040
- const walk = (node: any, cb: (node: any, path: (string | number)[]) => void, path: (string | number)[] = []) => {
1041
- if (node === null || typeof node !== "object") {
1042
- return
1043
- }
1044
- if (Array.isArray(node)) {
1045
- node.forEach((item, i) => walk(item, cb, [...path, i]))
1046
- return
1047
- }
1048
- cb(node, path)
1049
- Object.entries(node).forEach(([key, value]) => walk(value, cb, [...path, key]))
1050
- }
1051
-
1052
- test("keeps edits.items.anyOf without adding type", () => {
1053
- const schema = {
1054
- type: "object",
1055
- properties: {
1056
- edits: {
1057
- type: "array",
1058
- items: {
1059
- anyOf: [
1060
- {
1061
- type: "object",
1062
- properties: {
1063
- old_string: { type: "string" },
1064
- new_string: { type: "string" },
1065
- },
1066
- required: ["old_string", "new_string"],
1067
- },
1068
- {
1069
- type: "object",
1070
- properties: {
1071
- old_string: { type: "string" },
1072
- new_string: { type: "string" },
1073
- replace_all: { type: "boolean" },
1074
- },
1075
- required: ["old_string", "new_string"],
1076
- },
1077
- ],
1078
- },
1079
- },
1080
- },
1081
- required: ["edits"],
1082
- } as any
1083
-
1084
- const result = ProviderTransform.schema(geminiModel, schema) as any
1085
-
1086
- expect(Array.isArray(result.properties.edits.items.anyOf)).toBe(true)
1087
- expect(result.properties.edits.items.type).toBeUndefined()
1088
- })
1089
-
1090
- test("does not add sibling keys to combiner nodes during sanitize", () => {
1091
- const schema = {
1092
- type: "object",
1093
- properties: {
1094
- edits: {
1095
- type: "array",
1096
- items: {
1097
- anyOf: [{ type: "string" }, { type: "number" }],
1098
- },
1099
- },
1100
- value: {
1101
- oneOf: [{ type: "string" }, { type: "boolean" }],
1102
- },
1103
- meta: {
1104
- allOf: [
1105
- {
1106
- type: "object",
1107
- properties: { a: { type: "string" } },
1108
- },
1109
- {
1110
- type: "object",
1111
- properties: { b: { type: "string" } },
1112
- },
1113
- ],
1114
- },
1115
- },
1116
- } as any
1117
- const input = JSON.parse(JSON.stringify(schema))
1118
- const result = ProviderTransform.schema(geminiModel, schema) as any
1119
-
1120
- walk(result, (node, path) => {
1121
- const hasCombiner = Array.isArray(node.anyOf) || Array.isArray(node.oneOf) || Array.isArray(node.allOf)
1122
- if (!hasCombiner) {
1123
- return
1124
- }
1125
- const before = path.reduce((acc: any, key) => acc?.[key], input)
1126
- const added = Object.keys(node).filter((key) => !(key in before))
1127
- expect(added).toEqual([])
1128
- })
1129
- })
1130
- })
1131
-
1132
- describe("ProviderTransform.schema - gemini non-object properties removal", () => {
1133
- const geminiModel = {
1134
- providerID: "google",
1135
- api: {
1136
- id: "gemini-3-pro",
1137
- },
1138
- } as any
1139
-
1140
- test("removes properties from non-object types", () => {
1141
- const schema = {
1142
- type: "object",
1143
- properties: {
1144
- data: {
1145
- type: "string",
1146
- properties: { invalid: { type: "string" } },
1147
- },
1148
- },
1149
- } as any
1150
-
1151
- const result = ProviderTransform.schema(geminiModel, schema) as any
1152
-
1153
- expect(result.properties.data.type).toBe("string")
1154
- expect(result.properties.data.properties).toBeUndefined()
1155
- })
1156
-
1157
- test("removes required from non-object types", () => {
1158
- const schema = {
1159
- type: "object",
1160
- properties: {
1161
- data: {
1162
- type: "array",
1163
- items: { type: "string" },
1164
- required: ["invalid"],
1165
- },
1166
- },
1167
- } as any
1168
-
1169
- const result = ProviderTransform.schema(geminiModel, schema) as any
1170
-
1171
- expect(result.properties.data.type).toBe("array")
1172
- expect(result.properties.data.required).toBeUndefined()
1173
- })
1174
-
1175
- test("removes properties and required from nested non-object types", () => {
1176
- const schema = {
1177
- type: "object",
1178
- properties: {
1179
- outer: {
1180
- type: "object",
1181
- properties: {
1182
- inner: {
1183
- type: "number",
1184
- properties: { bad: { type: "string" } },
1185
- required: ["bad"],
1186
- },
1187
- },
1188
- },
1189
- },
1190
- } as any
1191
-
1192
- const result = ProviderTransform.schema(geminiModel, schema) as any
1193
-
1194
- expect(result.properties.outer.properties.inner.type).toBe("number")
1195
- expect(result.properties.outer.properties.inner.properties).toBeUndefined()
1196
- expect(result.properties.outer.properties.inner.required).toBeUndefined()
1197
- })
1198
-
1199
- test("keeps properties and required on object types", () => {
1200
- const schema = {
1201
- type: "object",
1202
- properties: {
1203
- data: {
1204
- type: "object",
1205
- properties: { name: { type: "string" } },
1206
- required: ["name"],
1207
- },
1208
- },
1209
- } as any
1210
-
1211
- const result = ProviderTransform.schema(geminiModel, schema) as any
1212
-
1213
- expect(result.properties.data.type).toBe("object")
1214
- expect(result.properties.data.properties).toBeDefined()
1215
- expect(result.properties.data.required).toEqual(["name"])
1216
- })
1217
-
1218
- test("does not affect non-gemini providers", () => {
1219
- const openaiModel = {
1220
- providerID: "openai",
1221
- api: {
1222
- id: "gpt-4",
1223
- },
1224
- } as any
1225
-
1226
- const schema = {
1227
- type: "object",
1228
- properties: {
1229
- data: {
1230
- type: "string",
1231
- properties: { invalid: { type: "string" } },
1232
- },
1233
- },
1234
- } as any
1235
-
1236
- const result = ProviderTransform.schema(openaiModel, schema) as any
1237
-
1238
- expect(result.properties.data.properties).toBeDefined()
1239
- })
1240
- })
1241
-
1242
- describe("ProviderTransform.schema - openai supported schema subset", () => {
1243
- const openaiModel = {
1244
- providerID: "openai",
1245
- api: {
1246
- id: "gpt-4.1",
1247
- npm: "@ai-sdk/openai",
1248
- },
1249
- } as any
1250
-
1251
- test("removes unsupported JSON Schema keywords recursively", () => {
1252
- const result = ProviderTransform.schema(openaiModel, {
1253
- $schema: "https://json-schema.org/draft/2020-12/schema",
1254
- title: "Search",
1255
- type: "object",
1256
- properties: {
1257
- query: {
1258
- type: "string",
1259
- description: "Search query",
1260
- format: "uri",
1261
- pattern: "^https://",
1262
- minLength: 1,
1263
- maxLength: 100,
1264
- default: "https://example.com",
1265
- },
1266
- count: {
1267
- type: "integer",
1268
- minimum: 1,
1269
- maximum: 10,
1270
- multipleOf: 1,
1271
- },
1272
- createdAt: {
1273
- format: "date-time",
1274
- },
1275
- mode: {
1276
- const: "fast",
1277
- },
1278
- tags: {
1279
- type: "array",
1280
- minItems: 1,
1281
- maxItems: 3,
1282
- uniqueItems: true,
1283
- },
1284
- tuple: {
1285
- type: "array",
1286
- items: [
1287
- { type: "number", minimum: 0 },
1288
- { type: "string", pattern: "^ok$" },
1289
- ],
1290
- },
1291
- metadata: {
1292
- type: "object",
1293
- patternProperties: {
1294
- "^x-": { type: "string" },
1295
- },
1296
- additionalProperties: {
1297
- type: "string",
1298
- pattern: "^safe$",
1299
- },
1300
- },
1301
- },
1302
- patternProperties: {
1303
- "^extra": { type: "string" },
1304
- },
1305
- required: ["query"],
1306
- additionalProperties: false,
1307
- } as any) as any
1308
-
1309
- expect(result).toEqual({
1310
- type: "object",
1311
- properties: {
1312
- query: {
1313
- type: "string",
1314
- description: "Search query",
1315
- },
1316
- count: {
1317
- type: "integer",
1318
- },
1319
- createdAt: {
1320
- type: "string",
1321
- },
1322
- mode: {
1323
- enum: ["fast"],
1324
- type: "string",
1325
- },
1326
- tags: {
1327
- type: "array",
1328
- items: { type: "string" },
1329
- },
1330
- tuple: {
1331
- type: "array",
1332
- items: [{ type: "number" }, { type: "string" }],
1333
- },
1334
- metadata: {
1335
- type: "object",
1336
- properties: {},
1337
- additionalProperties: {
1338
- type: "string",
1339
- },
1340
- },
1341
- },
1342
- required: ["query"],
1343
- additionalProperties: false,
1344
- })
1345
- })
1346
-
1347
- test("keeps local references and sanitizes definitions", () => {
1348
- const result = ProviderTransform.schema(openaiModel, {
1349
- type: "object",
1350
- properties: {
1351
- value: {
1352
- $ref: "#/$defs/Value",
1353
- description: "Referenced value",
1354
- examples: ["ignored"],
1355
- },
1356
- },
1357
- $defs: {
1358
- Value: {
1359
- type: "string",
1360
- pattern: "^value$",
1361
- description: "Definition description",
1362
- },
1363
- Unused: {
1364
- type: "number",
1365
- minimum: 0,
1366
- },
1367
- },
1368
- } as any) as any
1369
-
1370
- expect(result.properties.value).toEqual({
1371
- $ref: "#/$defs/Value",
1372
- description: "Referenced value",
1373
- })
1374
- expect(result.$defs).toEqual({
1375
- Value: {
1376
- type: "string",
1377
- description: "Definition description",
1378
- },
1379
- Unused: {
1380
- type: "number",
1381
- },
1382
- })
1383
- })
1384
-
1385
- test("does not sanitize non-openai providers", () => {
1386
- const result = ProviderTransform.schema(
1387
- {
1388
- providerID: "anthropic",
1389
- api: {
1390
- id: "claude-sonnet-4",
1391
- npm: "@ai-sdk/anthropic",
1392
- },
1393
- } as any,
1394
- {
1395
- type: "object",
1396
- properties: {
1397
- query: {
1398
- type: "string",
1399
- pattern: "^https://",
1400
- },
1401
- },
1402
- } as any,
1403
- ) as any
1404
-
1405
- expect(result.properties.query.pattern).toBe("^https://")
1406
- })
1407
-
1408
- test.each([
1409
- ["opencode", "@ai-sdk/openai"],
1410
- ["custom-openai-compatible", "@ai-sdk/openai"],
1411
- ["azure", "@ai-sdk/azure"],
1412
- ])("sanitizes %s models using %s", (providerID, npm) => {
1413
- expect(
1414
- ProviderTransform.schema(
1415
- {
1416
- providerID,
1417
- api: {
1418
- id: "custom-model",
1419
- npm,
1420
- },
1421
- } as any,
1422
- {
1423
- type: "object",
1424
- properties: {
1425
- query: {
1426
- type: "string",
1427
- pattern: "^https://",
1428
- },
1429
- },
1430
- } as any,
1431
- ),
1432
- ).toEqual({
1433
- type: "object",
1434
- properties: {
1435
- query: {
1436
- type: "string",
1437
- },
1438
- },
1439
- })
1440
- })
1441
- })
1442
-
1443
- describe("ProviderTransform.schema - moonshot $ref siblings", () => {
1444
- const moonshotModel = {
1445
- providerID: "moonshotai",
1446
- api: {
1447
- id: "kimi-k2",
1448
- },
1449
- } as any
1450
-
1451
- test("removes sibling descriptions from referenced tool parameter schemas", () => {
1452
- const schema = {
1453
- type: "object",
1454
- properties: {
1455
- deviceType: {
1456
- description: "Optional. The type of device that captured the screenshot, e.g. mobile or desktop.",
1457
- enum: ["DEVICE_TYPE_UNSPECIFIED", "MOBILE", "DESKTOP", "TABLET", "AGNOSTIC"],
1458
- type: "string",
1459
- },
1460
- modelId: {
1461
- description: "Optional. The model to use for generation.",
1462
- enum: ["MODEL_ID_UNSPECIFIED", "GEMINI_3_PRO", "GEMINI_3_FLASH", "GEMINI_3_1_PRO"],
1463
- type: "string",
1464
- },
1465
- projectId: {
1466
- description: "Required. The project ID of screens to generate variants for.",
1467
- type: "string",
1468
- },
1469
- prompt: {
1470
- description: "Required. The input text used to generate the variants.",
1471
- type: "string",
1472
- },
1473
- selectedScreenIds: {
1474
- description: "Required. The screen ids of screen to generate variants for.",
1475
- items: {
1476
- type: "string",
1477
- },
1478
- type: "array",
1479
- },
1480
- variantOptions: {
1481
- $ref: "#/$defs/VariantOptions",
1482
- description:
1483
- "Required. The variant options for generation, including the number of variants, creative range, and aspects to focus on.",
1484
- },
1485
- },
1486
- required: ["projectId", "selectedScreenIds", "prompt", "variantOptions"],
1487
- $defs: {
1488
- VariantOptions: {
1489
- description:
1490
- "Configuration options for design variant generation. This message captures all parameters used to generate variants, allowing the configuration to be stored, replayed, or analyzed.",
1491
- properties: {
1492
- aspects: {
1493
- description: "Optional. Specific aspects to focus on. If empty, all aspects may be varied.",
1494
- items: {
1495
- enum: ["VARIANT_ASPECT_UNSPECIFIED", "LAYOUT", "COLOR_SCHEME", "IMAGES", "TEXT_FONT", "TEXT_CONTENT"],
1496
- type: "string",
1497
- },
1498
- type: "array",
1499
- },
1500
- creativeRange: {
1501
- description: "Optional. Creative range for variations. Default: EXPLORE",
1502
- enum: ["CREATIVE_RANGE_UNSPECIFIED", "REFINE", "EXPLORE", "REIMAGINE"],
1503
- type: "string",
1504
- },
1505
- variantCount: {
1506
- description: "Optional. Number of variants to generate (1-5). Default: 3",
1507
- format: "int32",
1508
- type: "integer",
1509
- },
1510
- },
1511
- type: "object",
1512
- },
1513
- },
1514
- description: "Request message for GenerateVariants.",
1515
- additionalProperties: false,
1516
- } as any
1517
-
1518
- const result = ProviderTransform.schema(moonshotModel, schema) as any
1519
-
1520
- expect(result.properties.variantOptions).toEqual({
1521
- $ref: "#/$defs/VariantOptions",
1522
- })
1523
- expect(result.$defs.VariantOptions.description).toBe(schema.$defs.VariantOptions.description)
1524
- })
1525
-
1526
- test("also runs for kimi models outside the moonshot provider", () => {
1527
- const result = ProviderTransform.schema(
1528
- {
1529
- providerID: "openrouter",
1530
- name: "Kimi K2",
1531
- api: {
1532
- id: "moonshotai/kimi-k2",
1533
- },
1534
- } as any,
1535
- {
1536
- type: "object",
1537
- properties: {
1538
- value: {
1539
- $ref: "#/$defs/Value",
1540
- description: "Moonshot rejects this sibling after ref expansion.",
1541
- },
1542
- },
1543
- $defs: {
1544
- Value: {
1545
- description: "Referenced schema description stays here.",
1546
- type: "object",
1547
- },
1548
- },
1549
- } as any,
1550
- ) as any
1551
-
1552
- expect(result.properties.value).toEqual({
1553
- $ref: "#/$defs/Value",
1554
- })
1555
- })
1556
-
1557
- test("converts tuple-style array items to a single item schema", () => {
1558
- const result = ProviderTransform.schema(moonshotModel, {
1559
- type: "object",
1560
- properties: {
1561
- codeSpec: {
1562
- type: "object",
1563
- properties: {
1564
- accessibility: {
1565
- type: "object",
1566
- properties: {
1567
- renderedSize: {
1568
- description: "Rendered size [width, height] in px",
1569
- type: "array",
1570
- items: [{ type: "number" }, { type: "number" }],
1571
- minItems: 2,
1572
- maxItems: 2,
1573
- },
1574
- },
1575
- },
1576
- },
1577
- },
1578
- },
1579
- } as any) as any
1580
-
1581
- expect(result.properties.codeSpec.properties.accessibility.properties.renderedSize.items).toEqual({
1582
- type: "number",
1583
- })
1584
- })
1585
- })
1586
-
1587
- describe("ProviderTransform.message - DeepSeek reasoning content", () => {
1588
- test("DeepSeek with tool calls includes reasoning_content in providerOptions", () => {
1589
- const msgs = [
1590
- {
1591
- role: "assistant",
1592
- content: [
1593
- { type: "reasoning", text: "Let me think about this..." },
1594
- {
1595
- type: "tool-call",
1596
- toolCallId: "test",
1597
- toolName: "bash",
1598
- input: { command: "echo hello" },
1599
- },
1600
- ],
1601
- },
1602
- ] as any[]
1603
-
1604
- const result = ProviderTransform.message(
1605
- msgs,
1606
- {
1607
- id: ModelV2.ID.make("deepseek/deepseek-chat"),
1608
- providerID: ProviderV2.ID.make("deepseek"),
1609
- api: {
1610
- id: "deepseek-chat",
1611
- url: "https://api.deepseek.com",
1612
- npm: "@ai-sdk/openai-compatible",
1613
- },
1614
- name: "DeepSeek Chat",
1615
- capabilities: {
1616
- temperature: true,
1617
- reasoning: true,
1618
- attachment: false,
1619
- toolcall: true,
1620
- input: { text: true, audio: false, image: false, video: false, pdf: false },
1621
- output: { text: true, audio: false, image: false, video: false, pdf: false },
1622
- interleaved: {
1623
- field: "reasoning_content",
1624
- },
1625
- },
1626
- cost: {
1627
- input: 0.001,
1628
- output: 0.002,
1629
- cache: { read: 0.0001, write: 0.0002 },
1630
- },
1631
- limit: {
1632
- context: 128000,
1633
- output: 8192,
1634
- },
1635
- status: "active",
1636
- options: {},
1637
- headers: {},
1638
- release_date: "2023-04-01",
1639
- },
1640
- {},
1641
- )
1642
-
1643
- expect(result).toHaveLength(1)
1644
- expect(result[0].content).toEqual([
1645
- {
1646
- type: "tool-call",
1647
- toolCallId: "test",
1648
- toolName: "bash",
1649
- input: { command: "echo hello" },
1650
- },
1651
- ])
1652
- expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBe("Let me think about this...")
1653
- })
1654
-
1655
- test("Non-DeepSeek providers leave reasoning content unchanged", () => {
1656
- const msgs = [
1657
- {
1658
- role: "assistant",
1659
- content: [
1660
- { type: "reasoning", text: "Should not be processed" },
1661
- { type: "text", text: "Answer" },
1662
- ],
1663
- },
1664
- ] as any[]
1665
-
1666
- const result = ProviderTransform.message(
1667
- msgs,
1668
- {
1669
- id: ModelV2.ID.make("openai/gpt-4"),
1670
- providerID: ProviderV2.ID.make("openai"),
1671
- api: {
1672
- id: "gpt-4",
1673
- url: "https://api.openai.com",
1674
- npm: "@ai-sdk/openai",
1675
- },
1676
- name: "GPT-4",
1677
- capabilities: {
1678
- temperature: true,
1679
- reasoning: false,
1680
- attachment: true,
1681
- toolcall: true,
1682
- input: { text: true, audio: false, image: true, video: false, pdf: false },
1683
- output: { text: true, audio: false, image: false, video: false, pdf: false },
1684
- interleaved: false,
1685
- },
1686
- cost: {
1687
- input: 0.03,
1688
- output: 0.06,
1689
- cache: { read: 0.001, write: 0.002 },
1690
- },
1691
- limit: {
1692
- context: 128000,
1693
- output: 4096,
1694
- },
1695
- status: "active",
1696
- options: {},
1697
- headers: {},
1698
- release_date: "2023-04-01",
1699
- },
1700
- {},
1701
- )
1702
-
1703
- expect(result[0].content).toEqual([
1704
- { type: "reasoning", text: "Should not be processed" },
1705
- { type: "text", text: "Answer" },
1706
- ])
1707
- expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined()
1708
- })
1709
- })
1710
-
1711
- describe("ProviderTransform.message - surrogate sanitization", () => {
1712
- const model = {
1713
- id: "test/test-model",
1714
- providerID: "test",
1715
- api: {
1716
- id: "test-model",
1717
- url: "https://api.test.com",
1718
- npm: "@ai-sdk/openai-compatible",
1719
- },
1720
- name: "Test Model",
1721
- capabilities: {
1722
- temperature: true,
1723
- reasoning: true,
1724
- attachment: true,
1725
- toolcall: true,
1726
- input: { text: true, audio: false, image: true, video: false, pdf: false },
1727
- output: { text: true, audio: false, image: false, video: false, pdf: false },
1728
- interleaved: false,
1729
- },
1730
- cost: { input: 0.001, output: 0.002, cache: { read: 0.0001, write: 0.0002 } },
1731
- limit: { context: 128000, output: 8192 },
1732
- status: "active",
1733
- options: {},
1734
- headers: {},
1735
- } as any
1736
-
1737
- test("replaces lone surrogates in model-visible text", () => {
1738
- const lone = "\uD83D"
1739
- const valid = "🚀"
1740
- const sanitized = "�"
1741
- const text = (label: string) => `${label} ${lone} and ${valid}`
1742
- const expected = (label: string) => `${label} ${sanitized} and ${valid}`
1743
- const msgs = [
1744
- { role: "system", content: text("system") },
1745
- { role: "user", content: text("user string") },
1746
- {
1747
- role: "user",
1748
- content: [
1749
- { type: "text", text: text("user text") },
1750
- { type: "image", image: "data:image/png;base64,abcd" },
1751
- ],
1752
- },
1753
- { role: "assistant", content: text("assistant string") },
1754
- {
1755
- role: "assistant",
1756
- content: [
1757
- { type: "text", text: text("assistant text") },
1758
- { type: "reasoning", text: text("assistant reasoning") },
1759
- { type: "tool-call", toolCallId: "call-1", toolName: "Read", input: { filePath: ".opencode/tool/emoji.ts" } },
1760
- {
1761
- type: "tool-result",
1762
- toolCallId: "call-2",
1763
- toolName: "Read",
1764
- output: { type: "text", value: text("assistant tool text") },
1765
- },
1766
- {
1767
- type: "tool-result",
1768
- toolCallId: "call-3",
1769
- toolName: "Read",
1770
- output: { type: "error-text", value: text("assistant tool error") },
1771
- },
1772
- {
1773
- type: "tool-result",
1774
- toolCallId: "call-4",
1775
- toolName: "Read",
1776
- output: { type: "content", value: [{ type: "text", text: text("assistant tool content") }] },
1777
- },
1778
- ],
1779
- },
1780
- {
1781
- role: "tool",
1782
- content: [
1783
- {
1784
- type: "tool-result",
1785
- toolCallId: "call-5",
1786
- toolName: "Read",
1787
- output: { type: "text", value: text("tool text") },
1788
- },
1789
- {
1790
- type: "tool-result",
1791
- toolCallId: "call-6",
1792
- toolName: "Read",
1793
- output: { type: "error-text", value: text("tool error") },
1794
- },
1795
- {
1796
- type: "tool-result",
1797
- toolCallId: "call-7",
1798
- toolName: "Read",
1799
- output: { type: "content", value: [{ type: "text", text: text("tool content") }] },
1800
- },
1801
- ],
1802
- },
1803
- ] as any[]
1804
-
1805
- const result = ProviderTransform.message(msgs, model, {}) as any[]
1806
-
1807
- expect(result[0].content).toBe(expected("system"))
1808
- expect(result[1].content).toBe(expected("user string"))
1809
- expect(result[2].content[0].text).toBe(expected("user text"))
1810
- expect(result[3].content).toBe(expected("assistant string"))
1811
- expect(result[4].content[0].text).toBe(expected("assistant text"))
1812
- expect(result[4].content[1].text).toBe(expected("assistant reasoning"))
1813
- expect(result[4].content[3].output.value).toBe(expected("assistant tool text"))
1814
- expect(result[4].content[4].output.value).toBe(expected("assistant tool error"))
1815
- expect(result[4].content[5].output.value[0].text).toBe(expected("assistant tool content"))
1816
- expect(result[5].content[0].output.value).toBe(expected("tool text"))
1817
- expect(result[5].content[1].output.value).toBe(expected("tool error"))
1818
- expect(result[5].content[2].output.value[0].text).toBe(expected("tool content"))
1819
- expect(result[2].content[1]).toEqual({ type: "image", image: "data:image/png;base64,abcd" })
1820
- })
1821
- })
1822
-
1823
- describe("ProviderTransform.message - empty image handling", () => {
1824
- const mockModel = {
1825
- id: "anthropic/claude-3-5-sonnet",
1826
- providerID: "anthropic",
1827
- api: {
1828
- id: "claude-3-5-sonnet-20241022",
1829
- url: "https://api.anthropic.com",
1830
- npm: "@ai-sdk/anthropic",
1831
- },
1832
- name: "Claude 3.5 Sonnet",
1833
- capabilities: {
1834
- temperature: true,
1835
- reasoning: false,
1836
- attachment: true,
1837
- toolcall: true,
1838
- input: { text: true, audio: false, image: true, video: false, pdf: true },
1839
- output: { text: true, audio: false, image: false, video: false, pdf: false },
1840
- interleaved: false,
1841
- },
1842
- cost: {
1843
- input: 0.003,
1844
- output: 0.015,
1845
- cache: { read: 0.0003, write: 0.00375 },
1846
- },
1847
- limit: {
1848
- context: 200000,
1849
- output: 8192,
1850
- },
1851
- status: "active",
1852
- options: {},
1853
- headers: {},
1854
- } as any
1855
-
1856
- test("should replace empty base64 image with error text", () => {
1857
- const msgs = [
1858
- {
1859
- role: "user",
1860
- content: [
1861
- { type: "text", text: "What is in this image?" },
1862
- { type: "image", image: "data:image/png;base64," },
1863
- ],
1864
- },
1865
- ] as any[]
1866
-
1867
- const result = ProviderTransform.message(msgs, mockModel, {})
1868
-
1869
- expect(result).toHaveLength(1)
1870
- expect(result[0].content).toHaveLength(2)
1871
- expect(result[0].content[0]).toEqual({ type: "text", text: "What is in this image?" })
1872
- expect(result[0].content[1]).toEqual({
1873
- type: "text",
1874
- text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
1875
- })
1876
- })
1877
-
1878
- test("should keep valid base64 images unchanged", () => {
1879
- const validBase64 =
1880
- "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
1881
- const msgs = [
1882
- {
1883
- role: "user",
1884
- content: [
1885
- { type: "text", text: "What is in this image?" },
1886
- { type: "image", image: `data:image/png;base64,${validBase64}` },
1887
- ],
1888
- },
1889
- ] as any[]
1890
-
1891
- const result = ProviderTransform.message(msgs, mockModel, {})
1892
-
1893
- expect(result).toHaveLength(1)
1894
- expect(result[0].content).toHaveLength(2)
1895
- expect(result[0].content[0]).toEqual({ type: "text", text: "What is in this image?" })
1896
- expect(result[0].content[1]).toEqual({ type: "image", image: `data:image/png;base64,${validBase64}` })
1897
- })
1898
-
1899
- test("should handle mixed valid and empty images", () => {
1900
- const validBase64 =
1901
- "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
1902
- const msgs = [
1903
- {
1904
- role: "user",
1905
- content: [
1906
- { type: "text", text: "Compare these images" },
1907
- { type: "image", image: `data:image/png;base64,${validBase64}` },
1908
- { type: "image", image: "data:image/jpeg;base64," },
1909
- ],
1910
- },
1911
- ] as any[]
1912
-
1913
- const result = ProviderTransform.message(msgs, mockModel, {})
1914
-
1915
- expect(result).toHaveLength(1)
1916
- expect(result[0].content).toHaveLength(3)
1917
- expect(result[0].content[0]).toEqual({ type: "text", text: "Compare these images" })
1918
- expect(result[0].content[1]).toEqual({ type: "image", image: `data:image/png;base64,${validBase64}` })
1919
- expect(result[0].content[2]).toEqual({
1920
- type: "text",
1921
- text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
1922
- })
1923
- })
1924
- })
1925
-
1926
- describe("ProviderTransform.message - anthropic empty content filtering", () => {
1927
- const anthropicModel = {
1928
- id: "anthropic/claude-3-5-sonnet",
1929
- providerID: "anthropic",
1930
- api: {
1931
- id: "claude-3-5-sonnet-20241022",
1932
- url: "https://api.anthropic.com",
1933
- npm: "@ai-sdk/anthropic",
1934
- },
1935
- name: "Claude 3.5 Sonnet",
1936
- capabilities: {
1937
- temperature: true,
1938
- reasoning: false,
1939
- attachment: true,
1940
- toolcall: true,
1941
- input: { text: true, audio: false, image: true, video: false, pdf: true },
1942
- output: { text: true, audio: false, image: false, video: false, pdf: false },
1943
- interleaved: false,
1944
- },
1945
- cost: {
1946
- input: 0.003,
1947
- output: 0.015,
1948
- cache: { read: 0.0003, write: 0.00375 },
1949
- },
1950
- limit: {
1951
- context: 200000,
1952
- output: 8192,
1953
- },
1954
- status: "active",
1955
- options: {},
1956
- headers: {},
1957
- } as any
1958
-
1959
- test("filters out messages with empty string content", () => {
1960
- const msgs = [
1961
- { role: "user", content: "Hello" },
1962
- { role: "assistant", content: "" },
1963
- { role: "user", content: "World" },
1964
- ] as any[]
1965
-
1966
- const result = ProviderTransform.message(msgs, anthropicModel, {})
1967
-
1968
- expect(result).toHaveLength(2)
1969
- expect(result[0].content).toBe("Hello")
1970
- expect(result[1].content).toBe("World")
1971
- })
1972
-
1973
- test("filters out empty text parts from array content", () => {
1974
- const msgs = [
1975
- {
1976
- role: "assistant",
1977
- content: [
1978
- { type: "text", text: "" },
1979
- { type: "text", text: "Hello" },
1980
- { type: "text", text: "" },
1981
- ],
1982
- },
1983
- ] as any[]
1984
-
1985
- const result = ProviderTransform.message(msgs, anthropicModel, {})
1986
-
1987
- expect(result).toHaveLength(1)
1988
- expect(result[0].content).toHaveLength(1)
1989
- expect(result[0].content[0]).toEqual({ type: "text", text: "Hello" })
1990
- })
1991
-
1992
- test("filters out empty reasoning parts from array content", () => {
1993
- const msgs = [
1994
- {
1995
- role: "assistant",
1996
- content: [
1997
- { type: "reasoning", text: "" },
1998
- { type: "text", text: "Answer" },
1999
- { type: "reasoning", text: "" },
2000
- ],
2001
- },
2002
- ] as any[]
2003
-
2004
- const result = ProviderTransform.message(msgs, anthropicModel, {})
2005
-
2006
- expect(result).toHaveLength(1)
2007
- expect(result[0].content).toHaveLength(1)
2008
- expect(result[0].content[0]).toEqual({ type: "text", text: "Answer" })
2009
- })
2010
-
2011
- test("removes entire message when all parts are empty", () => {
2012
- const msgs = [
2013
- { role: "user", content: "Hello" },
2014
- {
2015
- role: "assistant",
2016
- content: [
2017
- { type: "text", text: "" },
2018
- { type: "reasoning", text: "" },
2019
- ],
2020
- },
2021
- { role: "user", content: "World" },
2022
- ] as any[]
2023
-
2024
- const result = ProviderTransform.message(msgs, anthropicModel, {})
2025
-
2026
- expect(result).toHaveLength(2)
2027
- expect(result[0].content).toBe("Hello")
2028
- expect(result[1].content).toBe("World")
2029
- })
2030
-
2031
- test("keeps non-text/reasoning parts even if text parts are empty", () => {
2032
- const msgs = [
2033
- {
2034
- role: "assistant",
2035
- content: [
2036
- { type: "text", text: "" },
2037
- { type: "tool-call", toolCallId: "123", toolName: "bash", input: { command: "ls" } },
2038
- ],
2039
- },
2040
- ] as any[]
2041
-
2042
- const result = ProviderTransform.message(msgs, anthropicModel, {})
2043
-
2044
- expect(result).toHaveLength(1)
2045
- expect(result[0].content).toHaveLength(1)
2046
- expect(result[0].content[0]).toEqual({
2047
- type: "tool-call",
2048
- toolCallId: "123",
2049
- toolName: "bash",
2050
- input: { command: "ls" },
2051
- })
2052
- })
2053
-
2054
- test("keeps messages with valid text alongside empty parts", () => {
2055
- const msgs = [
2056
- {
2057
- role: "assistant",
2058
- content: [
2059
- { type: "reasoning", text: "Thinking..." },
2060
- { type: "text", text: "" },
2061
- { type: "text", text: "Result" },
2062
- ],
2063
- },
2064
- ] as any[]
2065
-
2066
- const result = ProviderTransform.message(msgs, anthropicModel, {})
2067
-
2068
- expect(result).toHaveLength(1)
2069
- expect(result[0].content).toHaveLength(2)
2070
- expect(result[0].content[0]).toEqual({ type: "reasoning", text: "Thinking..." })
2071
- expect(result[0].content[1]).toEqual({ type: "text", text: "Result" })
2072
- })
2073
-
2074
- test("filters empty content for bedrock provider", () => {
2075
- const bedrockModel = {
2076
- ...anthropicModel,
2077
- id: "amazon-bedrock/anthropic.claude-opus-4-6",
2078
- providerID: "amazon-bedrock",
2079
- api: {
2080
- id: "anthropic.claude-opus-4-6",
2081
- url: "https://bedrock-runtime.us-east-1.amazonaws.com",
2082
- npm: "@ai-sdk/amazon-bedrock",
2083
- },
2084
- }
2085
-
2086
- const msgs = [
2087
- { role: "user", content: "Hello" },
2088
- { role: "assistant", content: "" },
2089
- {
2090
- role: "assistant",
2091
- content: [
2092
- { type: "text", text: "" },
2093
- { type: "text", text: "Answer" },
2094
- ],
2095
- },
2096
- ] as any[]
2097
-
2098
- const result = ProviderTransform.message(msgs, bedrockModel, {})
2099
-
2100
- expect(result).toHaveLength(2)
2101
- expect(result[0].content).toBe("Hello")
2102
- expect(result[1].content).toHaveLength(1)
2103
- expect(result[1].content[0]).toEqual({ type: "text", text: "Answer" })
2104
- })
2105
-
2106
- test("does not filter for non-anthropic providers", () => {
2107
- const openaiModel = {
2108
- ...anthropicModel,
2109
- providerID: "openai",
2110
- api: {
2111
- id: "gpt-4",
2112
- url: "https://api.openai.com",
2113
- npm: "@ai-sdk/openai",
2114
- },
2115
- }
2116
-
2117
- const msgs = [
2118
- { role: "assistant", content: "" },
2119
- {
2120
- role: "assistant",
2121
- content: [{ type: "text", text: "" }],
2122
- },
2123
- ] as any[]
2124
-
2125
- const result = ProviderTransform.message(msgs, openaiModel, {})
2126
-
2127
- expect(result).toHaveLength(2)
2128
- expect(result[0].content).toBe("")
2129
- expect(result[1].content).toHaveLength(1)
2130
- })
2131
-
2132
- test("leaves valid anthropic assistant tool ordering unchanged", () => {
2133
- const msgs = [
2134
- {
2135
- role: "assistant",
2136
- content: [
2137
- { type: "text", text: "I checked your home directory and looked for PDF files." },
2138
- { type: "tool-call", toolCallId: "toolu_1", toolName: "read", input: { filePath: "/root" } },
2139
- { type: "tool-call", toolCallId: "toolu_2", toolName: "glob", input: { pattern: "**/*.pdf" } },
2140
- ],
2141
- },
2142
- ] as any[]
2143
-
2144
- const result = ProviderTransform.message(msgs, anthropicModel, {}) as any[]
2145
-
2146
- expect(result).toHaveLength(1)
2147
- expect(result[0].content).toMatchObject([
2148
- { type: "text", text: "I checked your home directory and looked for PDF files." },
2149
- { type: "tool-call", toolCallId: "toolu_1", toolName: "read", input: { filePath: "/root" } },
2150
- { type: "tool-call", toolCallId: "toolu_2", toolName: "glob", input: { pattern: "**/*.pdf" } },
2151
- ])
2152
- })
2153
- })
2154
-
2155
- describe("ProviderTransform.message - strip openai metadata when store=false", () => {
2156
- const openaiModel = {
2157
- id: "openai/gpt-5",
2158
- providerID: "openai",
2159
- api: {
2160
- id: "gpt-5",
2161
- url: "https://api.openai.com",
2162
- npm: "@ai-sdk/openai",
2163
- },
2164
- name: "GPT-5",
2165
- capabilities: {
2166
- temperature: true,
2167
- reasoning: true,
2168
- attachment: true,
2169
- toolcall: true,
2170
- input: { text: true, audio: false, image: true, video: false, pdf: false },
2171
- output: { text: true, audio: false, image: false, video: false, pdf: false },
2172
- interleaved: false,
2173
- },
2174
- cost: { input: 0.03, output: 0.06, cache: { read: 0.001, write: 0.002 } },
2175
- limit: { context: 128000, output: 4096 },
2176
- status: "active",
2177
- options: {},
2178
- headers: {},
2179
- } as any
2180
-
2181
- test("strips OpenAI itemId and preserves reasoningEncryptedContent when store=false", () => {
2182
- const msgs = [
2183
- {
2184
- role: "assistant",
2185
- content: [
2186
- {
2187
- type: "reasoning",
2188
- text: "thinking...",
2189
- providerOptions: {
2190
- openai: {
2191
- itemId: "rs_123",
2192
- reasoningEncryptedContent: "encrypted",
2193
- },
2194
- },
2195
- },
2196
- {
2197
- type: "text",
2198
- text: "Hello",
2199
- providerOptions: {
2200
- openai: {
2201
- itemId: "msg_456",
2202
- },
2203
- },
2204
- },
2205
- ],
2206
- },
2207
- ] as any[]
2208
-
2209
- const result = ProviderTransform.message(msgs, openaiModel, { store: false }) as any[]
2210
-
2211
- expect(result).toHaveLength(1)
2212
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined()
2213
- expect(result[0].content[0].providerOptions?.openai?.reasoningEncryptedContent).toBe("encrypted")
2214
- expect(result[0].content[1].providerOptions?.openai?.itemId).toBeUndefined()
2215
- })
2216
-
2217
- test("uses the SDK package namespace rather than provider ID", () => {
2218
- const zenModel = {
2219
- ...openaiModel,
2220
- providerID: "zen",
2221
- }
2222
- const msgs = [
2223
- {
2224
- role: "assistant",
2225
- content: [
2226
- {
2227
- type: "reasoning",
2228
- text: "thinking...",
2229
- providerOptions: {
2230
- openai: {
2231
- itemId: "rs_123",
2232
- reasoningEncryptedContent: "encrypted",
2233
- },
2234
- },
2235
- },
2236
- {
2237
- type: "text",
2238
- text: "Hello",
2239
- providerOptions: {
2240
- openai: {
2241
- itemId: "msg_456",
2242
- },
2243
- },
2244
- },
2245
- ],
2246
- },
2247
- ] as any[]
2248
-
2249
- const result = ProviderTransform.message(msgs, zenModel, { store: false }) as any[]
2250
-
2251
- expect(result).toHaveLength(1)
2252
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined()
2253
- expect(result[0].content[0].providerOptions?.openai?.reasoningEncryptedContent).toBe("encrypted")
2254
- expect(result[0].content[1].providerOptions?.openai?.itemId).toBeUndefined()
2255
- })
2256
-
2257
- test("preserves other OpenAI options", () => {
2258
- const msgs = [
2259
- {
2260
- role: "assistant",
2261
- content: [
2262
- {
2263
- type: "text",
2264
- text: "Hello",
2265
- providerOptions: {
2266
- openai: {
2267
- itemId: "msg_123",
2268
- otherOption: "value",
2269
- },
2270
- },
2271
- },
2272
- ],
2273
- },
2274
- ] as any[]
2275
-
2276
- const result = ProviderTransform.message(msgs, openaiModel, { store: false }) as any[]
2277
-
2278
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined()
2279
- expect(result[0].content[0].providerOptions?.openai?.otherOption).toBe("value")
2280
- })
2281
-
2282
- test("strips Azure itemId from the Azure namespace", () => {
2283
- const azureModel = {
2284
- ...openaiModel,
2285
- providerID: "azure",
2286
- api: {
2287
- id: "gpt-5",
2288
- url: "https://example.openai.azure.com",
2289
- npm: "@ai-sdk/azure",
2290
- },
2291
- }
2292
- const msgs = [
2293
- {
2294
- role: "assistant",
2295
- content: [
2296
- {
2297
- type: "text",
2298
- text: "Hello",
2299
- providerOptions: {
2300
- azure: { itemId: "msg_123", otherOption: "value" },
2301
- openai: { itemId: "msg_openai" },
2302
- },
2303
- },
2304
- ],
2305
- },
2306
- ] as any[]
2307
-
2308
- const result = ProviderTransform.message(msgs, azureModel, { store: false }) as any[]
2309
-
2310
- expect(result[0].content[0].providerOptions?.azure?.itemId).toBeUndefined()
2311
- expect(result[0].content[0].providerOptions?.azure?.otherOption).toBe("value")
2312
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_openai")
2313
- })
2314
-
2315
- test("strips Bedrock Mantle itemId from the OpenAI namespace", () => {
2316
- const mantleModel = {
2317
- ...openaiModel,
2318
- providerID: "amazon-bedrock",
2319
- api: {
2320
- id: "openai.gpt-5.5",
2321
- url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
2322
- npm: "@ai-sdk/amazon-bedrock/mantle",
2323
- },
2324
- }
2325
- const msgs = [
2326
- {
2327
- role: "assistant",
2328
- providerOptions: { openai: { itemId: "msg_root", otherOption: "root-value" } },
2329
- content: [
2330
- {
2331
- type: "reasoning",
2332
- text: "thinking...",
2333
- providerOptions: {
2334
- openai: { itemId: "rs_123", reasoningEncryptedContent: "encrypted" },
2335
- },
2336
- },
2337
- ],
2338
- },
2339
- ] as any[]
2340
-
2341
- const result = ProviderTransform.message(msgs, mantleModel, { store: false }) as any[]
2342
-
2343
- expect(result[0].providerOptions?.openai?.itemId).toBeUndefined()
2344
- expect(result[0].providerOptions?.openai?.otherOption).toBe("root-value")
2345
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBeUndefined()
2346
- expect(result[0].content[0].providerOptions?.openai?.reasoningEncryptedContent).toBe("encrypted")
2347
- })
2348
-
2349
- test("strips GitHub Copilot itemId from the copilot namespace, preserving other copilot options", () => {
2350
- const copilotModel = {
2351
- ...openaiModel,
2352
- id: "github-copilot/gpt-5.5",
2353
- providerID: "github-copilot",
2354
- api: {
2355
- id: "gpt-5.5",
2356
- url: "https://api.githubcopilot.com",
2357
- npm: "@ai-sdk/github-copilot",
2358
- },
2359
- }
2360
- const msgs = [
2361
- {
2362
- role: "assistant",
2363
- content: [
2364
- {
2365
- type: "reasoning",
2366
- text: "thinking...",
2367
- providerOptions: {
2368
- copilot: { itemId: "rs_123", reasoningEncryptedContent: "encrypted" },
2369
- },
2370
- },
2371
- {
2372
- // The stale itemId on tool-call parts is what Copilot echoes back as the
2373
- // `function_call` item `id`, which is what the upstream connection rejects.
2374
- type: "tool-call",
2375
- toolCallId: "call_1",
2376
- toolName: "bash",
2377
- input: { command: "ls" },
2378
- providerOptions: {
2379
- copilot: { itemId: "fc_456", reasoningEffort: "medium" },
2380
- },
2381
- },
2382
- ],
2383
- },
2384
- ] as any[]
2385
-
2386
- const result = ProviderTransform.message(msgs, copilotModel, { store: false }) as any[]
2387
-
2388
- expect(result[0].content[0].providerOptions?.copilot?.itemId).toBeUndefined()
2389
- expect(result[0].content[0].providerOptions?.copilot?.reasoningEncryptedContent).toBe("encrypted")
2390
- expect(result[0].content[1].providerOptions?.copilot?.itemId).toBeUndefined()
2391
- expect(result[0].content[1].providerOptions?.copilot?.reasoningEffort).toBe("medium")
2392
- })
2393
-
2394
- test("leaves a stray openai namespace on a Copilot model untouched, since Copilot's Responses model only reads the copilot namespace", () => {
2395
- const copilotModel = {
2396
- ...openaiModel,
2397
- id: "github-copilot/gpt-5.5",
2398
- providerID: "github-copilot",
2399
- api: {
2400
- id: "gpt-5.5",
2401
- url: "https://api.githubcopilot.com",
2402
- npm: "@ai-sdk/github-copilot",
2403
- },
2404
- }
2405
- const msgs = [
2406
- {
2407
- role: "assistant",
2408
- content: [
2409
- {
2410
- type: "text",
2411
- text: "Hello",
2412
- providerOptions: {
2413
- openai: { itemId: "msg_456" },
2414
- },
2415
- },
2416
- ],
2417
- },
2418
- ] as any[]
2419
-
2420
- const result = ProviderTransform.message(msgs, copilotModel, { store: false }) as any[]
2421
-
2422
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_456")
2423
- })
2424
-
2425
- test("preserves metadata for openai package when store is true", () => {
2426
- const msgs = [
2427
- {
2428
- role: "assistant",
2429
- content: [
2430
- {
2431
- type: "text",
2432
- text: "Hello",
2433
- providerOptions: {
2434
- openai: {
2435
- itemId: "msg_123",
2436
- },
2437
- },
2438
- },
2439
- ],
2440
- },
2441
- ] as any[]
2442
-
2443
- // openai package preserves itemId regardless of store value
2444
- const result = ProviderTransform.message(msgs, openaiModel, { store: true }) as any[]
2445
-
2446
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_123")
2447
- })
2448
-
2449
- test("preserves metadata for non-openai packages when store is false", () => {
2450
- const anthropicModel = {
2451
- ...openaiModel,
2452
- providerID: "anthropic",
2453
- api: {
2454
- id: "claude-3",
2455
- url: "https://api.anthropic.com",
2456
- npm: "@ai-sdk/anthropic",
2457
- },
2458
- }
2459
- const msgs = [
2460
- {
2461
- role: "assistant",
2462
- content: [
2463
- {
2464
- type: "text",
2465
- text: "Hello",
2466
- providerOptions: {
2467
- openai: {
2468
- itemId: "msg_123",
2469
- },
2470
- },
2471
- },
2472
- ],
2473
- },
2474
- ] as any[]
2475
-
2476
- // store=false preserves metadata for non-openai packages
2477
- const result = ProviderTransform.message(msgs, anthropicModel, { store: false }) as any[]
2478
-
2479
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_123")
2480
- })
2481
-
2482
- test("preserves metadata using providerID key when store is false", () => {
2483
- const acModel = {
2484
- ...openaiModel,
2485
- providerID: "opencode",
2486
- api: {
2487
- id: "opencode-test",
2488
- url: "https://api.opencode.ai",
2489
- npm: "@ai-sdk/openai-compatible",
2490
- },
2491
- }
2492
- const msgs = [
2493
- {
2494
- role: "assistant",
2495
- content: [
2496
- {
2497
- type: "text",
2498
- text: "Hello",
2499
- providerOptions: {
2500
- opencode: {
2501
- itemId: "msg_123",
2502
- otherOption: "value",
2503
- },
2504
- },
2505
- },
2506
- ],
2507
- },
2508
- ] as any[]
2509
-
2510
- const result = ProviderTransform.message(msgs, acModel, { store: false }) as any[]
2511
-
2512
- expect(result[0].content[0].providerOptions?.opencode?.itemId).toBe("msg_123")
2513
- expect(result[0].content[0].providerOptions?.opencode?.otherOption).toBe("value")
2514
- })
2515
-
2516
- test("preserves itemId across all providerOptions keys", () => {
2517
- const acModel = {
2518
- ...openaiModel,
2519
- providerID: "opencode",
2520
- api: {
2521
- id: "opencode-test",
2522
- url: "https://api.opencode.ai",
2523
- npm: "@ai-sdk/openai-compatible",
2524
- },
2525
- }
2526
- const msgs = [
2527
- {
2528
- role: "assistant",
2529
- providerOptions: {
2530
- openai: { itemId: "msg_root" },
2531
- opencode: { itemId: "msg_opencode" },
2532
- extra: { itemId: "msg_extra" },
2533
- },
2534
- content: [
2535
- {
2536
- type: "text",
2537
- text: "Hello",
2538
- providerOptions: {
2539
- openai: { itemId: "msg_openai_part" },
2540
- opencode: { itemId: "msg_opencode_part" },
2541
- extra: { itemId: "msg_extra_part" },
2542
- },
2543
- },
2544
- ],
2545
- },
2546
- ] as any[]
2547
-
2548
- const result = ProviderTransform.message(msgs, acModel, { store: false }) as any[]
2549
-
2550
- expect(result[0].providerOptions?.openai?.itemId).toBe("msg_root")
2551
- expect(result[0].providerOptions?.opencode?.itemId).toBe("msg_opencode")
2552
- expect(result[0].providerOptions?.extra?.itemId).toBe("msg_extra")
2553
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_openai_part")
2554
- expect(result[0].content[0].providerOptions?.opencode?.itemId).toBe("msg_opencode_part")
2555
- expect(result[0].content[0].providerOptions?.extra?.itemId).toBe("msg_extra_part")
2556
- })
2557
-
2558
- test("does not strip metadata for non-openai packages when store is not false", () => {
2559
- const anthropicModel = {
2560
- ...openaiModel,
2561
- providerID: "anthropic",
2562
- api: {
2563
- id: "claude-3",
2564
- url: "https://api.anthropic.com",
2565
- npm: "@ai-sdk/anthropic",
2566
- },
2567
- }
2568
- const msgs = [
2569
- {
2570
- role: "assistant",
2571
- content: [
2572
- {
2573
- type: "text",
2574
- text: "Hello",
2575
- providerOptions: {
2576
- openai: {
2577
- itemId: "msg_123",
2578
- },
2579
- },
2580
- },
2581
- ],
2582
- },
2583
- ] as any[]
2584
-
2585
- const result = ProviderTransform.message(msgs, anthropicModel, {}) as any[]
2586
-
2587
- expect(result[0].content[0].providerOptions?.openai?.itemId).toBe("msg_123")
2588
- })
2589
- })
2590
-
2591
- describe("ProviderTransform.message - providerOptions key remapping", () => {
2592
- const createModel = (providerID: string, npm: string) =>
2593
- ({
2594
- id: `${providerID}/test-model`,
2595
- providerID,
2596
- api: {
2597
- id: "test-model",
2598
- url: "https://api.test.com",
2599
- npm,
2600
- },
2601
- name: "Test Model",
2602
- capabilities: {
2603
- temperature: true,
2604
- reasoning: false,
2605
- attachment: true,
2606
- toolcall: true,
2607
- input: { text: true, audio: false, image: true, video: false, pdf: true },
2608
- output: { text: true, audio: false, image: false, video: false, pdf: false },
2609
- interleaved: false,
2610
- },
2611
- cost: { input: 0.001, output: 0.002, cache: { read: 0.0001, write: 0.0002 } },
2612
- limit: { context: 128000, output: 8192 },
2613
- status: "active",
2614
- options: {},
2615
- headers: {},
2616
- }) as any
2617
-
2618
- test("azure keeps 'azure' key and does not remap to 'openai'", () => {
2619
- const model = createModel("azure", "@ai-sdk/azure")
2620
- const msgs = [
2621
- {
2622
- role: "user",
2623
- content: "Hello",
2624
- providerOptions: {
2625
- azure: { someOption: "value" },
2626
- },
2627
- },
2628
- ] as any[]
2629
-
2630
- const result = ProviderTransform.message(msgs, model, {})
2631
-
2632
- expect(result[0].providerOptions?.azure).toEqual({ someOption: "value" })
2633
- expect(result[0].providerOptions?.openai).toBeUndefined()
2634
- })
2635
-
2636
- test("azure cognitive services remaps providerID to 'azure' key", () => {
2637
- const model = createModel("azure-cognitive-services", "@ai-sdk/azure")
2638
- const msgs = [
2639
- {
2640
- role: "user",
2641
- content: [
2642
- {
2643
- type: "text",
2644
- text: "Hello",
2645
- providerOptions: {
2646
- "azure-cognitive-services": { part: true },
2647
- },
2648
- },
2649
- ],
2650
- providerOptions: {
2651
- "azure-cognitive-services": { someOption: "value" },
2652
- },
2653
- },
2654
- ] as any[]
2655
-
2656
- const result = ProviderTransform.message(msgs, model, {}) as any[]
2657
- const part = result[0].content[0] as any
2658
-
2659
- expect(result[0].providerOptions?.azure).toEqual({ someOption: "value" })
2660
- expect(result[0].providerOptions?.["azure-cognitive-services"]).toBeUndefined()
2661
- expect(part.providerOptions?.azure).toEqual({ part: true })
2662
- expect(part.providerOptions?.["azure-cognitive-services"]).toBeUndefined()
2663
- })
2664
-
2665
- test("copilot remaps providerID to 'copilot' key", () => {
2666
- const model = createModel("github-copilot", "@ai-sdk/github-copilot")
2667
- const msgs = [
2668
- {
2669
- role: "user",
2670
- content: "Hello",
2671
- providerOptions: {
2672
- copilot: { someOption: "value" },
2673
- },
2674
- },
2675
- ] as any[]
2676
-
2677
- const result = ProviderTransform.message(msgs, model, {})
2678
-
2679
- expect(result[0].providerOptions?.copilot).toEqual({ someOption: "value" })
2680
- expect(result[0].providerOptions?.["github-copilot"]).toBeUndefined()
2681
- })
2682
-
2683
- test("bedrock remaps providerID to 'bedrock' key", () => {
2684
- const model = createModel("my-bedrock", "@ai-sdk/amazon-bedrock")
2685
- const msgs = [
2686
- {
2687
- role: "user",
2688
- content: "Hello",
2689
- providerOptions: {
2690
- "my-bedrock": { someOption: "value" },
2691
- },
2692
- },
2693
- ] as any[]
2694
-
2695
- const result = ProviderTransform.message(msgs, model, {})
2696
-
2697
- expect(result[0].providerOptions?.bedrock).toEqual({ someOption: "value" })
2698
- expect(result[0].providerOptions?.["my-bedrock"]).toBeUndefined()
2699
- })
2700
- })
2701
-
2702
- describe("ProviderTransform.message - claude w/bedrock custom inference profile", () => {
2703
- test("adds cachePoint", () => {
2704
- const model = {
2705
- id: "amazon-bedrock/custom-claude-sonnet-4.5",
2706
- providerID: "amazon-bedrock",
2707
- api: {
2708
- id: "arn:aws:bedrock:xxx:yyy:application-inference-profile/zzz",
2709
- url: "https://api.test.com",
2710
- npm: "@ai-sdk/amazon-bedrock",
2711
- },
2712
- name: "Custom inference profile",
2713
- capabilities: {},
2714
- options: {},
2715
- headers: {},
2716
- } as any
2717
-
2718
- const msgs = [
2719
- {
2720
- role: "user",
2721
- content: "Hello",
2722
- },
2723
- ] as any[]
2724
-
2725
- const result = ProviderTransform.message(msgs, model, {})
2726
-
2727
- expect(result[0].providerOptions?.bedrock).toEqual(
2728
- expect.objectContaining({
2729
- cachePoint: {
2730
- type: "default",
2731
- },
2732
- }),
2733
- )
2734
- })
2735
- })
2736
-
2737
- describe("ProviderTransform.message - bedrock caching with non-bedrock providerID", () => {
2738
- test("applies cache options at message level when npm package is amazon-bedrock", () => {
2739
- const model = {
2740
- id: "aws/us.anthropic.claude-opus-4-6-v1",
2741
- providerID: "aws",
2742
- api: {
2743
- id: "us.anthropic.claude-opus-4-6-v1",
2744
- url: "https://bedrock-runtime.us-east-1.amazonaws.com",
2745
- npm: "@ai-sdk/amazon-bedrock",
2746
- },
2747
- name: "Claude Opus 4.6",
2748
- capabilities: {},
2749
- options: {},
2750
- headers: {},
2751
- } as any
2752
-
2753
- const msgs = [
2754
- {
2755
- role: "system",
2756
- content: "You are a helpful assistant",
2757
- },
2758
- {
2759
- role: "user",
2760
- content: [{ type: "text", text: "Hello" }],
2761
- },
2762
- ] as any[]
2763
-
2764
- const result = ProviderTransform.message(msgs, model, {}) as any[]
2765
-
2766
- // Cache should be at the message level and not the content-part level
2767
- expect(result[0].providerOptions?.bedrock).toEqual({
2768
- cachePoint: { type: "default" },
2769
- })
2770
- expect(result[0].content).toBe("You are a helpful assistant")
2771
- })
2772
- })
2773
-
2774
- describe("ProviderTransform.message - cache control on gateway", () => {
2775
- const createModel = (overrides: Partial<any> = {}) =>
2776
- ({
2777
- id: "anthropic/claude-sonnet-4",
2778
- providerID: "vercel",
2779
- api: {
2780
- id: "anthropic/claude-sonnet-4",
2781
- url: "https://ai-gateway.vercel.sh/v3/ai",
2782
- npm: "@ai-sdk/gateway",
2783
- },
2784
- name: "Claude Sonnet 4",
2785
- capabilities: {
2786
- temperature: true,
2787
- reasoning: true,
2788
- attachment: true,
2789
- toolcall: true,
2790
- input: { text: true, audio: false, image: true, video: false, pdf: true },
2791
- output: { text: true, audio: false, image: false, video: false, pdf: false },
2792
- interleaved: false,
2793
- },
2794
- cost: { input: 0.001, output: 0.002, cache: { read: 0.0001, write: 0.0002 } },
2795
- limit: { context: 200_000, output: 8192 },
2796
- status: "active",
2797
- options: {},
2798
- headers: {},
2799
- ...overrides,
2800
- }) as any
2801
-
2802
- test("gateway does not set cache control for anthropic models", () => {
2803
- const model = createModel()
2804
- const msgs = [
2805
- {
2806
- role: "system",
2807
- content: "You are a helpful assistant",
2808
- },
2809
- {
2810
- role: "user",
2811
- content: "Hello",
2812
- },
2813
- ] as any[]
2814
-
2815
- const result = ProviderTransform.message(msgs, model, {}) as any[]
2816
-
2817
- expect(result[0].content).toBe("You are a helpful assistant")
2818
- expect(result[0].providerOptions).toBeUndefined()
2819
- })
2820
-
2821
- test("non-gateway anthropic keeps existing cache control behavior", () => {
2822
- const model = createModel({
2823
- providerID: "anthropic",
2824
- api: {
2825
- id: "claude-sonnet-4",
2826
- url: "https://api.anthropic.com",
2827
- npm: "@ai-sdk/anthropic",
2828
- },
2829
- })
2830
- const msgs = [
2831
- {
2832
- role: "system",
2833
- content: "You are a helpful assistant",
2834
- },
2835
- {
2836
- role: "user",
2837
- content: "Hello",
2838
- },
2839
- ] as any[]
2840
-
2841
- const result = ProviderTransform.message(msgs, model, {}) as any[]
2842
-
2843
- expect(result[0].providerOptions).toEqual({
2844
- anthropic: {
2845
- cacheControl: {
2846
- type: "ephemeral",
2847
- },
2848
- },
2849
- openrouter: {
2850
- cacheControl: {
2851
- type: "ephemeral",
2852
- },
2853
- },
2854
- bedrock: {
2855
- cachePoint: {
2856
- type: "default",
2857
- },
2858
- },
2859
- openaiCompatible: {
2860
- cache_control: {
2861
- type: "ephemeral",
2862
- },
2863
- },
2864
- copilot: {
2865
- copilot_cache_control: {
2866
- type: "ephemeral",
2867
- },
2868
- },
2869
- alibaba: {
2870
- cacheControl: {
2871
- type: "ephemeral",
2872
- },
2873
- },
2874
- })
2875
- })
2876
-
2877
- test("google-vertex-anthropic applies cache control", () => {
2878
- const model = createModel({
2879
- providerID: "google-vertex-anthropic",
2880
- api: {
2881
- id: "google-vertex-anthropic",
2882
- url: "https://us-central1-aiplatform.googleapis.com",
2883
- npm: "@ai-sdk/google-vertex/anthropic",
2884
- },
2885
- id: "claude-sonnet-4@20250514",
2886
- })
2887
- const msgs = [
2888
- {
2889
- role: "system",
2890
- content: "You are a helpful assistant",
2891
- },
2892
- {
2893
- role: "user",
2894
- content: "Hello",
2895
- },
2896
- ] as any[]
2897
-
2898
- const result = ProviderTransform.message(msgs, model, {}) as any[]
2899
-
2900
- expect(result[0].providerOptions).toEqual({
2901
- anthropic: {
2902
- cacheControl: {
2903
- type: "ephemeral",
2904
- },
2905
- },
2906
- openrouter: {
2907
- cacheControl: {
2908
- type: "ephemeral",
2909
- },
2910
- },
2911
- bedrock: {
2912
- cachePoint: {
2913
- type: "default",
2914
- },
2915
- },
2916
- openaiCompatible: {
2917
- cache_control: {
2918
- type: "ephemeral",
2919
- },
2920
- },
2921
- copilot: {
2922
- copilot_cache_control: {
2923
- type: "ephemeral",
2924
- },
2925
- },
2926
- alibaba: {
2927
- cacheControl: {
2928
- type: "ephemeral",
2929
- },
2930
- },
2931
- })
2932
- })
2933
- })
2934
-
2935
- describe("ProviderTransform.temperature - Cohere North", () => {
2936
- test("defaults north-mini-code models to 1.0", () => {
2937
- expect(ProviderTransform.temperature({ id: "cohere/North-Mini-Code-1-0-latest" } as any)).toBe(1.0)
2938
- })
2939
- })
2940
-
2941
- describe("ProviderTransform.variants", () => {
2942
- const createMockModel = (overrides: Partial<any> = {}): any => ({
2943
- id: "test/test-model",
2944
- providerID: "test",
2945
- api: {
2946
- id: "test-model",
2947
- url: "https://api.test.com",
2948
- npm: "@ai-sdk/openai",
2949
- },
2950
- name: "Test Model",
2951
- capabilities: {
2952
- temperature: true,
2953
- reasoning: true,
2954
- attachment: true,
2955
- toolcall: true,
2956
- input: { text: true, audio: false, image: true, video: false, pdf: false },
2957
- output: { text: true, audio: false, image: false, video: false, pdf: false },
2958
- interleaved: false,
2959
- },
2960
- cost: {
2961
- input: 0.001,
2962
- output: 0.002,
2963
- cache: { read: 0.0001, write: 0.0002 },
2964
- },
2965
- limit: {
2966
- context: 200_000,
2967
- output: 64_000,
2968
- },
2969
- status: "active",
2970
- options: {},
2971
- headers: {},
2972
- release_date: "2024-01-01",
2973
- ...overrides,
2974
- })
2975
-
2976
- test("returns empty object when model has no reasoning capabilities", () => {
2977
- const model = createMockModel({
2978
- capabilities: { reasoning: false },
2979
- })
2980
- const result = ProviderTransform.variants(model)
2981
- expect(result).toEqual({})
2982
- })
2983
-
2984
- test("deepseek returns empty object", () => {
2985
- const model = createMockModel({
2986
- id: "deepseek/deepseek-chat",
2987
- providerID: "deepseek",
2988
- api: {
2989
- id: "deepseek-chat",
2990
- url: "https://api.deepseek.com",
2991
- npm: "@ai-sdk/openai-compatible",
2992
- },
2993
- })
2994
- const result = ProviderTransform.variants(model)
2995
- expect(result).toEqual({})
2996
- })
2997
-
2998
- test("minimax returns empty object", () => {
2999
- const model = createMockModel({
3000
- id: "minimax/minimax-model",
3001
- providerID: "minimax",
3002
- api: {
3003
- id: "minimax-model",
3004
- url: "https://api.minimax.com",
3005
- npm: "@ai-sdk/openai-compatible",
3006
- },
3007
- })
3008
- const result = ProviderTransform.variants(model)
3009
- expect(result).toEqual({})
3010
- })
3011
-
3012
- test("minimax m3 using anthropic returns thinking toggles", () => {
3013
- const model = createMockModel({
3014
- id: "minimax/minimax-m3",
3015
- providerID: "minimax",
3016
- api: {
3017
- id: "MiniMax-M3",
3018
- url: "https://api.minimax.com/anthropic/v1",
3019
- npm: "@ai-sdk/anthropic",
3020
- },
3021
- })
3022
- const result = ProviderTransform.variants(model)
3023
- expect(result).toEqual({
3024
- none: { thinking: { type: "disabled" } },
3025
- thinking: { thinking: { type: "adaptive" } },
3026
- })
3027
- })
3028
-
3029
- test("minimax m3 using openai-compatible returns thinking toggles", () => {
3030
- const model = createMockModel({
3031
- id: "minimax/minimax-m3",
3032
- providerID: "minimax",
3033
- api: {
3034
- id: "minimax-m3",
3035
- url: "https://api.minimax.com/v1",
3036
- npm: "@ai-sdk/openai-compatible",
3037
- },
3038
- })
3039
- expect(ProviderTransform.variants(model)).toEqual({
3040
- none: { thinking: { type: "disabled" } },
3041
- thinking: { thinking: { type: "adaptive" } },
3042
- })
3043
- })
3044
-
3045
- test("glm returns empty object", () => {
3046
- const model = createMockModel({
3047
- id: "glm/glm-4",
3048
- providerID: "glm",
3049
- api: {
3050
- id: "glm-4",
3051
- url: "https://api.glm.com",
3052
- npm: "@ai-sdk/openai-compatible",
3053
- },
3054
- })
3055
- const result = ProviderTransform.variants(model)
3056
- expect(result).toEqual({})
3057
- })
3058
-
3059
- test("glm-5.2 returns native effort variants for openai-compatible providers", () => {
3060
- const model = createMockModel({
3061
- id: "zhipuai/glm-5.2",
3062
- providerID: "zhipuai",
3063
- api: {
3064
- id: "glm-5.2",
3065
- url: "https://open.bigmodel.cn/api/paas/v4",
3066
- npm: "@ai-sdk/openai-compatible",
3067
- },
3068
- })
3069
- expect(ProviderTransform.variants(model)).toEqual({
3070
- high: { reasoningEffort: "high" },
3071
- max: { reasoningEffort: "max" },
3072
- })
3073
- })
3074
-
3075
- test("recognizes GLM-5.2 provider model IDs", () => {
3076
- for (const id of ["accounts/fireworks/models/glm-5p2", "zai-org-glm-5-2", "umans-glm-5.2"]) {
3077
- const model = createMockModel({
3078
- id: `test/${id}`,
3079
- api: {
3080
- id,
3081
- url: "https://api.test.com",
3082
- npm: "@ai-sdk/openai-compatible",
3083
- },
3084
- })
3085
- expect(ProviderTransform.variants(model)).toEqual({
3086
- high: { reasoningEffort: "high" },
3087
- max: { reasoningEffort: "max" },
3088
- })
3089
- }
3090
- })
3091
-
3092
- test("recognizes GLM-5.2 from the API ID when the configured model ID is an alias", () => {
3093
- const model = createMockModel({
3094
- id: "custom/my-glm",
3095
- api: {
3096
- id: "accounts/fireworks/models/glm-5p2",
3097
- url: "https://api.fireworks.ai/inference/v1",
3098
- npm: "@ai-sdk/openai-compatible",
3099
- },
3100
- })
3101
- expect(ProviderTransform.variants(model)).toEqual({
3102
- high: { reasoningEffort: "high" },
3103
- max: { reasoningEffort: "max" },
3104
- })
3105
- })
3106
-
3107
- test("glm-5.2 returns openrouter effort variants for openrouter", () => {
3108
- const model = createMockModel({
3109
- id: "openrouter/z-ai/glm-5.2",
3110
- providerID: "openrouter",
3111
- api: {
3112
- id: "z-ai/glm-5.2",
3113
- url: "https://openrouter.ai/api/v1",
3114
- npm: "@openrouter/ai-sdk-provider",
3115
- },
3116
- })
3117
- expect(ProviderTransform.variants(model)).toEqual({
3118
- high: { reasoning: { effort: "high" } },
3119
- xhigh: { reasoning: { effort: "xhigh" } },
3120
- })
3121
- })
3122
-
3123
- test("glm-5.2 returns effort variants for anthropic-compatible providers", () => {
3124
- const model = createMockModel({
3125
- id: "zai-coding-plan/glm-5.2",
3126
- providerID: "zai-coding-plan",
3127
- api: {
3128
- id: "glm-5.2",
3129
- url: "https://api.z.ai/api/anthropic",
3130
- npm: "@ai-sdk/anthropic",
3131
- },
3132
- })
3133
- expect(ProviderTransform.variants(model)).toEqual({
3134
- high: { effort: "high" },
3135
- max: { effort: "max" },
3136
- })
3137
- })
3138
-
3139
- test("glm-5.2 falls back to provider defaults for other packages", () => {
3140
- const model = createMockModel({
3141
- id: "test/glm-5.2",
3142
- api: {
3143
- id: "glm-5.2",
3144
- url: "https://api.test.com",
3145
- npm: "@ai-sdk/amazon-bedrock",
3146
- },
3147
- })
3148
- expect(ProviderTransform.variants(model)).toEqual({
3149
- low: { reasoningConfig: { type: "enabled", maxReasoningEffort: "low" } },
3150
- medium: { reasoningConfig: { type: "enabled", maxReasoningEffort: "medium" } },
3151
- high: { reasoningConfig: { type: "enabled", maxReasoningEffort: "high" } },
3152
- })
3153
- })
3154
-
3155
- test("mistral models with reasoning support return variants", () => {
3156
- const model = createMockModel({
3157
- id: "mistral/mistral-small-latest",
3158
- providerID: "mistral",
3159
- api: {
3160
- id: "mistral-small-latest",
3161
- url: "https://api.mistral.com",
3162
- npm: "@ai-sdk/mistral",
3163
- },
3164
- capabilities: { reasoning: true },
3165
- })
3166
- const result = ProviderTransform.variants(model)
3167
- expect(result).toEqual({
3168
- high: { reasoningEffort: "high" },
3169
- })
3170
- })
3171
-
3172
- test("mistral-medium-3.5 with reasoning returns variants", () => {
3173
- const model = createMockModel({
3174
- id: "mistral/mistral-medium-3.5",
3175
- providerID: "mistral",
3176
- api: {
3177
- id: "mistral-medium-3.5",
3178
- url: "https://api.mistral.com",
3179
- npm: "@ai-sdk/mistral",
3180
- },
3181
- capabilities: { reasoning: true },
3182
- })
3183
- const result = ProviderTransform.variants(model)
3184
- expect(result).toEqual({
3185
- high: { reasoningEffort: "high" },
3186
- })
3187
- })
3188
-
3189
- test("mistral without reasoning returns empty object", () => {
3190
- const model = createMockModel({
3191
- id: "mistral/mistral-large",
3192
- providerID: "mistral",
3193
- api: {
3194
- id: "mistral-large-latest",
3195
- url: "https://api.mistral.com",
3196
- npm: "@ai-sdk/mistral",
3197
- },
3198
- capabilities: { reasoning: false },
3199
- })
3200
- const result = ProviderTransform.variants(model)
3201
- expect(result).toEqual({})
3202
- })
3203
-
3204
- test("mistral large with reasoning returns empty object (only small supports reasoning)", () => {
3205
- const model = createMockModel({
3206
- id: "mistral/mistral-large",
3207
- providerID: "mistral",
3208
- api: {
3209
- id: "mistral-large-latest",
3210
- url: "https://api.mistral.com",
3211
- npm: "@ai-sdk/mistral",
3212
- },
3213
- capabilities: { reasoning: true },
3214
- })
3215
- const result = ProviderTransform.variants(model)
3216
- expect(result).toEqual({})
3217
- })
3218
-
3219
- describe("@openrouter/ai-sdk-provider", () => {
3220
- test("returns widely supported efforts for other reasoning models", () => {
3221
- const model = createMockModel({
3222
- id: "openrouter/test-model",
3223
- providerID: "openrouter",
3224
- api: {
3225
- id: "test-model",
3226
- url: "https://openrouter.ai",
3227
- npm: "@openrouter/ai-sdk-provider",
3228
- },
3229
- })
3230
- const result = ProviderTransform.variants(model)
3231
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3232
- expect(result.medium).toEqual({ reasoning: { effort: "medium" } })
3233
- })
3234
-
3235
- test("gpt models return OPENAI_EFFORTS with reasoning", () => {
3236
- const model = createMockModel({
3237
- id: "openrouter/gpt-4",
3238
- providerID: "openrouter",
3239
- api: {
3240
- id: "gpt-4",
3241
- url: "https://openrouter.ai",
3242
- npm: "@openrouter/ai-sdk-provider",
3243
- },
3244
- })
3245
- const result = ProviderTransform.variants(model)
3246
- expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"])
3247
- expect(result.low).toEqual({ reasoning: { effort: "low" } })
3248
- expect(result.high).toEqual({ reasoning: { effort: "high" } })
3249
- })
3250
-
3251
- for (const testCase of [
3252
- { id: "openai/o3-mini", efforts: ["none", "minimal", "low", "medium", "high", "xhigh"] },
3253
- { id: "openai/gpt-5.4", efforts: ["none", "low", "medium", "high", "xhigh"] },
3254
- { id: "openai/gpt-5-pro", efforts: ["high"] },
3255
- { id: "openai/gpt-5.5-pro", efforts: ["medium", "high", "xhigh"] },
3256
- { id: "openai/gpt-5.2-codex", efforts: ["low", "medium", "high", "xhigh"] },
3257
- { id: "openai/gpt-5.3-codex", efforts: ["none", "low", "medium", "high", "xhigh"] },
3258
- { id: "openai/gpt-5.3-codex-max", efforts: ["none", "low", "medium", "high", "xhigh"] },
3259
- { id: "openai/gpt-5-chat-latest", efforts: [] },
3260
- { id: "openai/gpt-5.2-chat-latest", efforts: ["medium"] },
3261
- ]) {
3262
- test(`${testCase.id} returns supported OpenAI reasoning efforts`, () => {
3263
- const result = ProviderTransform.variants(
3264
- createMockModel({
3265
- id: testCase.id,
3266
- providerID: "openrouter",
3267
- api: {
3268
- id: testCase.id,
3269
- url: "https://openrouter.ai",
3270
- npm: "@openrouter/ai-sdk-provider",
3271
- },
3272
- }),
3273
- )
3274
- expect(Object.keys(result)).toEqual(testCase.efforts)
3275
- })
3276
- }
3277
-
3278
- test("gemini-3 returns widely supported efforts with reasoning", () => {
3279
- const model = createMockModel({
3280
- id: "openrouter/gemini-3-5-pro",
3281
- providerID: "openrouter",
3282
- api: {
3283
- id: "gemini-3-5-pro",
3284
- url: "https://openrouter.ai",
3285
- npm: "@openrouter/ai-sdk-provider",
3286
- },
3287
- })
3288
- const result = ProviderTransform.variants(model)
3289
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3290
- })
3291
-
3292
- test("grok-4 returns empty object", () => {
3293
- const model = createMockModel({
3294
- id: "openrouter/grok-4",
3295
- providerID: "openrouter",
3296
- api: {
3297
- id: "grok-4",
3298
- url: "https://openrouter.ai",
3299
- npm: "@openrouter/ai-sdk-provider",
3300
- },
3301
- })
3302
- const result = ProviderTransform.variants(model)
3303
- expect(result).toEqual({})
3304
- })
3305
-
3306
- test("grok-3-mini returns low and high with reasoning", () => {
3307
- const model = createMockModel({
3308
- id: "openrouter/grok-3-mini",
3309
- providerID: "openrouter",
3310
- api: {
3311
- id: "grok-3-mini",
3312
- url: "https://openrouter.ai",
3313
- npm: "@openrouter/ai-sdk-provider",
3314
- },
3315
- })
3316
- const result = ProviderTransform.variants(model)
3317
- expect(Object.keys(result)).toEqual(["low", "high"])
3318
- expect(result.low).toEqual({ reasoning: { effort: "low" } })
3319
- expect(result.high).toEqual({ reasoning: { effort: "high" } })
3320
- })
3321
- })
3322
-
3323
- describe("@ai-sdk/gateway", () => {
3324
- test("anthropic sonnet 4.6 models return adaptive thinking options", () => {
3325
- const model = createMockModel({
3326
- id: "anthropic/claude-sonnet-4-6",
3327
- providerID: "gateway",
3328
- api: {
3329
- id: "anthropic/claude-sonnet-4-6",
3330
- url: "https://gateway.ai",
3331
- npm: "@ai-sdk/gateway",
3332
- },
3333
- })
3334
- const result = ProviderTransform.variants(model)
3335
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
3336
- expect(result.medium).toEqual({
3337
- thinking: {
3338
- type: "adaptive",
3339
- },
3340
- effort: "medium",
3341
- })
3342
- })
3343
-
3344
- test("anthropic sonnet 4.6 dot-format models return adaptive thinking options", () => {
3345
- const model = createMockModel({
3346
- id: "anthropic/claude-sonnet-4-6",
3347
- providerID: "gateway",
3348
- api: {
3349
- id: "anthropic/claude-sonnet-4.6",
3350
- url: "https://gateway.ai",
3351
- npm: "@ai-sdk/gateway",
3352
- },
3353
- })
3354
- const result = ProviderTransform.variants(model)
3355
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
3356
- expect(result.medium).toEqual({
3357
- thinking: {
3358
- type: "adaptive",
3359
- },
3360
- effort: "medium",
3361
- })
3362
- })
3363
-
3364
- test("anthropic opus 4.6 dot-format models return adaptive thinking options", () => {
3365
- const model = createMockModel({
3366
- id: "anthropic/claude-opus-4-6",
3367
- providerID: "gateway",
3368
- api: {
3369
- id: "anthropic/claude-opus-4.6",
3370
- url: "https://gateway.ai",
3371
- npm: "@ai-sdk/gateway",
3372
- },
3373
- })
3374
- const result = ProviderTransform.variants(model)
3375
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
3376
- expect(result.high).toEqual({
3377
- thinking: {
3378
- type: "adaptive",
3379
- },
3380
- effort: "high",
3381
- })
3382
- })
3383
-
3384
- test("anthropic opus 4.7 models return adaptive thinking options with xhigh", () => {
3385
- const model = createMockModel({
3386
- id: "anthropic/claude-opus-4-7",
3387
- providerID: "gateway",
3388
- api: {
3389
- id: "anthropic/claude-opus-4-7",
3390
- url: "https://gateway.ai",
3391
- npm: "@ai-sdk/gateway",
3392
- },
3393
- })
3394
- const result = ProviderTransform.variants(model)
3395
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
3396
- expect(result.xhigh).toEqual({
3397
- thinking: {
3398
- type: "adaptive",
3399
- display: "summarized",
3400
- },
3401
- effort: "xhigh",
3402
- })
3403
- expect(result.max).toEqual({
3404
- thinking: {
3405
- type: "adaptive",
3406
- display: "summarized",
3407
- },
3408
- effort: "max",
3409
- })
3410
- })
3411
-
3412
- test("anthropic opus 4.7 dot-format models return adaptive thinking options with xhigh", () => {
3413
- const model = createMockModel({
3414
- id: "anthropic/claude-opus-4-7",
3415
- providerID: "gateway",
3416
- api: {
3417
- id: "anthropic/claude-opus-4.7",
3418
- url: "https://gateway.ai",
3419
- npm: "@ai-sdk/gateway",
3420
- },
3421
- })
3422
- const result = ProviderTransform.variants(model)
3423
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
3424
- })
3425
-
3426
- test("anthropic opus 4.8 forces display summarized for adaptive reasoning", () => {
3427
- const model = createMockModel({
3428
- id: "anthropic/claude-opus-4-8",
3429
- providerID: "gateway",
3430
- api: {
3431
- id: "anthropic/claude-opus-4-8",
3432
- url: "https://gateway.ai",
3433
- npm: "@ai-sdk/gateway",
3434
- },
3435
- })
3436
- const result = ProviderTransform.variants(model)
3437
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
3438
- expect(result.high).toEqual({
3439
- thinking: {
3440
- type: "adaptive",
3441
- display: "summarized",
3442
- },
3443
- effort: "high",
3444
- })
3445
- })
3446
-
3447
- test("anthropic sonnet 5 returns adaptive thinking options with xhigh", () => {
3448
- const model = createMockModel({
3449
- id: "anthropic/claude-sonnet-5",
3450
- providerID: "gateway",
3451
- api: {
3452
- id: "anthropic/claude-sonnet-5",
3453
- url: "https://gateway.ai",
3454
- npm: "@ai-sdk/gateway",
3455
- },
3456
- })
3457
- const result = ProviderTransform.variants(model)
3458
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
3459
- expect(result.high).toEqual({
3460
- thinking: {
3461
- type: "adaptive",
3462
- display: "summarized",
3463
- },
3464
- effort: "high",
3465
- })
3466
- })
3467
-
3468
- test("anthropic opus 4.6 omits display so it keeps the summarized default", () => {
3469
- const model = createMockModel({
3470
- id: "anthropic/claude-opus-4-6",
3471
- providerID: "gateway",
3472
- api: {
3473
- id: "anthropic/claude-opus-4-6",
3474
- url: "https://gateway.ai",
3475
- npm: "@ai-sdk/gateway",
3476
- },
3477
- })
3478
- const result = ProviderTransform.variants(model)
3479
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
3480
- expect(result.high).toEqual({
3481
- thinking: {
3482
- type: "adaptive",
3483
- },
3484
- effort: "high",
3485
- })
3486
- })
3487
-
3488
- test("anthropic models return anthropic thinking options", () => {
3489
- const model = createMockModel({
3490
- id: "anthropic/claude-sonnet-4",
3491
- providerID: "gateway",
3492
- api: {
3493
- id: "anthropic/claude-sonnet-4",
3494
- url: "https://gateway.ai",
3495
- npm: "@ai-sdk/gateway",
3496
- },
3497
- })
3498
- const result = ProviderTransform.variants(model)
3499
- expect(Object.keys(result)).toEqual(["high", "max"])
3500
- expect(result.high).toEqual({
3501
- thinking: {
3502
- type: "enabled",
3503
- budgetTokens: 16000,
3504
- },
3505
- })
3506
- expect(result.max).toEqual({
3507
- thinking: {
3508
- type: "enabled",
3509
- budgetTokens: 31999,
3510
- },
3511
- })
3512
- })
3513
-
3514
- test("returns OPENAI_EFFORTS with reasoningEffort", () => {
3515
- const model = createMockModel({
3516
- id: "gateway/gateway-model",
3517
- providerID: "gateway",
3518
- api: {
3519
- id: "gateway-model",
3520
- url: "https://gateway.ai",
3521
- npm: "@ai-sdk/gateway",
3522
- },
3523
- })
3524
- const result = ProviderTransform.variants(model)
3525
- expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"])
3526
- expect(result.low).toEqual({ reasoningEffort: "low" })
3527
- expect(result.high).toEqual({ reasoningEffort: "high" })
3528
- })
3529
-
3530
- for (const testCase of [
3531
- { id: "openai/gpt-5-5", efforts: ["none", "low", "medium", "high", "xhigh"] },
3532
- { id: "openai/gpt-5-pro", efforts: ["high"] },
3533
- { id: "openai/gpt-5-5-pro", efforts: ["medium", "high", "xhigh"] },
3534
- { id: "openai/gpt-5-2-codex", efforts: ["low", "medium", "high", "xhigh"] },
3535
- { id: "openai/gpt-5-3-codex", efforts: ["none", "low", "medium", "high", "xhigh"] },
3536
- { id: "openai/gpt-5-3-codex-max", efforts: ["none", "low", "medium", "high", "xhigh"] },
3537
- { id: "openai/gpt-5-chat-latest", efforts: [] },
3538
- { id: "openai/gpt-5-2-chat-latest", efforts: ["medium"] },
3539
- ]) {
3540
- test(`${testCase.id} returns supported OpenAI reasoning efforts`, () => {
3541
- const result = ProviderTransform.variants(
3542
- createMockModel({
3543
- id: testCase.id,
3544
- providerID: "gateway",
3545
- api: {
3546
- id: testCase.id,
3547
- url: "https://gateway.ai",
3548
- npm: "@ai-sdk/gateway",
3549
- },
3550
- }),
3551
- )
3552
- expect(Object.keys(result)).toEqual(testCase.efforts)
3553
- })
3554
- }
3555
- })
3556
-
3557
- describe("@ai-sdk/github-copilot", () => {
3558
- test("standard models return low, medium, high", () => {
3559
- const model = createMockModel({
3560
- id: "gpt-4.5",
3561
- providerID: "github-copilot",
3562
- api: {
3563
- id: "gpt-4.5",
3564
- url: "https://api.githubcopilot.com",
3565
- npm: "@ai-sdk/github-copilot",
3566
- },
3567
- })
3568
- const result = ProviderTransform.variants(model)
3569
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3570
- expect(result.low).toEqual({
3571
- reasoningEffort: "low",
3572
- reasoningSummary: "auto",
3573
- include: ["reasoning.encrypted_content"],
3574
- })
3575
- })
3576
-
3577
- test("gpt-5.1-codex-max includes xhigh", () => {
3578
- const model = createMockModel({
3579
- id: "gpt-5.1-codex-max",
3580
- providerID: "github-copilot",
3581
- api: {
3582
- id: "gpt-5.1-codex-max",
3583
- url: "https://api.githubcopilot.com",
3584
- npm: "@ai-sdk/github-copilot",
3585
- },
3586
- })
3587
- const result = ProviderTransform.variants(model)
3588
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"])
3589
- })
3590
-
3591
- test("gpt-5.1-codex-mini does not include xhigh", () => {
3592
- const model = createMockModel({
3593
- id: "gpt-5.1-codex-mini",
3594
- providerID: "github-copilot",
3595
- api: {
3596
- id: "gpt-5.1-codex-mini",
3597
- url: "https://api.githubcopilot.com",
3598
- npm: "@ai-sdk/github-copilot",
3599
- },
3600
- })
3601
- const result = ProviderTransform.variants(model)
3602
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3603
- })
3604
-
3605
- test("gpt-5.1-codex does not include xhigh", () => {
3606
- const model = createMockModel({
3607
- id: "gpt-5.1-codex",
3608
- providerID: "github-copilot",
3609
- api: {
3610
- id: "gpt-5.1-codex",
3611
- url: "https://api.githubcopilot.com",
3612
- npm: "@ai-sdk/github-copilot",
3613
- },
3614
- })
3615
- const result = ProviderTransform.variants(model)
3616
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3617
- })
3618
-
3619
- test("gpt-5.2 includes xhigh", () => {
3620
- const model = createMockModel({
3621
- id: "gpt-5.2",
3622
- providerID: "github-copilot",
3623
- api: {
3624
- id: "gpt-5.2",
3625
- url: "https://api.githubcopilot.com",
3626
- npm: "@ai-sdk/github-copilot",
3627
- },
3628
- })
3629
- const result = ProviderTransform.variants(model)
3630
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"])
3631
- expect(result.xhigh).toEqual({
3632
- reasoningEffort: "xhigh",
3633
- reasoningSummary: "auto",
3634
- include: ["reasoning.encrypted_content"],
3635
- })
3636
- })
3637
-
3638
- test("gpt-5.2-codex includes xhigh", () => {
3639
- const model = createMockModel({
3640
- id: "gpt-5.2-codex",
3641
- providerID: "github-copilot",
3642
- api: {
3643
- id: "gpt-5.2-codex",
3644
- url: "https://api.githubcopilot.com",
3645
- npm: "@ai-sdk/github-copilot",
3646
- },
3647
- })
3648
- const result = ProviderTransform.variants(model)
3649
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"])
3650
- })
3651
-
3652
- test("gpt-5.3-codex includes xhigh", () => {
3653
- const model = createMockModel({
3654
- id: "gpt-5.3-codex",
3655
- providerID: "github-copilot",
3656
- api: {
3657
- id: "gpt-5.3-codex",
3658
- url: "https://api.githubcopilot.com",
3659
- npm: "@ai-sdk/github-copilot",
3660
- },
3661
- })
3662
- const result = ProviderTransform.variants(model)
3663
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"])
3664
- })
3665
-
3666
- test("gpt-5.4 includes xhigh", () => {
3667
- const model = createMockModel({
3668
- id: "gpt-5.4",
3669
- release_date: "2026-03-05",
3670
- providerID: "github-copilot",
3671
- api: {
3672
- id: "gpt-5.4",
3673
- url: "https://api.githubcopilot.com",
3674
- npm: "@ai-sdk/github-copilot",
3675
- },
3676
- })
3677
- const result = ProviderTransform.variants(model)
3678
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh"])
3679
- })
3680
- })
3681
-
3682
- describe("@ai-sdk/cerebras", () => {
3683
- test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => {
3684
- const model = createMockModel({
3685
- id: "cerebras/llama-4",
3686
- providerID: "cerebras",
3687
- api: {
3688
- id: "llama-4-sc",
3689
- url: "https://api.cerebras.ai",
3690
- npm: "@ai-sdk/cerebras",
3691
- },
3692
- })
3693
- const result = ProviderTransform.variants(model)
3694
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3695
- expect(result.low).toEqual({ reasoningEffort: "low" })
3696
- expect(result.high).toEqual({ reasoningEffort: "high" })
3697
- })
3698
- })
3699
-
3700
- describe("@ai-sdk/togetherai", () => {
3701
- test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => {
3702
- const model = createMockModel({
3703
- id: "togetherai/llama-4",
3704
- providerID: "togetherai",
3705
- api: {
3706
- id: "llama-4-sc",
3707
- url: "https://api.togetherai.com",
3708
- npm: "@ai-sdk/togetherai",
3709
- },
3710
- })
3711
- const result = ProviderTransform.variants(model)
3712
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3713
- expect(result.low).toEqual({ reasoningEffort: "low" })
3714
- expect(result.high).toEqual({ reasoningEffort: "high" })
3715
- })
3716
- })
3717
-
3718
- describe("@ai-sdk/xai", () => {
3719
- test("grok-3 returns empty object", () => {
3720
- const model = createMockModel({
3721
- id: "xai/grok-3",
3722
- providerID: "xai",
3723
- api: {
3724
- id: "grok-3",
3725
- url: "https://api.x.ai",
3726
- npm: "@ai-sdk/xai",
3727
- },
3728
- })
3729
- const result = ProviderTransform.variants(model)
3730
- expect(result).toEqual({})
3731
- })
3732
-
3733
- test("grok-3-mini returns low and high with reasoningEffort", () => {
3734
- const model = createMockModel({
3735
- id: "xai/grok-3-mini",
3736
- providerID: "xai",
3737
- api: {
3738
- id: "grok-3-mini",
3739
- url: "https://api.x.ai",
3740
- npm: "@ai-sdk/xai",
3741
- },
3742
- })
3743
- const result = ProviderTransform.variants(model)
3744
- expect(Object.keys(result)).toEqual(["low", "high"])
3745
- expect(result.low).toEqual({ reasoningEffort: "low" })
3746
- expect(result.high).toEqual({ reasoningEffort: "high" })
3747
- })
3748
- })
3749
-
3750
- describe("@ai-sdk/deepinfra", () => {
3751
- test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => {
3752
- const model = createMockModel({
3753
- id: "deepinfra/llama-4",
3754
- providerID: "deepinfra",
3755
- api: {
3756
- id: "llama-4-sc",
3757
- url: "https://api.deepinfra.com",
3758
- npm: "@ai-sdk/deepinfra",
3759
- },
3760
- })
3761
- const result = ProviderTransform.variants(model)
3762
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3763
- expect(result.low).toEqual({ reasoningEffort: "low" })
3764
- expect(result.high).toEqual({ reasoningEffort: "high" })
3765
- })
3766
- })
3767
-
3768
- describe("@ai-sdk/openai-compatible", () => {
3769
- test("returns WIDELY_SUPPORTED_EFFORTS with reasoningEffort", () => {
3770
- const model = createMockModel({
3771
- id: "custom-provider/custom-model",
3772
- providerID: "custom-provider",
3773
- api: {
3774
- id: "custom-model",
3775
- url: "https://api.custom.com",
3776
- npm: "@ai-sdk/openai-compatible",
3777
- },
3778
- })
3779
- const result = ProviderTransform.variants(model)
3780
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3781
- expect(result.low).toEqual({ reasoningEffort: "low" })
3782
- expect(result.high).toEqual({ reasoningEffort: "high" })
3783
- })
3784
-
3785
- test("north-mini-code-1-0 returns only none and high", () => {
3786
- const model = createMockModel({
3787
- id: "cohere/north-mini-code-1-0",
3788
- providerID: "cohere",
3789
- api: {
3790
- id: "North-Mini-Code-1-0-latest",
3791
- url: "https://api.cohere.com/compatibility/v1",
3792
- npm: "@ai-sdk/openai-compatible",
3793
- },
3794
- })
3795
- const result = ProviderTransform.variants(model)
3796
- expect(result).toEqual({
3797
- none: { reasoningEffort: "none" },
3798
- high: { reasoningEffort: "high" },
3799
- })
3800
- })
3801
- })
3802
-
3803
- describe("@ai-sdk/azure", () => {
3804
- test("o1-mini returns empty object", () => {
3805
- const model = createMockModel({
3806
- id: "o1-mini",
3807
- providerID: "azure",
3808
- api: {
3809
- id: "o1-mini",
3810
- url: "https://azure.com",
3811
- npm: "@ai-sdk/azure",
3812
- },
3813
- })
3814
- const result = ProviderTransform.variants(model)
3815
- expect(result).toEqual({})
3816
- })
3817
-
3818
- test("standard azure models return custom efforts with reasoningSummary", () => {
3819
- const model = createMockModel({
3820
- id: "o1",
3821
- providerID: "azure",
3822
- api: {
3823
- id: "o1",
3824
- url: "https://azure.com",
3825
- npm: "@ai-sdk/azure",
3826
- },
3827
- })
3828
- const result = ProviderTransform.variants(model)
3829
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3830
- expect(result.low).toEqual({
3831
- reasoningEffort: "low",
3832
- reasoningSummary: "auto",
3833
- include: ["reasoning.encrypted_content"],
3834
- })
3835
- })
3836
-
3837
- test("gpt-5 adds minimal effort", () => {
3838
- const model = createMockModel({
3839
- id: "gpt-5",
3840
- providerID: "azure",
3841
- api: {
3842
- id: "gpt-5",
3843
- url: "https://azure.com",
3844
- npm: "@ai-sdk/azure",
3845
- },
3846
- })
3847
- const result = ProviderTransform.variants(model)
3848
- expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high"])
3849
- })
3850
-
3851
- for (const testCase of [
3852
- { id: "gpt-5-1", efforts: ["none", "low", "medium", "high"] },
3853
- { id: "gpt-5-4", efforts: ["none", "low", "medium", "high", "xhigh"] },
3854
- { id: "gpt-5.4", efforts: ["none", "low", "medium", "high", "xhigh"] },
3855
- { id: "gpt-5-5", efforts: ["none", "low", "medium", "high", "xhigh"] },
3856
- ]) {
3857
- test(`${testCase.id} returns supported Azure reasoning efforts`, () => {
3858
- const result = ProviderTransform.variants(
3859
- createMockModel({
3860
- id: testCase.id,
3861
- providerID: "azure",
3862
- api: {
3863
- id: testCase.id,
3864
- url: "https://azure.com",
3865
- npm: "@ai-sdk/azure",
3866
- },
3867
- }),
3868
- )
3869
- expect(Object.keys(result)).toEqual(testCase.efforts)
3870
- })
3871
- }
3872
- })
3873
-
3874
- describe("@ai-sdk/openai", () => {
3875
- test("gpt-5-pro returns only high effort", () => {
3876
- const model = createMockModel({
3877
- id: "gpt-5-pro",
3878
- providerID: "openai",
3879
- api: {
3880
- id: "gpt-5-pro",
3881
- url: "https://api.openai.com",
3882
- npm: "@ai-sdk/openai",
3883
- },
3884
- })
3885
- const result = ProviderTransform.variants(model)
3886
- expect(Object.keys(result)).toEqual(["high"])
3887
- })
3888
-
3889
- test("standard openai models return custom efforts with reasoningSummary", () => {
3890
- const model = createMockModel({
3891
- id: "gpt-5",
3892
- providerID: "openai",
3893
- api: {
3894
- id: "gpt-5",
3895
- url: "https://api.openai.com",
3896
- npm: "@ai-sdk/openai",
3897
- },
3898
- release_date: "2024-06-01",
3899
- })
3900
- const result = ProviderTransform.variants(model)
3901
- expect(Object.keys(result)).toEqual(["minimal", "low", "medium", "high"])
3902
- expect(result.low).toEqual({
3903
- reasoningEffort: "low",
3904
- reasoningSummary: "auto",
3905
- include: ["reasoning.encrypted_content"],
3906
- })
3907
- })
3908
-
3909
- test("models after 2025-11-13 include 'none' effort", () => {
3910
- const model = createMockModel({
3911
- id: "gpt-5-nano",
3912
- providerID: "openai",
3913
- api: {
3914
- id: "gpt-5-nano",
3915
- url: "https://api.openai.com",
3916
- npm: "@ai-sdk/openai",
3917
- },
3918
- release_date: "2025-11-14",
3919
- })
3920
- const result = ProviderTransform.variants(model)
3921
- expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high"])
3922
- })
3923
-
3924
- test("models after 2025-12-04 include 'xhigh' effort", () => {
3925
- const model = createMockModel({
3926
- id: "openai/gpt-5-reasoning",
3927
- providerID: "openai",
3928
- api: {
3929
- id: "gpt-5-reasoning",
3930
- url: "https://api.openai.com",
3931
- npm: "@ai-sdk/openai",
3932
- },
3933
- release_date: "2025-12-05",
3934
- })
3935
- const result = ProviderTransform.variants(model)
3936
- expect(Object.keys(result)).toEqual(["none", "minimal", "low", "medium", "high", "xhigh"])
3937
- })
3938
-
3939
- for (const testCase of [
3940
- { id: "o1", releaseDate: "2024-12-17", efforts: ["low", "medium", "high"] },
3941
- { id: "o1-pro", releaseDate: "2025-03-19", efforts: ["low", "medium", "high"] },
3942
- { id: "o3", releaseDate: "2025-04-16", efforts: ["low", "medium", "high"] },
3943
- { id: "o3-mini", releaseDate: "2025-01-31", efforts: ["low", "medium", "high"] },
3944
- { id: "o3-pro", releaseDate: "2025-06-10", efforts: ["low", "medium", "high"] },
3945
- { id: "o4-mini", releaseDate: "2025-04-16", efforts: ["low", "medium", "high"] },
3946
- { id: "o3-deep-research", releaseDate: "2025-06-26", efforts: ["medium"] },
3947
- { id: "o4-mini-deep-research", releaseDate: "2025-06-26", efforts: ["medium"] },
3948
- { id: "gpt-5.1", releaseDate: "2025-11-13", efforts: ["none", "low", "medium", "high"] },
3949
- { id: "gpt-5.4", releaseDate: "2026-03-05", efforts: ["none", "low", "medium", "high", "xhigh"] },
3950
- {
3951
- id: "gpt-5.5",
3952
- modelID: "gpt-5-5",
3953
- releaseDate: "2026-04-23",
3954
- efforts: ["none", "low", "medium", "high", "xhigh"],
3955
- },
3956
- { id: "gpt-5.4-pro", releaseDate: "2026-03-05", efforts: ["medium", "high", "xhigh"] },
3957
- { id: "gpt-5.5-pro", releaseDate: "2026-04-23", efforts: ["medium", "high", "xhigh"] },
3958
- { id: "gpt-5-codex", releaseDate: "2025-09-23", efforts: ["low", "medium", "high"] },
3959
- { id: "gpt-5.1-codex", releaseDate: "2025-11-13", efforts: ["low", "medium", "high"] },
3960
- { id: "gpt-5.1-codex-max", releaseDate: "2025-11-13", efforts: ["low", "medium", "high", "xhigh"] },
3961
- { id: "gpt-5.2-codex", releaseDate: "2025-12-11", efforts: ["low", "medium", "high", "xhigh"] },
3962
- { id: "gpt-5.3-codex", releaseDate: "2026-01-22", efforts: ["none", "low", "medium", "high", "xhigh"] },
3963
- { id: "gpt-5.3-codex-max", releaseDate: "2026-01-22", efforts: ["none", "low", "medium", "high", "xhigh"] },
3964
- { id: "gpt-5-chat-latest", releaseDate: "2025-08-07", efforts: [] },
3965
- { id: "gpt-5.1-chat-latest", releaseDate: "2025-11-13", efforts: ["medium"] },
3966
- { id: "gpt-5.2-chat-latest", releaseDate: "2025-12-11", efforts: ["medium"] },
3967
- ]) {
3968
- test(`${testCase.id} returns supported reasoning efforts`, () => {
3969
- const result = ProviderTransform.variants(
3970
- createMockModel({
3971
- id: testCase.modelID ?? testCase.id,
3972
- providerID: "openai",
3973
- api: {
3974
- id: testCase.id,
3975
- url: "https://api.openai.com",
3976
- npm: "@ai-sdk/openai",
3977
- },
3978
- release_date: testCase.releaseDate,
3979
- }),
3980
- )
3981
- expect(Object.keys(result)).toEqual(testCase.efforts)
3982
- })
3983
- }
3984
-
3985
- test("gpt-50 (lookalike) does not get gpt-5 family treatment", () => {
3986
- const model = createMockModel({
3987
- id: "gpt-50",
3988
- providerID: "openai",
3989
- api: {
3990
- id: "gpt-50",
3991
- url: "https://api.openai.com",
3992
- npm: "@ai-sdk/openai",
3993
- },
3994
- release_date: "2024-01-01",
3995
- })
3996
- const result = ProviderTransform.variants(model)
3997
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
3998
- })
3999
- })
4000
-
4001
- describe("@ai-sdk/amazon-bedrock/mantle", () => {
4002
- test("gpt-5.5 returns OpenAI-style reasoning variants", () => {
4003
- const model = createMockModel({
4004
- id: "openai.gpt-5.5",
4005
- providerID: "amazon-bedrock",
4006
- api: {
4007
- id: "openai.gpt-5.5",
4008
- url: "https://bedrock-mantle.us-east-2.api.aws/openai/v1",
4009
- npm: "@ai-sdk/amazon-bedrock/mantle",
4010
- },
4011
- release_date: "2026-04-23",
4012
- })
4013
- const result = ProviderTransform.variants(model)
4014
- expect(Object.keys(result)).toEqual(["none", "low", "medium", "high", "xhigh"])
4015
- expect(result.medium).toEqual({
4016
- reasoningEffort: "medium",
4017
- reasoningSummary: "auto",
4018
- include: ["reasoning.encrypted_content"],
4019
- })
4020
- })
4021
- })
4022
-
4023
- describe("@ai-sdk/anthropic", () => {
4024
- for (const testCase of [
4025
- {
4026
- name: "opus 4.5",
4027
- apiIds: ["claude-opus-4-5-20251101", "claude-opus-4.5-20251101"],
4028
- efforts: ["low", "medium", "high"],
4029
- expectedHigh: { effort: "high" },
4030
- },
4031
- {
4032
- name: "sonnet 4.6",
4033
- apiIds: ["claude-sonnet-4-6", "claude-sonnet-4.6"],
4034
- efforts: ["low", "medium", "high", "max"],
4035
- expectedHigh: { thinking: { type: "adaptive" }, effort: "high" },
4036
- },
4037
- {
4038
- name: "opus 4.6",
4039
- apiIds: ["claude-opus-4-6", "claude-opus-4.6"],
4040
- efforts: ["low", "medium", "high", "max"],
4041
- expectedHigh: { thinking: { type: "adaptive" }, effort: "high" },
4042
- },
4043
- {
4044
- name: "opus 4.7",
4045
- apiIds: ["claude-opus-4-7", "claude-opus-4.7"],
4046
- efforts: ["low", "medium", "high", "xhigh", "max"],
4047
- expectedHigh: { thinking: { type: "adaptive", display: "summarized" }, effort: "high" },
4048
- },
4049
- {
4050
- name: "opus 4.8",
4051
- apiIds: ["claude-opus-4-8", "claude-opus-4.8"],
4052
- efforts: ["low", "medium", "high", "xhigh", "max"],
4053
- expectedHigh: { thinking: { type: "adaptive", display: "summarized" }, effort: "high" },
4054
- },
4055
- {
4056
- name: "sonnet 5",
4057
- apiIds: ["claude-sonnet-5", "claude-sonnet-5-20260630"],
4058
- efforts: ["low", "medium", "high", "xhigh", "max"],
4059
- expectedHigh: { thinking: { type: "adaptive", display: "summarized" }, effort: "high" },
4060
- },
4061
- {
4062
- name: "fable 5",
4063
- apiIds: ["claude-fable-5"],
4064
- efforts: ["low", "medium", "high", "xhigh", "max"],
4065
- expectedHigh: { thinking: { type: "adaptive", display: "summarized" }, effort: "high" },
4066
- },
4067
- ]) {
4068
- for (const apiId of testCase.apiIds) {
4069
- test(`${testCase.name} ${apiId} returns supported reasoning efforts`, () => {
4070
- const result = ProviderTransform.variants(
4071
- createMockModel({
4072
- id: `anthropic/${apiId}`,
4073
- providerID: "anthropic",
4074
- api: {
4075
- id: apiId,
4076
- url: "https://api.anthropic.com",
4077
- npm: "@ai-sdk/anthropic",
4078
- },
4079
- }),
4080
- )
4081
- expect(Object.keys(result)).toEqual(testCase.efforts)
4082
- expect(result.high).toEqual(testCase.expectedHigh)
4083
- })
4084
- }
4085
- }
4086
-
4087
- test("github copilot opus 4.7 returns only medium reasoning effort", () => {
4088
- const model = createMockModel({
4089
- id: "claude-opus-4.7",
4090
- providerID: "github-copilot",
4091
- api: {
4092
- id: "claude-opus-4.7",
4093
- url: "https://api.githubcopilot.com/v1",
4094
- npm: "@ai-sdk/anthropic",
4095
- },
4096
- })
4097
- const result = ProviderTransform.variants(model)
4098
- expect(result).toEqual({
4099
- medium: {
4100
- thinking: {
4101
- type: "adaptive",
4102
- display: "summarized",
4103
- },
4104
- effort: "medium",
4105
- },
4106
- })
4107
- })
4108
-
4109
- test("returns high and max with thinking config", () => {
4110
- const model = createMockModel({
4111
- id: "anthropic/claude-4",
4112
- providerID: "anthropic",
4113
- api: {
4114
- id: "claude-4",
4115
- url: "https://api.anthropic.com",
4116
- npm: "@ai-sdk/anthropic",
4117
- },
4118
- })
4119
- const result = ProviderTransform.variants(model)
4120
- expect(Object.keys(result)).toEqual(["high", "max"])
4121
- expect(result.high).toEqual({
4122
- thinking: {
4123
- type: "enabled",
4124
- budgetTokens: 16000,
4125
- },
4126
- })
4127
- expect(result.max).toEqual({
4128
- thinking: {
4129
- type: "enabled",
4130
- budgetTokens: 31999,
4131
- },
4132
- })
4133
- })
4134
- })
4135
-
4136
- describe("@ai-sdk/google-vertex/anthropic", () => {
4137
- test("opus 4.8 uses adaptive reasoning for Vertex model IDs", () => {
4138
- const result = ProviderTransform.variants(
4139
- createMockModel({
4140
- id: "google-vertex-anthropic/claude-opus-4-8@default",
4141
- providerID: "google-vertex-anthropic",
4142
- api: {
4143
- id: "claude-opus-4-8@default",
4144
- url: "https://us-central1-aiplatform.googleapis.com",
4145
- npm: "@ai-sdk/google-vertex/anthropic",
4146
- },
4147
- }),
4148
- )
4149
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
4150
- expect(result.high).toEqual({
4151
- thinking: {
4152
- type: "adaptive",
4153
- display: "summarized",
4154
- },
4155
- effort: "high",
4156
- })
4157
- })
4158
-
4159
- test("sonnet 5 uses adaptive reasoning for Vertex model IDs", () => {
4160
- const result = ProviderTransform.variants(
4161
- createMockModel({
4162
- id: "google-vertex-anthropic/claude-sonnet-5@default",
4163
- providerID: "google-vertex-anthropic",
4164
- api: {
4165
- id: "claude-sonnet-5@default",
4166
- url: "https://us-central1-aiplatform.googleapis.com",
4167
- npm: "@ai-sdk/google-vertex/anthropic",
4168
- },
4169
- }),
4170
- )
4171
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
4172
- expect(result.high).toEqual({
4173
- thinking: {
4174
- type: "adaptive",
4175
- display: "summarized",
4176
- },
4177
- effort: "high",
4178
- })
4179
- })
4180
- })
4181
-
4182
- describe("@ai-sdk/amazon-bedrock", () => {
4183
- test("anthropic sonnet 4.6 returns adaptive reasoning options", () => {
4184
- const model = createMockModel({
4185
- id: "bedrock/anthropic-claude-sonnet-4-6",
4186
- providerID: "bedrock",
4187
- api: {
4188
- id: "anthropic.claude-sonnet-4-6",
4189
- url: "https://bedrock.amazonaws.com",
4190
- npm: "@ai-sdk/amazon-bedrock",
4191
- },
4192
- })
4193
- const result = ProviderTransform.variants(model)
4194
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "max"])
4195
- expect(result.max).toEqual({
4196
- reasoningConfig: {
4197
- type: "adaptive",
4198
- maxReasoningEffort: "max",
4199
- },
4200
- })
4201
- })
4202
-
4203
- test("anthropic opus 4.7 returns adaptive reasoning options with xhigh", () => {
4204
- const model = createMockModel({
4205
- id: "bedrock/anthropic-claude-opus-4-7",
4206
- providerID: "bedrock",
4207
- api: {
4208
- id: "anthropic.claude-opus-4-7",
4209
- url: "https://bedrock.amazonaws.com",
4210
- npm: "@ai-sdk/amazon-bedrock",
4211
- },
4212
- })
4213
- const result = ProviderTransform.variants(model)
4214
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
4215
- expect(result.xhigh).toEqual({
4216
- reasoningConfig: {
4217
- type: "adaptive",
4218
- maxReasoningEffort: "xhigh",
4219
- display: "summarized",
4220
- },
4221
- })
4222
- expect(result.max).toEqual({
4223
- reasoningConfig: {
4224
- type: "adaptive",
4225
- maxReasoningEffort: "max",
4226
- display: "summarized",
4227
- },
4228
- })
4229
- })
4230
-
4231
- test("anthropic opus 4.8 returns adaptive reasoning options with xhigh", () => {
4232
- const result = ProviderTransform.variants(
4233
- createMockModel({
4234
- id: "bedrock/anthropic-claude-opus-4.8",
4235
- providerID: "bedrock",
4236
- api: {
4237
- id: "anthropic.claude-opus-4.8",
4238
- url: "https://bedrock.amazonaws.com",
4239
- npm: "@ai-sdk/amazon-bedrock",
4240
- },
4241
- }),
4242
- )
4243
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
4244
- expect(result.high).toEqual({
4245
- reasoningConfig: {
4246
- type: "adaptive",
4247
- maxReasoningEffort: "high",
4248
- display: "summarized",
4249
- },
4250
- })
4251
- })
4252
-
4253
- test("anthropic sonnet 5 returns adaptive reasoning options with xhigh", () => {
4254
- const result = ProviderTransform.variants(
4255
- createMockModel({
4256
- id: "bedrock/anthropic-claude-sonnet-5",
4257
- providerID: "bedrock",
4258
- api: {
4259
- id: "anthropic.claude-sonnet-5",
4260
- url: "https://bedrock.amazonaws.com",
4261
- npm: "@ai-sdk/amazon-bedrock",
4262
- },
4263
- }),
4264
- )
4265
- expect(Object.keys(result)).toEqual(["low", "medium", "high", "xhigh", "max"])
4266
- expect(result.high).toEqual({
4267
- reasoningConfig: {
4268
- type: "adaptive",
4269
- maxReasoningEffort: "high",
4270
- display: "summarized",
4271
- },
4272
- })
4273
- })
4274
-
4275
- test("returns WIDELY_SUPPORTED_EFFORTS with reasoningConfig", () => {
4276
- const model = createMockModel({
4277
- id: "bedrock/llama-4",
4278
- providerID: "bedrock",
4279
- api: {
4280
- id: "llama-4-sc",
4281
- url: "https://bedrock.amazonaws.com",
4282
- npm: "@ai-sdk/amazon-bedrock",
4283
- },
4284
- })
4285
- const result = ProviderTransform.variants(model)
4286
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
4287
- expect(result.low).toEqual({
4288
- reasoningConfig: {
4289
- type: "enabled",
4290
- maxReasoningEffort: "low",
4291
- },
4292
- })
4293
- })
4294
- })
4295
-
4296
- for (const provider of [
4297
- { name: "@ai-sdk/google", providerID: "google", url: "https://generativelanguage.googleapis.com" },
4298
- { name: "@ai-sdk/google-vertex", providerID: "google-vertex", url: "https://vertexai.googleapis.com" },
4299
- ]) {
4300
- describe(provider.name, () => {
4301
- for (const testCase of [
4302
- {
4303
- apiId: "gemini-2.5-pro",
4304
- efforts: ["high", "max"],
4305
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16_000 } },
4306
- expectedMax: { thinkingConfig: { includeThoughts: true, thinkingBudget: 32_768 } },
4307
- },
4308
- {
4309
- apiId: "gemini-2.5-flash",
4310
- efforts: ["high", "max"],
4311
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16_000 } },
4312
- expectedMax: { thinkingConfig: { includeThoughts: true, thinkingBudget: 24_576 } },
4313
- },
4314
- {
4315
- apiId: "gemini-3-pro-preview",
4316
- efforts: ["low", "medium", "high"],
4317
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
4318
- },
4319
- {
4320
- apiId: "gemini-3.1-pro-preview",
4321
- efforts: ["low", "medium", "high"],
4322
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
4323
- },
4324
- {
4325
- apiId: "gemini-3-flash-preview",
4326
- efforts: ["minimal", "low", "medium", "high"],
4327
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
4328
- },
4329
- {
4330
- apiId: "gemini-3.1-flash-lite",
4331
- efforts: ["minimal", "low", "medium", "high"],
4332
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
4333
- },
4334
- {
4335
- apiId: "gemini-3.1-flash-image-preview",
4336
- efforts: ["minimal", "high"],
4337
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
4338
- },
4339
- {
4340
- apiId: "gemini-3-pro-image-preview",
4341
- efforts: ["high"],
4342
- expectedHigh: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } },
4343
- },
4344
- ]) {
4345
- test(`${testCase.apiId} returns supported thinking controls`, () => {
4346
- const result = ProviderTransform.variants(
4347
- createMockModel({
4348
- id: `${provider.providerID}/${testCase.apiId}`,
4349
- providerID: provider.providerID,
4350
- api: {
4351
- id: testCase.apiId,
4352
- url: provider.url,
4353
- npm: provider.name,
4354
- },
4355
- }),
4356
- )
4357
- expect(Object.keys(result)).toEqual(testCase.efforts)
4358
- expect(result.high).toEqual(testCase.expectedHigh)
4359
- if (testCase.expectedMax) expect(result.max).toEqual(testCase.expectedMax)
4360
- })
4361
- }
4362
- })
4363
- }
4364
-
4365
- describe("@ai-sdk/cohere", () => {
4366
- test("returns empty object", () => {
4367
- const model = createMockModel({
4368
- id: "cohere/command-r",
4369
- providerID: "cohere",
4370
- api: {
4371
- id: "command-r",
4372
- url: "https://api.cohere.com",
4373
- npm: "@ai-sdk/cohere",
4374
- },
4375
- })
4376
- const result = ProviderTransform.variants(model)
4377
- expect(result).toEqual({})
4378
- })
4379
- })
4380
-
4381
- describe("@ai-sdk/groq", () => {
4382
- test("returns none and WIDELY_SUPPORTED_EFFORTS with thinkingLevel", () => {
4383
- const model = createMockModel({
4384
- id: "groq/llama-4",
4385
- providerID: "groq",
4386
- api: {
4387
- id: "llama-4-sc",
4388
- url: "https://api.groq.com",
4389
- npm: "@ai-sdk/groq",
4390
- },
4391
- })
4392
- const result = ProviderTransform.variants(model)
4393
- expect(Object.keys(result)).toEqual(["none", "low", "medium", "high"])
4394
- expect(result.none).toEqual({
4395
- reasoningEffort: "none",
4396
- })
4397
- expect(result.low).toEqual({
4398
- reasoningEffort: "low",
4399
- })
4400
- })
4401
- })
4402
-
4403
- describe("@ai-sdk/perplexity", () => {
4404
- test("returns empty object", () => {
4405
- const model = createMockModel({
4406
- id: "perplexity/sonar-plus",
4407
- providerID: "perplexity",
4408
- api: {
4409
- id: "sonar-plus",
4410
- url: "https://api.perplexity.ai",
4411
- npm: "@ai-sdk/perplexity",
4412
- },
4413
- })
4414
- const result = ProviderTransform.variants(model)
4415
- expect(result).toEqual({})
4416
- })
4417
- })
4418
-
4419
- describe("@jerome-benoit/sap-ai-provider-v2", () => {
4420
- const sapModel = (apiId: string, releaseDate = "2024-01-01") =>
4421
- createMockModel({
4422
- id: `sap-ai-core/${apiId}`,
4423
- providerID: "sap-ai-core",
4424
- api: {
4425
- id: apiId,
4426
- url: "https://api.ai.sap",
4427
- npm: "@jerome-benoit/sap-ai-provider-v2",
4428
- },
4429
- release_date: releaseDate,
4430
- })
4431
-
4432
- for (const testCase of [
4433
- {
4434
- name: "sonnet 4.6",
4435
- apiIds: ["anthropic--claude-sonnet-4-6"],
4436
- efforts: ["low", "medium", "high", "max"],
4437
- thinking: { type: "adaptive" },
4438
- },
4439
- {
4440
- name: "opus 4.6",
4441
- apiIds: ["anthropic--claude-4.6-opus", "anthropic--claude-4-6-opus"],
4442
- efforts: ["low", "medium", "high", "max"],
4443
- thinking: { type: "adaptive" },
4444
- },
4445
- {
4446
- name: "opus 4.7",
4447
- apiIds: ["anthropic--claude-4.7-opus", "anthropic--claude-4-7-opus"],
4448
- efforts: ["low", "medium", "high", "xhigh", "max"],
4449
- thinking: { type: "adaptive", display: "summarized" },
4450
- },
4451
- {
4452
- name: "opus 4.8",
4453
- apiIds: ["anthropic--claude-4.8-opus", "anthropic--claude-4-8-opus"],
4454
- efforts: ["low", "medium", "high", "xhigh", "max"],
4455
- thinking: { type: "adaptive", display: "summarized" },
4456
- },
4457
- {
4458
- name: "sonnet 5",
4459
- apiIds: ["anthropic--claude-sonnet-5", "anthropic--claude-5-sonnet"],
4460
- efforts: ["low", "medium", "high", "xhigh", "max"],
4461
- thinking: { type: "adaptive", display: "summarized" },
4462
- },
4463
- ]) {
4464
- for (const apiId of testCase.apiIds) {
4465
- test(`${testCase.name} ${apiId} returns adaptive thinking variants under modelParams`, () => {
4466
- const result = ProviderTransform.variants(sapModel(apiId))
4467
- expect(Object.keys(result)).toEqual(testCase.efforts)
4468
- for (const effort of testCase.efforts) {
4469
- expect(result[effort]).toEqual({
4470
- modelParams: {
4471
- thinking: testCase.thinking,
4472
- output_config: { effort },
4473
- },
4474
- })
4475
- }
4476
- })
4477
- }
4478
- }
4479
-
4480
- for (const apiId of ["anthropic--claude-sonnet-4", "anthropic--claude-4.5-opus"]) {
4481
- test(`${apiId} returns budget_tokens variants under modelParams`, () => {
4482
- const result = ProviderTransform.variants(sapModel(apiId))
4483
- expect(Object.keys(result)).toEqual(["high", "max"])
4484
- expect(result.high).toEqual({
4485
- modelParams: { thinking: { type: "enabled", budget_tokens: 16000 } },
4486
- })
4487
- expect(result.max).toEqual({
4488
- modelParams: { thinking: { type: "enabled", budget_tokens: 31999 } },
4489
- })
4490
- })
4491
- }
4492
-
4493
- for (const testCase of [
4494
- { apiId: "gemini-2.5-pro", maxBudget: 32768 },
4495
- { apiId: "gemini-2.5-flash", maxBudget: 24576 },
4496
- ]) {
4497
- test(`${testCase.apiId} returns thinkingConfig variants under modelParams`, () => {
4498
- const result = ProviderTransform.variants(sapModel(testCase.apiId))
4499
- expect(Object.keys(result)).toEqual(["high", "max"])
4500
- expect(result.high).toEqual({
4501
- modelParams: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } },
4502
- })
4503
- expect(result.max).toEqual({
4504
- modelParams: { thinkingConfig: { includeThoughts: true, thinkingBudget: testCase.maxBudget } },
4505
- })
4506
- })
4507
- }
4508
-
4509
- for (const testCase of [
4510
- { apiId: "gpt-5", releaseDate: "2025-08-07", efforts: ["minimal", "low", "medium", "high"] },
4511
- { apiId: "gpt-5-mini", releaseDate: "2025-08-07", efforts: ["minimal", "low", "medium", "high"] },
4512
- { apiId: "gpt-5-nano", releaseDate: "2025-08-07", efforts: ["minimal", "low", "medium", "high"] },
4513
- { apiId: "gpt-5.4", releaseDate: "2026-01-15", efforts: ["none", "low", "medium", "high", "xhigh"] },
4514
- { apiId: "azure-openai--o3-mini", releaseDate: "2024-01-01", efforts: ["low", "medium", "high"] },
4515
- ]) {
4516
- test(`${testCase.apiId} returns reasoning_effort variants under modelParams`, () => {
4517
- const result = ProviderTransform.variants(sapModel(testCase.apiId, testCase.releaseDate))
4518
- expect(Object.keys(result)).toEqual(testCase.efforts)
4519
- for (const effort of testCase.efforts) {
4520
- expect(result[effort]).toEqual({ modelParams: { reasoning_effort: effort } })
4521
- }
4522
- })
4523
- }
4524
-
4525
- for (const apiId of [
4526
- "gemini-3.1-flash-lite",
4527
- "cohere--command-a-reasoning",
4528
- "sonar-deep-research",
4529
- "aws--llama-opus-4.7-fake",
4530
- ]) {
4531
- test(`${apiId} falls through to harmonized reasoning_effort fallback`, () => {
4532
- const result = ProviderTransform.variants(sapModel(apiId))
4533
- expect(Object.keys(result)).toEqual(["low", "medium", "high"])
4534
- for (const effort of ["low", "medium", "high"]) {
4535
- expect(result[effort]).toEqual({ modelParams: { reasoning_effort: effort } })
4536
- }
4537
- })
4538
- }
4539
- })
4540
-
4541
- describe("ai-gateway-provider (cloudflare-ai-gateway)", () => {
4542
- const cfModel = (apiId: string, releaseDate = "2024-01-01") =>
4543
- createMockModel({
4544
- id: `cloudflare-ai-gateway/${apiId}`,
4545
- providerID: "cloudflare-ai-gateway",
4546
- api: {
4547
- id: apiId,
4548
- url: "https://gateway.ai.cloudflare.com/v1/compat",
4549
- npm: "ai-gateway-provider",
4550
- },
4551
- release_date: releaseDate,
4552
- })
4553
-
4554
- for (const testCase of [
4555
- { id: "openai/gpt-5.4", efforts: ["none", "low", "medium", "high", "xhigh"] },
4556
- { id: "openai/gpt-5.2-codex", efforts: ["low", "medium", "high", "xhigh"] },
4557
- { id: "openai/gpt-5.3-codex", efforts: ["none", "low", "medium", "high", "xhigh"] },
4558
- { id: "openai/gpt-5-pro", efforts: ["high"] },
4559
- { id: "openai/gpt-5.2-pro", efforts: ["medium", "high", "xhigh"] },
4560
- { id: "openai/gpt-5-chat-latest", efforts: [] },
4561
- { id: "openai/gpt-5.2-chat-latest", efforts: ["medium"] },
4562
- ]) {
4563
- test(`${testCase.id} returns supported reasoning efforts`, () => {
4564
- const result = ProviderTransform.variants(cfModel(testCase.id, "2026-03-05"))
4565
- expect(Object.keys(result)).toEqual(testCase.efforts)
4566
- })
4567
- }
4568
-
4569
- test("openai gpt-4o (no reasoning) returns empty", () => {
4570
- const model = cfModel("openai/gpt-4o")
4571
- model.capabilities.reasoning = false
4572
- const result = ProviderTransform.variants(model)
4573
- expect(result).toEqual({})
4574
- })
4575
-
4576
- test("non-openai upstream falls back to widely-supported OAI efforts", () => {
4577
- const result = ProviderTransform.variants(cfModel("anthropic/claude-sonnet-4-6"))
4578
- expect(result).toEqual({
4579
- low: { reasoningEffort: "low" },
4580
- medium: { reasoningEffort: "medium" },
4581
- high: { reasoningEffort: "high" },
4582
- })
4583
- })
4584
- })
4585
- })
4586
-
4587
- describe("ProviderTransform.smallOptions - gpt-5 chat/search", () => {
4588
- const createModel = (apiId: string) => {
4589
- const model = {
4590
- id: `openai/${apiId}`,
4591
- providerID: "openai",
4592
- api: {
4593
- id: apiId,
4594
- url: "https://api.openai.com",
4595
- npm: "@ai-sdk/openai",
4596
- },
4597
- capabilities: { reasoning: true },
4598
- limit: { output: 64_000 },
4599
- release_date: "2026-01-01",
4600
- } as any
4601
- model.variants = ProviderTransform.variants(model)
4602
- return model
4603
- }
4604
-
4605
- for (const testCase of [
4606
- { id: "gpt-5-chat-latest", options: { store: false } },
4607
- {
4608
- id: "gpt-5.1-chat-latest",
4609
- options: {
4610
- store: false,
4611
- reasoningEffort: "medium",
4612
- reasoningSummary: "auto",
4613
- include: ["reasoning.encrypted_content"],
4614
- },
4615
- },
4616
- {
4617
- id: "gpt-5.2-chat-latest",
4618
- options: {
4619
- store: false,
4620
- reasoningEffort: "medium",
4621
- reasoningSummary: "auto",
4622
- include: ["reasoning.encrypted_content"],
4623
- },
4624
- },
4625
- {
4626
- id: "gpt-5-search-api",
4627
- options: {
4628
- store: false,
4629
- reasoningEffort: "none",
4630
- reasoningSummary: "auto",
4631
- include: ["reasoning.encrypted_content"],
4632
- },
4633
- },
4634
- ]) {
4635
- test(`${testCase.id} returns only supported small options`, () => {
4636
- expect(ProviderTransform.smallOptions(createModel(testCase.id))).toEqual(testCase.options)
4637
- })
4638
- }
4639
- })
4640
-
4641
- test("ProviderTransform.smallOptions disables OpenRouter reasoning when the weakest effort is low", () => {
4642
- expect(
4643
- ProviderTransform.smallOptions({
4644
- providerID: "openrouter",
4645
- api: {
4646
- id: "anthropic/claude-sonnet-4.6",
4647
- npm: "@openrouter/ai-sdk-provider",
4648
- },
4649
- variants: {
4650
- low: { reasoning: { effort: "low" } },
4651
- medium: { reasoning: { effort: "medium" } },
4652
- high: { reasoning: { effort: "high" } },
4653
- },
4654
- } as any),
4655
- ).toEqual({ reasoning: { effort: "none" } })
4656
- })
4657
-
4658
- describe("ProviderTransform.smallOptions - google thinking controls", () => {
4659
- const createGoogleModel = (apiId: string) => {
4660
- const model = {
4661
- id: `google/${apiId}`,
4662
- providerID: "google",
4663
- api: {
4664
- id: apiId,
4665
- url: "https://generativelanguage.googleapis.com",
4666
- npm: "@ai-sdk/google",
4667
- },
4668
- capabilities: { reasoning: true },
4669
- limit: { output: 64_000 },
4670
- } as any
4671
- model.variants = ProviderTransform.variants(model)
4672
- return model
4673
- }
4674
-
4675
- for (const testCase of [
4676
- { id: "gemini-3-pro-preview", options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "low" } } },
4677
- { id: "gemini-3-flash-preview", options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "minimal" } } },
4678
- {
4679
- id: "gemini-3.1-flash-image-preview",
4680
- options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "minimal" } },
4681
- },
4682
- { id: "gemini-3-pro-image-preview", options: { thinkingConfig: { includeThoughts: true, thinkingLevel: "high" } } },
4683
- { id: "gemini-2.5-pro", options: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } } },
4684
- { id: "gemini-2.5-flash", options: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } } },
4685
- ]) {
4686
- test(`${testCase.id} returns supported small thinking options`, () => {
4687
- expect(ProviderTransform.smallOptions(createGoogleModel(testCase.id))).toEqual(testCase.options)
4688
- })
4689
- }
4690
-
4691
- test("uses the first configured variant when available", () => {
4692
- expect(
4693
- ProviderTransform.smallOptions({
4694
- ...createGoogleModel("gemini-2.5-pro"),
4695
- variants: {
4696
- high: { thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } },
4697
- max: { thinkingConfig: { includeThoughts: true, thinkingBudget: 32768 } },
4698
- },
4699
- }),
4700
- ).toEqual({ thinkingConfig: { includeThoughts: true, thinkingBudget: 16000 } })
4701
- })
4702
-
4703
- test("does not synthesize thinking options when variants are empty", () => {
4704
- expect(ProviderTransform.smallOptions({ ...createGoogleModel("gemini-2.5-pro"), variants: {} })).toEqual({})
4705
- })
4706
- })
4707
-
4708
- describe("ProviderTransform.providerOptions - ai-gateway-provider", () => {
4709
- const createModel = (overrides: Partial<any> = {}) =>
4710
- ({
4711
- id: "cloudflare-ai-gateway/openai/gpt-5.4",
4712
- providerID: "cloudflare-ai-gateway",
4713
- api: {
4714
- id: "openai/gpt-5.4",
4715
- url: "https://gateway.ai.cloudflare.com/v1/compat",
4716
- npm: "ai-gateway-provider",
4717
- },
4718
- capabilities: {
4719
- temperature: false,
4720
- reasoning: true,
4721
- attachment: true,
4722
- toolcall: true,
4723
- input: { text: true, audio: false, image: true, video: false, pdf: true },
4724
- output: { text: true, audio: false, image: false, video: false, pdf: false },
4725
- interleaved: false,
4726
- },
4727
- cost: { input: 1, output: 1, cache: { read: 0, write: 0 } },
4728
- limit: { context: 1_000_000, output: 128_000 },
4729
- status: "active",
4730
- options: {},
4731
- headers: {},
4732
- release_date: "2026-03-05",
4733
- ...overrides,
4734
- }) as any
4735
-
4736
- test("routes options under openaiCompatible (the key @ai-sdk/openai-compatible reads)", () => {
4737
- // Regression: previously fell back to providerID="cloudflare-ai-gateway",
4738
- // which @ai-sdk/openai-compatible never reads, silently dropping reasoningEffort.
4739
- const result = ProviderTransform.providerOptions(createModel(), { reasoningEffort: "high" })
4740
- expect(result).toEqual({ openaiCompatible: { reasoningEffort: "high" } })
4741
- })
4742
- })