cerebras-cli 1.0.1 → 1.0.3

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 (313) hide show
  1. package/cerebras-cli-1.0.0.tgz +0 -0
  2. package/package.json +7 -88
  3. package/AGENTS.md +0 -27
  4. package/Dockerfile +0 -10
  5. package/bunfig.toml +0 -4
  6. package/parsers-config.ts +0 -239
  7. package/script/build.ts +0 -151
  8. package/script/postinstall.mjs +0 -122
  9. package/script/publish.ts +0 -256
  10. package/script/schema.ts +0 -47
  11. package/src/acp/README.md +0 -164
  12. package/src/acp/agent.ts +0 -812
  13. package/src/acp/session.ts +0 -70
  14. package/src/acp/types.ts +0 -22
  15. package/src/agent/agent.ts +0 -310
  16. package/src/agent/generate.txt +0 -75
  17. package/src/auth/index.ts +0 -70
  18. package/src/bun/index.ts +0 -152
  19. package/src/bus/global.ts +0 -10
  20. package/src/bus/index.ts +0 -142
  21. package/src/cli/bootstrap.ts +0 -17
  22. package/src/cli/cmd/acp.ts +0 -88
  23. package/src/cli/cmd/agent.ts +0 -165
  24. package/src/cli/cmd/auth.ts +0 -369
  25. package/src/cli/cmd/cmd.ts +0 -7
  26. package/src/cli/cmd/debug/config.ts +0 -15
  27. package/src/cli/cmd/debug/file.ts +0 -91
  28. package/src/cli/cmd/debug/index.ts +0 -41
  29. package/src/cli/cmd/debug/lsp.ts +0 -47
  30. package/src/cli/cmd/debug/ripgrep.ts +0 -83
  31. package/src/cli/cmd/debug/scrap.ts +0 -15
  32. package/src/cli/cmd/debug/snapshot.ts +0 -48
  33. package/src/cli/cmd/export.ts +0 -88
  34. package/src/cli/cmd/generate.ts +0 -38
  35. package/src/cli/cmd/github.ts +0 -1200
  36. package/src/cli/cmd/import.ts +0 -98
  37. package/src/cli/cmd/mcp.ts +0 -400
  38. package/src/cli/cmd/models.ts +0 -77
  39. package/src/cli/cmd/pr.ts +0 -112
  40. package/src/cli/cmd/run.ts +0 -342
  41. package/src/cli/cmd/serve.ts +0 -31
  42. package/src/cli/cmd/session.ts +0 -106
  43. package/src/cli/cmd/stats.ts +0 -298
  44. package/src/cli/cmd/tui/app.tsx +0 -732
  45. package/src/cli/cmd/tui/attach.ts +0 -25
  46. package/src/cli/cmd/tui/component/border.tsx +0 -21
  47. package/src/cli/cmd/tui/component/dialog-agent.tsx +0 -31
  48. package/src/cli/cmd/tui/component/dialog-command.tsx +0 -124
  49. package/src/cli/cmd/tui/component/dialog-feedback.tsx +0 -160
  50. package/src/cli/cmd/tui/component/dialog-mcp.tsx +0 -86
  51. package/src/cli/cmd/tui/component/dialog-model.tsx +0 -223
  52. package/src/cli/cmd/tui/component/dialog-notification.tsx +0 -78
  53. package/src/cli/cmd/tui/component/dialog-provider.tsx +0 -222
  54. package/src/cli/cmd/tui/component/dialog-session-list.tsx +0 -97
  55. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +0 -31
  56. package/src/cli/cmd/tui/component/dialog-status.tsx +0 -114
  57. package/src/cli/cmd/tui/component/dialog-tag.tsx +0 -44
  58. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +0 -50
  59. package/src/cli/cmd/tui/component/logo.tsx +0 -37
  60. package/src/cli/cmd/tui/component/notification-banner.tsx +0 -58
  61. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +0 -530
  62. package/src/cli/cmd/tui/component/prompt/history.tsx +0 -107
  63. package/src/cli/cmd/tui/component/prompt/index.tsx +0 -931
  64. package/src/cli/cmd/tui/context/args.tsx +0 -14
  65. package/src/cli/cmd/tui/context/directory.ts +0 -12
  66. package/src/cli/cmd/tui/context/exit.tsx +0 -23
  67. package/src/cli/cmd/tui/context/helper.tsx +0 -25
  68. package/src/cli/cmd/tui/context/keybind.tsx +0 -111
  69. package/src/cli/cmd/tui/context/kv.tsx +0 -49
  70. package/src/cli/cmd/tui/context/local.tsx +0 -339
  71. package/src/cli/cmd/tui/context/prompt.tsx +0 -18
  72. package/src/cli/cmd/tui/context/route.tsx +0 -45
  73. package/src/cli/cmd/tui/context/sdk.tsx +0 -75
  74. package/src/cli/cmd/tui/context/sync.tsx +0 -374
  75. package/src/cli/cmd/tui/context/theme/aura.json +0 -69
  76. package/src/cli/cmd/tui/context/theme/ayu.json +0 -80
  77. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +0 -233
  78. package/src/cli/cmd/tui/context/theme/catppuccin.json +0 -112
  79. package/src/cli/cmd/tui/context/theme/cobalt2.json +0 -228
  80. package/src/cli/cmd/tui/context/theme/dracula.json +0 -219
  81. package/src/cli/cmd/tui/context/theme/everforest.json +0 -241
  82. package/src/cli/cmd/tui/context/theme/flexoki.json +0 -237
  83. package/src/cli/cmd/tui/context/theme/github.json +0 -233
  84. package/src/cli/cmd/tui/context/theme/gruvbox.json +0 -95
  85. package/src/cli/cmd/tui/context/theme/kanagawa.json +0 -77
  86. package/src/cli/cmd/tui/context/theme/material.json +0 -235
  87. package/src/cli/cmd/tui/context/theme/matrix.json +0 -77
  88. package/src/cli/cmd/tui/context/theme/mercury.json +0 -252
  89. package/src/cli/cmd/tui/context/theme/monokai.json +0 -221
  90. package/src/cli/cmd/tui/context/theme/nightowl.json +0 -221
  91. package/src/cli/cmd/tui/context/theme/nord.json +0 -223
  92. package/src/cli/cmd/tui/context/theme/one-dark.json +0 -84
  93. package/src/cli/cmd/tui/context/theme/orng.json +0 -245
  94. package/src/cli/cmd/tui/context/theme/palenight.json +0 -222
  95. package/src/cli/cmd/tui/context/theme/rosepine.json +0 -234
  96. package/src/cli/cmd/tui/context/theme/solarized.json +0 -223
  97. package/src/cli/cmd/tui/context/theme/synthwave84.json +0 -226
  98. package/src/cli/cmd/tui/context/theme/tokyonight.json +0 -243
  99. package/src/cli/cmd/tui/context/theme/vercel.json +0 -245
  100. package/src/cli/cmd/tui/context/theme/vesper.json +0 -218
  101. package/src/cli/cmd/tui/context/theme/zenburn.json +0 -223
  102. package/src/cli/cmd/tui/context/theme.tsx +0 -1077
  103. package/src/cli/cmd/tui/event.ts +0 -39
  104. package/src/cli/cmd/tui/routes/home.tsx +0 -104
  105. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +0 -93
  106. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +0 -37
  107. package/src/cli/cmd/tui/routes/session/footer.tsx +0 -76
  108. package/src/cli/cmd/tui/routes/session/header.tsx +0 -183
  109. package/src/cli/cmd/tui/routes/session/index.tsx +0 -1703
  110. package/src/cli/cmd/tui/routes/session/sidebar.tsx +0 -586
  111. package/src/cli/cmd/tui/spawn.ts +0 -60
  112. package/src/cli/cmd/tui/thread.ts +0 -120
  113. package/src/cli/cmd/tui/ui/dialog-alert.tsx +0 -55
  114. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +0 -81
  115. package/src/cli/cmd/tui/ui/dialog-help.tsx +0 -36
  116. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +0 -75
  117. package/src/cli/cmd/tui/ui/dialog-select.tsx +0 -317
  118. package/src/cli/cmd/tui/ui/dialog.tsx +0 -170
  119. package/src/cli/cmd/tui/ui/spinner.ts +0 -368
  120. package/src/cli/cmd/tui/ui/toast.tsx +0 -100
  121. package/src/cli/cmd/tui/util/clipboard.ts +0 -127
  122. package/src/cli/cmd/tui/util/editor.ts +0 -32
  123. package/src/cli/cmd/tui/util/terminal.ts +0 -114
  124. package/src/cli/cmd/tui/worker.ts +0 -63
  125. package/src/cli/cmd/uninstall.ts +0 -344
  126. package/src/cli/cmd/upgrade.ts +0 -67
  127. package/src/cli/cmd/web.ts +0 -84
  128. package/src/cli/error.ts +0 -55
  129. package/src/cli/ui.ts +0 -84
  130. package/src/cli/upgrade.ts +0 -25
  131. package/src/command/index.ts +0 -79
  132. package/src/command/template/initialize.txt +0 -10
  133. package/src/command/template/review.txt +0 -73
  134. package/src/config/config.ts +0 -886
  135. package/src/config/markdown.ts +0 -41
  136. package/src/env/index.ts +0 -26
  137. package/src/file/fzf.ts +0 -124
  138. package/src/file/ignore.ts +0 -83
  139. package/src/file/index.ts +0 -326
  140. package/src/file/ripgrep.ts +0 -391
  141. package/src/file/time.ts +0 -38
  142. package/src/file/watcher.ts +0 -89
  143. package/src/flag/flag.ts +0 -28
  144. package/src/format/formatter.ts +0 -277
  145. package/src/format/index.ts +0 -137
  146. package/src/global/index.ts +0 -52
  147. package/src/id/id.ts +0 -73
  148. package/src/ide/index.ts +0 -75
  149. package/src/index.ts +0 -158
  150. package/src/installation/index.ts +0 -194
  151. package/src/lsp/client.ts +0 -215
  152. package/src/lsp/index.ts +0 -370
  153. package/src/lsp/language.ts +0 -111
  154. package/src/lsp/server.ts +0 -1327
  155. package/src/mcp/auth.ts +0 -82
  156. package/src/mcp/index.ts +0 -576
  157. package/src/mcp/oauth-callback.ts +0 -203
  158. package/src/mcp/oauth-provider.ts +0 -132
  159. package/src/notification/index.ts +0 -101
  160. package/src/patch/index.ts +0 -622
  161. package/src/permission/index.ts +0 -198
  162. package/src/plugin/index.ts +0 -95
  163. package/src/project/bootstrap.ts +0 -31
  164. package/src/project/instance.ts +0 -68
  165. package/src/project/project.ts +0 -133
  166. package/src/project/state.ts +0 -65
  167. package/src/project/vcs.ts +0 -77
  168. package/src/provider/auth.ts +0 -143
  169. package/src/provider/models-macro.ts +0 -11
  170. package/src/provider/models.ts +0 -93
  171. package/src/provider/provider.ts +0 -996
  172. package/src/provider/sdk/openai-compatible/src/README.md +0 -5
  173. package/src/provider/sdk/openai-compatible/src/index.ts +0 -2
  174. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +0 -100
  175. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +0 -303
  176. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +0 -27
  177. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +0 -18
  178. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +0 -22
  179. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +0 -207
  180. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +0 -1713
  181. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +0 -177
  182. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +0 -1
  183. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +0 -88
  184. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +0 -128
  185. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +0 -115
  186. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +0 -65
  187. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +0 -104
  188. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +0 -103
  189. package/src/provider/transform.ts +0 -406
  190. package/src/pty/index.ts +0 -226
  191. package/src/ratelimit/index.ts +0 -185
  192. package/src/server/error.ts +0 -36
  193. package/src/server/project.ts +0 -50
  194. package/src/server/server.ts +0 -2463
  195. package/src/server/tui.ts +0 -71
  196. package/src/session/compaction.ts +0 -257
  197. package/src/session/index.ts +0 -470
  198. package/src/session/message-v2.ts +0 -641
  199. package/src/session/message.ts +0 -189
  200. package/src/session/processor.ts +0 -443
  201. package/src/session/prompt/anthropic-20250930.txt +0 -166
  202. package/src/session/prompt/anthropic.txt +0 -105
  203. package/src/session/prompt/anthropic_spoof.txt +0 -1
  204. package/src/session/prompt/beast.txt +0 -147
  205. package/src/session/prompt/build-switch.txt +0 -5
  206. package/src/session/prompt/codex.txt +0 -318
  207. package/src/session/prompt/compaction.txt +0 -12
  208. package/src/session/prompt/copilot-gpt-5.txt +0 -143
  209. package/src/session/prompt/gemini.txt +0 -155
  210. package/src/session/prompt/max-steps.txt +0 -16
  211. package/src/session/prompt/plan-reminder-anthropic.txt +0 -67
  212. package/src/session/prompt/plan.txt +0 -26
  213. package/src/session/prompt/polaris.txt +0 -107
  214. package/src/session/prompt/qwen.txt +0 -109
  215. package/src/session/prompt/summarize.txt +0 -4
  216. package/src/session/prompt/title.txt +0 -36
  217. package/src/session/prompt.ts +0 -1541
  218. package/src/session/retry.ts +0 -82
  219. package/src/session/revert.ts +0 -108
  220. package/src/session/status.ts +0 -75
  221. package/src/session/summary.ts +0 -203
  222. package/src/session/system.ts +0 -148
  223. package/src/session/todo.ts +0 -36
  224. package/src/share/share-next.ts +0 -195
  225. package/src/share/share.ts +0 -87
  226. package/src/snapshot/index.ts +0 -197
  227. package/src/storage/storage.ts +0 -226
  228. package/src/telemetry/index.ts +0 -232
  229. package/src/tool/bash.ts +0 -365
  230. package/src/tool/bash.txt +0 -128
  231. package/src/tool/batch.ts +0 -173
  232. package/src/tool/batch.txt +0 -28
  233. package/src/tool/codesearch.ts +0 -138
  234. package/src/tool/codesearch.txt +0 -12
  235. package/src/tool/edit.ts +0 -674
  236. package/src/tool/edit.txt +0 -10
  237. package/src/tool/glob.ts +0 -65
  238. package/src/tool/glob.txt +0 -6
  239. package/src/tool/grep.ts +0 -120
  240. package/src/tool/grep.txt +0 -8
  241. package/src/tool/invalid.ts +0 -17
  242. package/src/tool/ls.ts +0 -110
  243. package/src/tool/ls.txt +0 -1
  244. package/src/tool/lsp-diagnostics.ts +0 -26
  245. package/src/tool/lsp-diagnostics.txt +0 -1
  246. package/src/tool/lsp-hover.ts +0 -31
  247. package/src/tool/lsp-hover.txt +0 -1
  248. package/src/tool/multiedit.ts +0 -46
  249. package/src/tool/multiedit.txt +0 -41
  250. package/src/tool/patch.ts +0 -233
  251. package/src/tool/patch.txt +0 -1
  252. package/src/tool/read.ts +0 -217
  253. package/src/tool/read.txt +0 -12
  254. package/src/tool/registry.ts +0 -148
  255. package/src/tool/task.ts +0 -135
  256. package/src/tool/task.txt +0 -60
  257. package/src/tool/todo.ts +0 -39
  258. package/src/tool/todoread.txt +0 -14
  259. package/src/tool/todowrite.txt +0 -167
  260. package/src/tool/tool.ts +0 -66
  261. package/src/tool/webfetch.ts +0 -187
  262. package/src/tool/webfetch.txt +0 -14
  263. package/src/tool/websearch.ts +0 -150
  264. package/src/tool/websearch.txt +0 -11
  265. package/src/tool/write.ts +0 -99
  266. package/src/tool/write.txt +0 -8
  267. package/src/types/shims.d.ts +0 -3
  268. package/src/util/color.ts +0 -19
  269. package/src/util/context.ts +0 -25
  270. package/src/util/defer.ts +0 -12
  271. package/src/util/eventloop.ts +0 -20
  272. package/src/util/filesystem.ts +0 -69
  273. package/src/util/fn.ts +0 -11
  274. package/src/util/iife.ts +0 -3
  275. package/src/util/keybind.ts +0 -79
  276. package/src/util/lazy.ts +0 -11
  277. package/src/util/locale.ts +0 -81
  278. package/src/util/lock.ts +0 -98
  279. package/src/util/log.ts +0 -177
  280. package/src/util/queue.ts +0 -32
  281. package/src/util/rpc.ts +0 -42
  282. package/src/util/scrap.ts +0 -10
  283. package/src/util/signal.ts +0 -12
  284. package/src/util/timeout.ts +0 -14
  285. package/src/util/token.ts +0 -7
  286. package/src/util/wildcard.ts +0 -54
  287. package/sst-env.d.ts +0 -9
  288. package/test/bun.test.ts +0 -53
  289. package/test/config/agent-color.test.ts +0 -66
  290. package/test/config/config.test.ts +0 -503
  291. package/test/config/markdown.test.ts +0 -89
  292. package/test/file/ignore.test.ts +0 -10
  293. package/test/fixture/fixture.ts +0 -28
  294. package/test/fixture/lsp/fake-lsp-server.js +0 -77
  295. package/test/ide/ide.test.ts +0 -82
  296. package/test/keybind.test.ts +0 -317
  297. package/test/lsp/client.test.ts +0 -95
  298. package/test/patch/patch.test.ts +0 -348
  299. package/test/preload.ts +0 -38
  300. package/test/project/project.test.ts +0 -42
  301. package/test/provider/provider.test.ts +0 -1809
  302. package/test/provider/transform.test.ts +0 -305
  303. package/test/session/retry.test.ts +0 -61
  304. package/test/session/session.test.ts +0 -71
  305. package/test/snapshot/snapshot.test.ts +0 -939
  306. package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
  307. package/test/tool/bash.test.ts +0 -55
  308. package/test/tool/patch.test.ts +0 -259
  309. package/test/util/iife.test.ts +0 -36
  310. package/test/util/lazy.test.ts +0 -50
  311. package/test/util/timeout.test.ts +0 -21
  312. package/test/util/wildcard.test.ts +0 -55
  313. package/tsconfig.json +0 -17
@@ -1,996 +0,0 @@
1
- import z from "zod"
2
- import fuzzysort from "fuzzysort"
3
- import { Config } from "../config/config"
4
- import { mapValues, mergeDeep, sortBy } from "remeda"
5
- import { NoSuchModelError, type Provider as SDK } from "ai"
6
- import { Log } from "../util/log"
7
- import { BunProc } from "../bun"
8
- import { Plugin } from "../plugin"
9
- import { ModelsDev } from "./models"
10
- import { NamedError } from "@opencode-ai/util/error"
11
- import { Auth } from "../auth"
12
- import { Env } from "../env"
13
- import { Instance } from "../project/instance"
14
- import { Flag } from "../flag/flag"
15
- import { iife } from "@/util/iife"
16
- import { RateLimit } from "@/ratelimit"
17
-
18
- // Direct imports for bundled providers
19
- import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock"
20
- import { createAnthropic } from "@ai-sdk/anthropic"
21
- import { createAzure } from "@ai-sdk/azure"
22
- import { createGoogleGenerativeAI } from "@ai-sdk/google"
23
- import { createVertex } from "@ai-sdk/google-vertex"
24
- import { createVertexAnthropic } from "@ai-sdk/google-vertex/anthropic"
25
- import { createOpenAI } from "@ai-sdk/openai"
26
- import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
27
- import { createOpenRouter, type LanguageModelV2 } from "@openrouter/ai-sdk-provider"
28
- import { createOpenaiCompatible as createGitHubCopilotOpenAICompatible } from "./sdk/openai-compatible/src"
29
-
30
- export namespace Provider {
31
- const log = Log.create({ service: "provider" })
32
-
33
- const BUNDLED_PROVIDERS: Record<string, (options: any) => SDK> = {
34
- "@ai-sdk/amazon-bedrock": createAmazonBedrock,
35
- "@ai-sdk/anthropic": createAnthropic,
36
- "@ai-sdk/azure": createAzure,
37
- "@ai-sdk/google": createGoogleGenerativeAI,
38
- "@ai-sdk/google-vertex": createVertex,
39
- "@ai-sdk/google-vertex/anthropic": createVertexAnthropic,
40
- "@ai-sdk/openai": createOpenAI,
41
- "@ai-sdk/openai-compatible": createOpenAICompatible,
42
- "@openrouter/ai-sdk-provider": createOpenRouter,
43
- // @ts-ignore (TODO: kill this code so we dont have to maintain it)
44
- "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible,
45
- // Use OpenAI-compatible SDK for Cerebras to handle response format differences
46
- "@ai-sdk/cerebras": createOpenAICompatible,
47
- }
48
-
49
- type CustomModelLoader = (sdk: any, modelID: string, options?: Record<string, any>) => Promise<any>
50
- type CustomLoader = (provider: Info) => Promise<{
51
- autoload: boolean
52
- getModel?: CustomModelLoader
53
- options?: Record<string, any>
54
- }>
55
-
56
- const CUSTOM_LOADERS: Record<string, CustomLoader> = {
57
- async anthropic() {
58
- return {
59
- autoload: false,
60
- options: {
61
- headers: {
62
- "anthropic-beta":
63
- "claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
64
- },
65
- },
66
- }
67
- },
68
- async opencode(input) {
69
- const hasKey = await (async () => {
70
- const env = Env.all()
71
- if (input.env.some((item) => env[item])) return true
72
- if (await Auth.get(input.id)) return true
73
- return false
74
- })()
75
-
76
- if (!hasKey) {
77
- for (const [key, value] of Object.entries(input.models)) {
78
- if (value.cost.input === 0) continue
79
- delete input.models[key]
80
- }
81
- }
82
-
83
- return {
84
- autoload: Object.keys(input.models).length > 0,
85
- options: hasKey ? {} : { apiKey: "public" },
86
- }
87
- },
88
- openai: async () => {
89
- return {
90
- autoload: false,
91
- async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
92
- return sdk.responses(modelID)
93
- },
94
- options: {},
95
- }
96
- },
97
- cerebras: async () => {
98
- return {
99
- autoload: false,
100
- options: {
101
- baseURL: "https://api.cerebras.ai/v1",
102
- },
103
- }
104
- },
105
- "github-copilot": async () => {
106
- return {
107
- autoload: false,
108
- async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
109
- if (modelID.includes("codex")) {
110
- return sdk.responses(modelID)
111
- }
112
- return sdk.chat(modelID)
113
- },
114
- options: {},
115
- }
116
- },
117
- "github-copilot-enterprise": async () => {
118
- return {
119
- autoload: false,
120
- async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
121
- if (modelID.includes("codex")) {
122
- return sdk.responses(modelID)
123
- }
124
- return sdk.chat(modelID)
125
- },
126
- options: {},
127
- }
128
- },
129
- azure: async () => {
130
- return {
131
- autoload: false,
132
- async getModel(sdk: any, modelID: string, options?: Record<string, any>) {
133
- if (options?.["useCompletionUrls"]) {
134
- return sdk.chat(modelID)
135
- } else {
136
- return sdk.responses(modelID)
137
- }
138
- },
139
- options: {},
140
- }
141
- },
142
- "azure-cognitive-services": async () => {
143
- const resourceName = Env.get("AZURE_COGNITIVE_SERVICES_RESOURCE_NAME")
144
- return {
145
- autoload: false,
146
- async getModel(sdk: any, modelID: string, options?: Record<string, any>) {
147
- if (options?.["useCompletionUrls"]) {
148
- return sdk.chat(modelID)
149
- } else {
150
- return sdk.responses(modelID)
151
- }
152
- },
153
- options: {
154
- baseURL: resourceName ? `https://${resourceName}.cognitiveservices.azure.com/openai` : undefined,
155
- },
156
- }
157
- },
158
- "amazon-bedrock": async () => {
159
- const [awsProfile, awsAccessKeyId, awsBearerToken, awsRegion] = await Promise.all([
160
- Env.get("AWS_PROFILE"),
161
- Env.get("AWS_ACCESS_KEY_ID"),
162
- Env.get("AWS_BEARER_TOKEN_BEDROCK"),
163
- Env.get("AWS_REGION"),
164
- ])
165
- if (!awsProfile && !awsAccessKeyId && !awsBearerToken) return { autoload: false }
166
-
167
- const region = awsRegion ?? "us-east-1"
168
-
169
- const { fromNodeProviderChain } = await import(await BunProc.install("@aws-sdk/credential-providers"))
170
- return {
171
- autoload: true,
172
- options: {
173
- region,
174
- credentialProvider: fromNodeProviderChain(),
175
- },
176
- async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
177
- // Skip region prefixing if model already has global prefix
178
- if (modelID.startsWith("global.")) {
179
- return sdk.languageModel(modelID)
180
- }
181
-
182
- let regionPrefix = region.split("-")[0]
183
-
184
- switch (regionPrefix) {
185
- case "us": {
186
- const modelRequiresPrefix = [
187
- "nova-micro",
188
- "nova-lite",
189
- "nova-pro",
190
- "nova-premier",
191
- "claude",
192
- "deepseek",
193
- ].some((m) => modelID.includes(m))
194
- const isGovCloud = region.startsWith("us-gov")
195
- if (modelRequiresPrefix && !isGovCloud) {
196
- modelID = `${regionPrefix}.${modelID}`
197
- }
198
- break
199
- }
200
- case "eu": {
201
- const regionRequiresPrefix = [
202
- "eu-west-1",
203
- "eu-west-2",
204
- "eu-west-3",
205
- "eu-north-1",
206
- "eu-central-1",
207
- "eu-south-1",
208
- "eu-south-2",
209
- ].some((r) => region.includes(r))
210
- const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "llama3", "pixtral"].some((m) =>
211
- modelID.includes(m),
212
- )
213
- if (regionRequiresPrefix && modelRequiresPrefix) {
214
- modelID = `${regionPrefix}.${modelID}`
215
- }
216
- break
217
- }
218
- case "ap": {
219
- const isAustraliaRegion = ["ap-southeast-2", "ap-southeast-4"].includes(region)
220
- if (
221
- isAustraliaRegion &&
222
- ["anthropic.claude-sonnet-4-5", "anthropic.claude-haiku"].some((m) => modelID.includes(m))
223
- ) {
224
- regionPrefix = "au"
225
- modelID = `${regionPrefix}.${modelID}`
226
- } else {
227
- const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "nova-pro"].some((m) =>
228
- modelID.includes(m),
229
- )
230
- if (modelRequiresPrefix) {
231
- regionPrefix = "apac"
232
- modelID = `${regionPrefix}.${modelID}`
233
- }
234
- }
235
- break
236
- }
237
- }
238
-
239
- return sdk.languageModel(modelID)
240
- },
241
- }
242
- },
243
- openrouter: async () => {
244
- return {
245
- autoload: false,
246
- options: {
247
- headers: {
248
- "HTTP-Referer": "https://opencode.ai/",
249
- "X-Title": "opencode",
250
- },
251
- },
252
- }
253
- },
254
- vercel: async () => {
255
- return {
256
- autoload: false,
257
- options: {
258
- headers: {
259
- "http-referer": "https://opencode.ai/",
260
- "x-title": "opencode",
261
- },
262
- },
263
- }
264
- },
265
- "google-vertex": async () => {
266
- const project = Env.get("GOOGLE_CLOUD_PROJECT") ?? Env.get("GCP_PROJECT") ?? Env.get("GCLOUD_PROJECT")
267
- const location = Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "us-east5"
268
- const autoload = Boolean(project)
269
- if (!autoload) return { autoload: false }
270
- return {
271
- autoload: true,
272
- options: {
273
- project,
274
- location,
275
- },
276
- async getModel(sdk: any, modelID: string) {
277
- const id = String(modelID).trim()
278
- return sdk.languageModel(id)
279
- },
280
- }
281
- },
282
- "google-vertex-anthropic": async () => {
283
- const project = Env.get("GOOGLE_CLOUD_PROJECT") ?? Env.get("GCP_PROJECT") ?? Env.get("GCLOUD_PROJECT")
284
- const location = Env.get("GOOGLE_CLOUD_LOCATION") ?? Env.get("VERTEX_LOCATION") ?? "global"
285
- const autoload = Boolean(project)
286
- if (!autoload) return { autoload: false }
287
- return {
288
- autoload: true,
289
- options: {
290
- project,
291
- location,
292
- },
293
- async getModel(sdk: any, modelID) {
294
- const id = String(modelID).trim()
295
- return sdk.languageModel(id)
296
- },
297
- }
298
- },
299
- "sap-ai-core": async () => {
300
- const auth = await Auth.get("sap-ai-core")
301
- const envServiceKey = iife(() => {
302
- const envAICoreServiceKey = Env.get("AICORE_SERVICE_KEY")
303
- if (envAICoreServiceKey) return envAICoreServiceKey
304
- if (auth?.type === "api") {
305
- Env.set("AICORE_SERVICE_KEY", auth.key)
306
- return auth.key
307
- }
308
- return undefined
309
- })
310
- const deploymentId = Env.get("AICORE_DEPLOYMENT_ID")
311
- const resourceGroup = Env.get("AICORE_RESOURCE_GROUP")
312
-
313
- return {
314
- autoload: !!envServiceKey,
315
- options: envServiceKey ? { deploymentId, resourceGroup } : {},
316
- async getModel(sdk: any, modelID: string) {
317
- return sdk(modelID)
318
- },
319
- }
320
- },
321
- zenmux: async () => {
322
- return {
323
- autoload: false,
324
- options: {
325
- headers: {
326
- "HTTP-Referer": "https://opencode.ai/",
327
- "X-Title": "opencode",
328
- },
329
- },
330
- }
331
- },
332
- }
333
-
334
- export const Model = z
335
- .object({
336
- id: z.string(),
337
- providerID: z.string(),
338
- api: z.object({
339
- id: z.string(),
340
- url: z.string(),
341
- npm: z.string(),
342
- }),
343
- name: z.string(),
344
- capabilities: z.object({
345
- temperature: z.boolean(),
346
- reasoning: z.boolean(),
347
- attachment: z.boolean(),
348
- toolcall: z.boolean(),
349
- input: z.object({
350
- text: z.boolean(),
351
- audio: z.boolean(),
352
- image: z.boolean(),
353
- video: z.boolean(),
354
- pdf: z.boolean(),
355
- }),
356
- output: z.object({
357
- text: z.boolean(),
358
- audio: z.boolean(),
359
- image: z.boolean(),
360
- video: z.boolean(),
361
- pdf: z.boolean(),
362
- }),
363
- }),
364
- cost: z.object({
365
- input: z.number(),
366
- output: z.number(),
367
- cache: z.object({
368
- read: z.number(),
369
- write: z.number(),
370
- }),
371
- experimentalOver200K: z
372
- .object({
373
- input: z.number(),
374
- output: z.number(),
375
- cache: z.object({
376
- read: z.number(),
377
- write: z.number(),
378
- }),
379
- })
380
- .optional(),
381
- }),
382
- limit: z.object({
383
- context: z.number(),
384
- output: z.number(),
385
- }),
386
- status: z.enum(["alpha", "beta", "deprecated", "active"]),
387
- options: z.record(z.string(), z.any()),
388
- headers: z.record(z.string(), z.string()),
389
- })
390
- .meta({
391
- ref: "Model",
392
- })
393
- export type Model = z.infer<typeof Model>
394
-
395
- export const Info = z
396
- .object({
397
- id: z.string(),
398
- name: z.string(),
399
- source: z.enum(["env", "config", "custom", "api"]),
400
- env: z.string().array(),
401
- key: z.string().optional(),
402
- options: z.record(z.string(), z.any()),
403
- models: z.record(z.string(), Model),
404
- })
405
- .meta({
406
- ref: "Provider",
407
- })
408
- export type Info = z.infer<typeof Info>
409
-
410
- function fromModelsDevModel(provider: ModelsDev.Provider, model: ModelsDev.Model): Model {
411
- return {
412
- id: model.id,
413
- providerID: provider.id,
414
- name: model.name,
415
- api: {
416
- id: model.id,
417
- url: provider.api!,
418
- npm: model.provider?.npm ?? provider.npm ?? provider.id,
419
- },
420
- status: model.status ?? "active",
421
- headers: model.headers ?? {},
422
- options: model.options ?? {},
423
- cost: {
424
- input: model.cost?.input ?? 0,
425
- output: model.cost?.output ?? 0,
426
- cache: {
427
- read: model.cost?.cache_read ?? 0,
428
- write: model.cost?.cache_write ?? 0,
429
- },
430
- experimentalOver200K: model.cost?.context_over_200k
431
- ? {
432
- cache: {
433
- read: model.cost.context_over_200k.cache_read ?? 0,
434
- write: model.cost.context_over_200k.cache_write ?? 0,
435
- },
436
- input: model.cost.context_over_200k.input,
437
- output: model.cost.context_over_200k.output,
438
- }
439
- : undefined,
440
- },
441
- limit: {
442
- context: model.limit.context,
443
- output: model.limit.output,
444
- },
445
- capabilities: {
446
- temperature: model.temperature,
447
- reasoning: model.reasoning,
448
- attachment: model.attachment,
449
- toolcall: model.tool_call,
450
- input: {
451
- text: model.modalities?.input?.includes("text") ?? false,
452
- audio: model.modalities?.input?.includes("audio") ?? false,
453
- image: model.modalities?.input?.includes("image") ?? false,
454
- video: model.modalities?.input?.includes("video") ?? false,
455
- pdf: model.modalities?.input?.includes("pdf") ?? false,
456
- },
457
- output: {
458
- text: model.modalities?.output?.includes("text") ?? false,
459
- audio: model.modalities?.output?.includes("audio") ?? false,
460
- image: model.modalities?.output?.includes("image") ?? false,
461
- video: model.modalities?.output?.includes("video") ?? false,
462
- pdf: model.modalities?.output?.includes("pdf") ?? false,
463
- },
464
- },
465
- }
466
- }
467
-
468
- export function fromModelsDevProvider(provider: ModelsDev.Provider): Info {
469
- return {
470
- id: provider.id,
471
- source: "custom",
472
- name: provider.name,
473
- env: provider.env ?? [],
474
- options: {},
475
- models: mapValues(provider.models, (model) => fromModelsDevModel(provider, model)),
476
- }
477
- }
478
-
479
- const state = Instance.state(async () => {
480
- using _ = log.time("state")
481
- const config = await Config.get()
482
- const modelsDev = await ModelsDev.get()
483
- const database = mapValues(modelsDev, fromModelsDevProvider)
484
-
485
- const disabled = new Set(config.disabled_providers ?? [])
486
- const enabled = config.enabled_providers ? new Set(config.enabled_providers) : null
487
-
488
- function isProviderAllowed(providerID: string): boolean {
489
- if (enabled && !enabled.has(providerID)) return false
490
- if (disabled.has(providerID)) return false
491
- return true
492
- }
493
-
494
- const providers: { [providerID: string]: Info } = {}
495
- const languages = new Map<string, LanguageModelV2>()
496
- const modelLoaders: {
497
- [providerID: string]: CustomModelLoader
498
- } = {}
499
- const sdk = new Map<number, SDK>()
500
-
501
- log.info("init")
502
-
503
- const configProviders = Object.entries(config.provider ?? {})
504
-
505
- // Add GitHub Copilot Enterprise provider that inherits from GitHub Copilot
506
- if (database["github-copilot"]) {
507
- const githubCopilot = database["github-copilot"]
508
- database["github-copilot-enterprise"] = {
509
- ...githubCopilot,
510
- id: "github-copilot-enterprise",
511
- name: "GitHub Copilot Enterprise",
512
- models: mapValues(githubCopilot.models, (model) => ({
513
- ...model,
514
- providerID: "github-copilot-enterprise",
515
- })),
516
- }
517
- }
518
-
519
- function mergeProvider(providerID: string, provider: Partial<Info>) {
520
- const existing = providers[providerID]
521
- if (existing) {
522
- // @ts-expect-error
523
- providers[providerID] = mergeDeep(existing, provider)
524
- return
525
- }
526
- const match = database[providerID]
527
- if (!match) return
528
- // @ts-expect-error
529
- providers[providerID] = mergeDeep(match, provider)
530
- }
531
-
532
- // extend database from config
533
- for (const [providerID, provider] of configProviders) {
534
- const existing = database[providerID]
535
- const parsed: Info = {
536
- id: providerID,
537
- name: provider.name ?? existing?.name ?? providerID,
538
- env: provider.env ?? existing?.env ?? [],
539
- options: mergeDeep(existing?.options ?? {}, provider.options ?? {}),
540
- source: "config",
541
- models: existing?.models ?? {},
542
- }
543
-
544
- for (const [modelID, model] of Object.entries(provider.models ?? {})) {
545
- const existingModel = parsed.models[model.id ?? modelID]
546
- const name = iife(() => {
547
- if (model.name) return model.name
548
- if (model.id && model.id !== modelID) return modelID
549
- return existingModel?.name ?? modelID
550
- })
551
- const parsedModel: Model = {
552
- id: modelID,
553
- api: {
554
- id: model.id ?? existingModel?.api.id ?? modelID,
555
- npm:
556
- model.provider?.npm ?? provider.npm ?? existingModel?.api.npm ?? modelsDev[providerID]?.npm ?? providerID,
557
- url: provider?.api ?? existingModel?.api.url ?? modelsDev[providerID]?.api,
558
- },
559
- status: model.status ?? existingModel?.status ?? "active",
560
- name,
561
- providerID,
562
- capabilities: {
563
- temperature: model.temperature ?? existingModel?.capabilities.temperature ?? false,
564
- reasoning: model.reasoning ?? existingModel?.capabilities.reasoning ?? false,
565
- attachment: model.attachment ?? existingModel?.capabilities.attachment ?? false,
566
- toolcall: model.tool_call ?? existingModel?.capabilities.toolcall ?? true,
567
- input: {
568
- text: model.modalities?.input?.includes("text") ?? existingModel?.capabilities.input.text ?? true,
569
- audio: model.modalities?.input?.includes("audio") ?? existingModel?.capabilities.input.audio ?? false,
570
- image: model.modalities?.input?.includes("image") ?? existingModel?.capabilities.input.image ?? false,
571
- video: model.modalities?.input?.includes("video") ?? existingModel?.capabilities.input.video ?? false,
572
- pdf: model.modalities?.input?.includes("pdf") ?? existingModel?.capabilities.input.pdf ?? false,
573
- },
574
- output: {
575
- text: model.modalities?.output?.includes("text") ?? existingModel?.capabilities.output.text ?? true,
576
- audio: model.modalities?.output?.includes("audio") ?? existingModel?.capabilities.output.audio ?? false,
577
- image: model.modalities?.output?.includes("image") ?? existingModel?.capabilities.output.image ?? false,
578
- video: model.modalities?.output?.includes("video") ?? existingModel?.capabilities.output.video ?? false,
579
- pdf: model.modalities?.output?.includes("pdf") ?? existingModel?.capabilities.output.pdf ?? false,
580
- },
581
- },
582
- cost: {
583
- input: model?.cost?.input ?? existingModel?.cost?.input ?? 0,
584
- output: model?.cost?.output ?? existingModel?.cost?.output ?? 0,
585
- cache: {
586
- read: model?.cost?.cache_read ?? existingModel?.cost?.cache.read ?? 0,
587
- write: model?.cost?.cache_write ?? existingModel?.cost?.cache.write ?? 0,
588
- },
589
- },
590
- options: mergeDeep(existingModel?.options ?? {}, model.options ?? {}),
591
- limit: {
592
- context: model.limit?.context ?? existingModel?.limit?.context ?? 0,
593
- output: model.limit?.output ?? existingModel?.limit?.output ?? 0,
594
- },
595
- headers: mergeDeep(existingModel?.headers ?? {}, model.headers ?? {}),
596
- }
597
- parsed.models[modelID] = parsedModel
598
- }
599
- database[providerID] = parsed
600
- }
601
-
602
- // load env
603
- const env = Env.all()
604
- for (const [providerID, provider] of Object.entries(database)) {
605
- if (disabled.has(providerID)) continue
606
- const apiKey = provider.env.map((item) => env[item]).find(Boolean)
607
- if (!apiKey) continue
608
- mergeProvider(providerID, {
609
- source: "env",
610
- key: provider.env.length === 1 ? apiKey : undefined,
611
- })
612
- }
613
-
614
- // load apikeys
615
- for (const [providerID, provider] of Object.entries(await Auth.all())) {
616
- if (disabled.has(providerID)) continue
617
- if (provider.type === "api") {
618
- mergeProvider(providerID, {
619
- source: "api",
620
- key: provider.key,
621
- })
622
- }
623
- }
624
-
625
- for (const plugin of await Plugin.list()) {
626
- if (!plugin.auth) continue
627
- const providerID = plugin.auth.provider
628
- if (disabled.has(providerID)) continue
629
-
630
- // For github-copilot plugin, check if auth exists for either github-copilot or github-copilot-enterprise
631
- let hasAuth = false
632
- const auth = await Auth.get(providerID)
633
- if (auth) hasAuth = true
634
-
635
- // Special handling for github-copilot: also check for enterprise auth
636
- if (providerID === "github-copilot" && !hasAuth) {
637
- const enterpriseAuth = await Auth.get("github-copilot-enterprise")
638
- if (enterpriseAuth) hasAuth = true
639
- }
640
-
641
- if (!hasAuth) continue
642
- if (!plugin.auth.loader) continue
643
-
644
- // Load for the main provider if auth exists
645
- if (auth) {
646
- const options = await plugin.auth.loader(() => Auth.get(providerID) as any, database[plugin.auth.provider])
647
- mergeProvider(plugin.auth.provider, {
648
- source: "custom",
649
- options: options,
650
- })
651
- }
652
-
653
- // If this is github-copilot plugin, also register for github-copilot-enterprise if auth exists
654
- if (providerID === "github-copilot") {
655
- const enterpriseProviderID = "github-copilot-enterprise"
656
- if (!disabled.has(enterpriseProviderID)) {
657
- const enterpriseAuth = await Auth.get(enterpriseProviderID)
658
- if (enterpriseAuth) {
659
- const enterpriseOptions = await plugin.auth.loader(
660
- () => Auth.get(enterpriseProviderID) as any,
661
- database[enterpriseProviderID],
662
- )
663
- mergeProvider(enterpriseProviderID, {
664
- source: "custom",
665
- options: enterpriseOptions,
666
- })
667
- }
668
- }
669
- }
670
- }
671
-
672
- for (const [providerID, fn] of Object.entries(CUSTOM_LOADERS)) {
673
- if (disabled.has(providerID)) continue
674
- const result = await fn(database[providerID])
675
- if (result && (result.autoload || providers[providerID])) {
676
- if (result.getModel) modelLoaders[providerID] = result.getModel
677
- mergeProvider(providerID, {
678
- source: "custom",
679
- options: result.options,
680
- })
681
- }
682
- }
683
-
684
- // load config
685
- for (const [providerID, provider] of configProviders) {
686
- const partial: Partial<Info> = { source: "config" }
687
- if (provider.env) partial.env = provider.env
688
- if (provider.name) partial.name = provider.name
689
- if (provider.options) partial.options = provider.options
690
- mergeProvider(providerID, partial)
691
- }
692
-
693
- for (const [providerID, provider] of Object.entries(providers)) {
694
- if (!isProviderAllowed(providerID)) {
695
- delete providers[providerID]
696
- continue
697
- }
698
-
699
- if (providerID === "github-copilot" || providerID === "github-copilot-enterprise") {
700
- provider.models = mapValues(provider.models, (model) => ({
701
- ...model,
702
- api: {
703
- ...model.api,
704
- npm: "@ai-sdk/github-copilot",
705
- },
706
- }))
707
- }
708
-
709
- const configProvider = config.provider?.[providerID]
710
-
711
- for (const [modelID, model] of Object.entries(provider.models)) {
712
- model.api.id = model.api.id ?? model.id ?? modelID
713
- if (modelID === "gpt-5-chat-latest" || (providerID === "openrouter" && modelID === "openai/gpt-5-chat"))
714
- delete provider.models[modelID]
715
- if (model.status === "alpha" && !Flag.OPENCODE_ENABLE_EXPERIMENTAL_MODELS) delete provider.models[modelID]
716
- if (
717
- (configProvider?.blacklist && configProvider.blacklist.includes(modelID)) ||
718
- (configProvider?.whitelist && !configProvider.whitelist.includes(modelID))
719
- )
720
- delete provider.models[modelID]
721
- }
722
-
723
- if (Object.keys(provider.models).length === 0) {
724
- delete providers[providerID]
725
- continue
726
- }
727
-
728
- log.info("found", { providerID })
729
- }
730
-
731
- return {
732
- models: languages,
733
- providers,
734
- sdk,
735
- modelLoaders,
736
- }
737
- })
738
-
739
- export async function list() {
740
- return state().then((state) => state.providers)
741
- }
742
-
743
- async function getSDK(model: Model) {
744
- try {
745
- using _ = log.time("getSDK", {
746
- providerID: model.providerID,
747
- })
748
- const s = await state()
749
- const provider = s.providers[model.providerID]
750
- const options = { ...provider.options }
751
-
752
- if (model.api.npm.includes("@ai-sdk/openai-compatible") && options["includeUsage"] !== false) {
753
- options["includeUsage"] = true
754
- }
755
-
756
- if (!options["baseURL"]) options["baseURL"] = model.api.url
757
- if (options["apiKey"] === undefined && provider.key) options["apiKey"] = provider.key
758
- if (model.headers)
759
- options["headers"] = {
760
- ...options["headers"],
761
- ...model.headers,
762
- }
763
-
764
- const key = Bun.hash.xxHash32(JSON.stringify({ npm: model.api.npm, options }))
765
- const existing = s.sdk.get(key)
766
- if (existing) return existing
767
-
768
- const customFetch = options["fetch"]
769
-
770
- options["fetch"] = async (input: any, init?: BunFetchRequestInit) => {
771
- // Preserve custom fetch if it exists, wrap it with timeout logic
772
- const fetchFn = customFetch ?? fetch
773
- const opts = init ?? {}
774
-
775
- if (options["timeout"] !== undefined && options["timeout"] !== null) {
776
- const signals: AbortSignal[] = []
777
- if (opts.signal) signals.push(opts.signal)
778
- if (options["timeout"] !== false) signals.push(AbortSignal.timeout(options["timeout"]))
779
-
780
- const combined = signals.length > 1 ? AbortSignal.any(signals) : signals[0]
781
-
782
- opts.signal = combined
783
- }
784
-
785
- const response = await fetchFn(input, {
786
- ...opts,
787
- // @ts-ignore see here: https://github.com/oven-sh/bun/issues/16682
788
- timeout: false,
789
- })
790
-
791
- // Capture rate limit headers from response
792
- try {
793
- const rateLimitInfo = RateLimit.parseHeaders(response.headers, model.providerID)
794
- if (rateLimitInfo) {
795
- RateLimit.setLatest(model.providerID, rateLimitInfo)
796
- }
797
- } catch {
798
- // Ignore errors parsing rate limit headers
799
- }
800
-
801
- return response
802
- }
803
-
804
- // Special case: google-vertex-anthropic uses a subpath import
805
- const bundledKey =
806
- model.providerID === "google-vertex-anthropic" ? "@ai-sdk/google-vertex/anthropic" : model.api.npm
807
- const bundledFn = BUNDLED_PROVIDERS[bundledKey]
808
- if (bundledFn) {
809
- log.info("using bundled provider", { providerID: model.providerID, pkg: bundledKey })
810
- const loaded = bundledFn({
811
- name: model.providerID,
812
- ...options,
813
- })
814
- s.sdk.set(key, loaded)
815
- return loaded as SDK
816
- }
817
-
818
- let installedPath: string
819
- if (!model.api.npm.startsWith("file://")) {
820
- installedPath = await BunProc.install(model.api.npm, "latest")
821
- } else {
822
- log.info("loading local provider", { pkg: model.api.npm })
823
- installedPath = model.api.npm
824
- }
825
-
826
- const mod = await import(installedPath)
827
-
828
- const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!]
829
- const loaded = fn({
830
- name: model.providerID,
831
- ...options,
832
- })
833
- s.sdk.set(key, loaded)
834
- return loaded as SDK
835
- } catch (e) {
836
- throw new InitError({ providerID: model.providerID }, { cause: e })
837
- }
838
- }
839
-
840
- export async function getProvider(providerID: string) {
841
- return state().then((s) => s.providers[providerID])
842
- }
843
-
844
- export async function getModel(providerID: string, modelID: string) {
845
- const s = await state()
846
- const provider = s.providers[providerID]
847
- if (!provider) {
848
- const availableProviders = Object.keys(s.providers)
849
- const matches = fuzzysort.go(providerID, availableProviders, { limit: 3, threshold: -10000 })
850
- const suggestions = matches.map((m) => m.target)
851
- throw new ModelNotFoundError({ providerID, modelID, suggestions })
852
- }
853
-
854
- const info = provider.models[modelID]
855
- if (!info) {
856
- const availableModels = Object.keys(provider.models)
857
- const matches = fuzzysort.go(modelID, availableModels, { limit: 3, threshold: -10000 })
858
- const suggestions = matches.map((m) => m.target)
859
- throw new ModelNotFoundError({ providerID, modelID, suggestions })
860
- }
861
- return info
862
- }
863
-
864
- export async function getLanguage(model: Model) {
865
- const s = await state()
866
- const key = `${model.providerID}/${model.id}`
867
- if (s.models.has(key)) return s.models.get(key)!
868
-
869
- const provider = s.providers[model.providerID]
870
- const sdk = await getSDK(model)
871
-
872
- try {
873
- const language = s.modelLoaders[model.providerID]
874
- ? await s.modelLoaders[model.providerID](sdk, model.api.id, provider.options)
875
- : sdk.languageModel(model.api.id)
876
- s.models.set(key, language)
877
- return language
878
- } catch (e) {
879
- if (e instanceof NoSuchModelError)
880
- throw new ModelNotFoundError(
881
- {
882
- modelID: model.id,
883
- providerID: model.providerID,
884
- },
885
- { cause: e },
886
- )
887
- throw e
888
- }
889
- }
890
-
891
- export async function closest(providerID: string, query: string[]) {
892
- const s = await state()
893
- const provider = s.providers[providerID]
894
- if (!provider) return undefined
895
- for (const item of query) {
896
- for (const modelID of Object.keys(provider.models)) {
897
- if (modelID.includes(item))
898
- return {
899
- providerID,
900
- modelID,
901
- }
902
- }
903
- }
904
- }
905
-
906
- export async function getSmallModel(providerID: string) {
907
- const cfg = await Config.get()
908
-
909
- if (cfg.small_model) {
910
- const parsed = parseModel(cfg.small_model)
911
- return getModel(parsed.providerID, parsed.modelID)
912
- }
913
-
914
- const provider = await state().then((state) => state.providers[providerID])
915
- if (provider) {
916
- let priority = [
917
- "claude-haiku-4-5",
918
- "claude-haiku-4.5",
919
- "3-5-haiku",
920
- "3.5-haiku",
921
- "gemini-2.5-flash",
922
- "gpt-5-nano",
923
- ]
924
- // claude-haiku-4.5 is considered a premium model in github copilot, we shouldn't use premium requests for title gen
925
- if (providerID === "github-copilot") {
926
- priority = priority.filter((m) => m !== "claude-haiku-4.5")
927
- }
928
- if (providerID.startsWith("opencode")) {
929
- priority = ["gpt-5-nano"]
930
- }
931
- for (const item of priority) {
932
- for (const model of Object.keys(provider.models)) {
933
- if (model.includes(item)) return getModel(providerID, model)
934
- }
935
- }
936
- }
937
-
938
- // Check if opencode provider is available before using it
939
- const opencodeProvider = await state().then((state) => state.providers["opencode"])
940
- if (opencodeProvider && opencodeProvider.models["gpt-5-nano"]) {
941
- return getModel("opencode", "gpt-5-nano")
942
- }
943
-
944
- return undefined
945
- }
946
-
947
- const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
948
- export function sort(models: Model[]) {
949
- return sortBy(
950
- models,
951
- [(model) => priority.findIndex((filter) => model.id.includes(filter)), "desc"],
952
- [(model) => (model.id.includes("latest") ? 0 : 1), "asc"],
953
- [(model) => model.id, "desc"],
954
- )
955
- }
956
-
957
- export async function defaultModel() {
958
- const cfg = await Config.get()
959
- if (cfg.model) return parseModel(cfg.model)
960
-
961
- const provider = await list()
962
- .then((val) => Object.values(val))
963
- .then((x) => x.find((p) => !cfg.provider || Object.keys(cfg.provider).includes(p.id)))
964
- if (!provider) throw new Error("no providers found")
965
- const [model] = sort(Object.values(provider.models))
966
- if (!model) throw new Error("no models found")
967
- return {
968
- providerID: provider.id,
969
- modelID: model.id,
970
- }
971
- }
972
-
973
- export function parseModel(model: string) {
974
- const [providerID, ...rest] = model.split("/")
975
- return {
976
- providerID: providerID,
977
- modelID: rest.join("/"),
978
- }
979
- }
980
-
981
- export const ModelNotFoundError = NamedError.create(
982
- "ProviderModelNotFoundError",
983
- z.object({
984
- providerID: z.string(),
985
- modelID: z.string(),
986
- suggestions: z.array(z.string()).optional(),
987
- }),
988
- )
989
-
990
- export const InitError = NamedError.create(
991
- "ProviderInitError",
992
- z.object({
993
- providerID: z.string(),
994
- }),
995
- )
996
- }