cerebras-cli 1.0.1 → 1.0.4

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 (314) hide show
  1. package/README.md +3 -5
  2. package/cerebras-cli-1.0.0.tgz +0 -0
  3. package/package.json +7 -88
  4. package/AGENTS.md +0 -27
  5. package/Dockerfile +0 -10
  6. package/bunfig.toml +0 -4
  7. package/parsers-config.ts +0 -239
  8. package/script/build.ts +0 -151
  9. package/script/postinstall.mjs +0 -122
  10. package/script/publish.ts +0 -256
  11. package/script/schema.ts +0 -47
  12. package/src/acp/README.md +0 -164
  13. package/src/acp/agent.ts +0 -812
  14. package/src/acp/session.ts +0 -70
  15. package/src/acp/types.ts +0 -22
  16. package/src/agent/agent.ts +0 -310
  17. package/src/agent/generate.txt +0 -75
  18. package/src/auth/index.ts +0 -70
  19. package/src/bun/index.ts +0 -152
  20. package/src/bus/global.ts +0 -10
  21. package/src/bus/index.ts +0 -142
  22. package/src/cli/bootstrap.ts +0 -17
  23. package/src/cli/cmd/acp.ts +0 -88
  24. package/src/cli/cmd/agent.ts +0 -165
  25. package/src/cli/cmd/auth.ts +0 -369
  26. package/src/cli/cmd/cmd.ts +0 -7
  27. package/src/cli/cmd/debug/config.ts +0 -15
  28. package/src/cli/cmd/debug/file.ts +0 -91
  29. package/src/cli/cmd/debug/index.ts +0 -41
  30. package/src/cli/cmd/debug/lsp.ts +0 -47
  31. package/src/cli/cmd/debug/ripgrep.ts +0 -83
  32. package/src/cli/cmd/debug/scrap.ts +0 -15
  33. package/src/cli/cmd/debug/snapshot.ts +0 -48
  34. package/src/cli/cmd/export.ts +0 -88
  35. package/src/cli/cmd/generate.ts +0 -38
  36. package/src/cli/cmd/github.ts +0 -1200
  37. package/src/cli/cmd/import.ts +0 -98
  38. package/src/cli/cmd/mcp.ts +0 -400
  39. package/src/cli/cmd/models.ts +0 -77
  40. package/src/cli/cmd/pr.ts +0 -112
  41. package/src/cli/cmd/run.ts +0 -342
  42. package/src/cli/cmd/serve.ts +0 -31
  43. package/src/cli/cmd/session.ts +0 -106
  44. package/src/cli/cmd/stats.ts +0 -298
  45. package/src/cli/cmd/tui/app.tsx +0 -732
  46. package/src/cli/cmd/tui/attach.ts +0 -25
  47. package/src/cli/cmd/tui/component/border.tsx +0 -21
  48. package/src/cli/cmd/tui/component/dialog-agent.tsx +0 -31
  49. package/src/cli/cmd/tui/component/dialog-command.tsx +0 -124
  50. package/src/cli/cmd/tui/component/dialog-feedback.tsx +0 -160
  51. package/src/cli/cmd/tui/component/dialog-mcp.tsx +0 -86
  52. package/src/cli/cmd/tui/component/dialog-model.tsx +0 -223
  53. package/src/cli/cmd/tui/component/dialog-notification.tsx +0 -78
  54. package/src/cli/cmd/tui/component/dialog-provider.tsx +0 -222
  55. package/src/cli/cmd/tui/component/dialog-session-list.tsx +0 -97
  56. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +0 -31
  57. package/src/cli/cmd/tui/component/dialog-status.tsx +0 -114
  58. package/src/cli/cmd/tui/component/dialog-tag.tsx +0 -44
  59. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +0 -50
  60. package/src/cli/cmd/tui/component/logo.tsx +0 -37
  61. package/src/cli/cmd/tui/component/notification-banner.tsx +0 -58
  62. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +0 -530
  63. package/src/cli/cmd/tui/component/prompt/history.tsx +0 -107
  64. package/src/cli/cmd/tui/component/prompt/index.tsx +0 -931
  65. package/src/cli/cmd/tui/context/args.tsx +0 -14
  66. package/src/cli/cmd/tui/context/directory.ts +0 -12
  67. package/src/cli/cmd/tui/context/exit.tsx +0 -23
  68. package/src/cli/cmd/tui/context/helper.tsx +0 -25
  69. package/src/cli/cmd/tui/context/keybind.tsx +0 -111
  70. package/src/cli/cmd/tui/context/kv.tsx +0 -49
  71. package/src/cli/cmd/tui/context/local.tsx +0 -339
  72. package/src/cli/cmd/tui/context/prompt.tsx +0 -18
  73. package/src/cli/cmd/tui/context/route.tsx +0 -45
  74. package/src/cli/cmd/tui/context/sdk.tsx +0 -75
  75. package/src/cli/cmd/tui/context/sync.tsx +0 -374
  76. package/src/cli/cmd/tui/context/theme/aura.json +0 -69
  77. package/src/cli/cmd/tui/context/theme/ayu.json +0 -80
  78. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +0 -233
  79. package/src/cli/cmd/tui/context/theme/catppuccin.json +0 -112
  80. package/src/cli/cmd/tui/context/theme/cobalt2.json +0 -228
  81. package/src/cli/cmd/tui/context/theme/dracula.json +0 -219
  82. package/src/cli/cmd/tui/context/theme/everforest.json +0 -241
  83. package/src/cli/cmd/tui/context/theme/flexoki.json +0 -237
  84. package/src/cli/cmd/tui/context/theme/github.json +0 -233
  85. package/src/cli/cmd/tui/context/theme/gruvbox.json +0 -95
  86. package/src/cli/cmd/tui/context/theme/kanagawa.json +0 -77
  87. package/src/cli/cmd/tui/context/theme/material.json +0 -235
  88. package/src/cli/cmd/tui/context/theme/matrix.json +0 -77
  89. package/src/cli/cmd/tui/context/theme/mercury.json +0 -252
  90. package/src/cli/cmd/tui/context/theme/monokai.json +0 -221
  91. package/src/cli/cmd/tui/context/theme/nightowl.json +0 -221
  92. package/src/cli/cmd/tui/context/theme/nord.json +0 -223
  93. package/src/cli/cmd/tui/context/theme/one-dark.json +0 -84
  94. package/src/cli/cmd/tui/context/theme/orng.json +0 -245
  95. package/src/cli/cmd/tui/context/theme/palenight.json +0 -222
  96. package/src/cli/cmd/tui/context/theme/rosepine.json +0 -234
  97. package/src/cli/cmd/tui/context/theme/solarized.json +0 -223
  98. package/src/cli/cmd/tui/context/theme/synthwave84.json +0 -226
  99. package/src/cli/cmd/tui/context/theme/tokyonight.json +0 -243
  100. package/src/cli/cmd/tui/context/theme/vercel.json +0 -245
  101. package/src/cli/cmd/tui/context/theme/vesper.json +0 -218
  102. package/src/cli/cmd/tui/context/theme/zenburn.json +0 -223
  103. package/src/cli/cmd/tui/context/theme.tsx +0 -1077
  104. package/src/cli/cmd/tui/event.ts +0 -39
  105. package/src/cli/cmd/tui/routes/home.tsx +0 -104
  106. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +0 -93
  107. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +0 -37
  108. package/src/cli/cmd/tui/routes/session/footer.tsx +0 -76
  109. package/src/cli/cmd/tui/routes/session/header.tsx +0 -183
  110. package/src/cli/cmd/tui/routes/session/index.tsx +0 -1703
  111. package/src/cli/cmd/tui/routes/session/sidebar.tsx +0 -586
  112. package/src/cli/cmd/tui/spawn.ts +0 -60
  113. package/src/cli/cmd/tui/thread.ts +0 -120
  114. package/src/cli/cmd/tui/ui/dialog-alert.tsx +0 -55
  115. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +0 -81
  116. package/src/cli/cmd/tui/ui/dialog-help.tsx +0 -36
  117. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +0 -75
  118. package/src/cli/cmd/tui/ui/dialog-select.tsx +0 -317
  119. package/src/cli/cmd/tui/ui/dialog.tsx +0 -170
  120. package/src/cli/cmd/tui/ui/spinner.ts +0 -368
  121. package/src/cli/cmd/tui/ui/toast.tsx +0 -100
  122. package/src/cli/cmd/tui/util/clipboard.ts +0 -127
  123. package/src/cli/cmd/tui/util/editor.ts +0 -32
  124. package/src/cli/cmd/tui/util/terminal.ts +0 -114
  125. package/src/cli/cmd/tui/worker.ts +0 -63
  126. package/src/cli/cmd/uninstall.ts +0 -344
  127. package/src/cli/cmd/upgrade.ts +0 -67
  128. package/src/cli/cmd/web.ts +0 -84
  129. package/src/cli/error.ts +0 -55
  130. package/src/cli/ui.ts +0 -84
  131. package/src/cli/upgrade.ts +0 -25
  132. package/src/command/index.ts +0 -79
  133. package/src/command/template/initialize.txt +0 -10
  134. package/src/command/template/review.txt +0 -73
  135. package/src/config/config.ts +0 -886
  136. package/src/config/markdown.ts +0 -41
  137. package/src/env/index.ts +0 -26
  138. package/src/file/fzf.ts +0 -124
  139. package/src/file/ignore.ts +0 -83
  140. package/src/file/index.ts +0 -326
  141. package/src/file/ripgrep.ts +0 -391
  142. package/src/file/time.ts +0 -38
  143. package/src/file/watcher.ts +0 -89
  144. package/src/flag/flag.ts +0 -28
  145. package/src/format/formatter.ts +0 -277
  146. package/src/format/index.ts +0 -137
  147. package/src/global/index.ts +0 -52
  148. package/src/id/id.ts +0 -73
  149. package/src/ide/index.ts +0 -75
  150. package/src/index.ts +0 -158
  151. package/src/installation/index.ts +0 -194
  152. package/src/lsp/client.ts +0 -215
  153. package/src/lsp/index.ts +0 -370
  154. package/src/lsp/language.ts +0 -111
  155. package/src/lsp/server.ts +0 -1327
  156. package/src/mcp/auth.ts +0 -82
  157. package/src/mcp/index.ts +0 -576
  158. package/src/mcp/oauth-callback.ts +0 -203
  159. package/src/mcp/oauth-provider.ts +0 -132
  160. package/src/notification/index.ts +0 -101
  161. package/src/patch/index.ts +0 -622
  162. package/src/permission/index.ts +0 -198
  163. package/src/plugin/index.ts +0 -95
  164. package/src/project/bootstrap.ts +0 -31
  165. package/src/project/instance.ts +0 -68
  166. package/src/project/project.ts +0 -133
  167. package/src/project/state.ts +0 -65
  168. package/src/project/vcs.ts +0 -77
  169. package/src/provider/auth.ts +0 -143
  170. package/src/provider/models-macro.ts +0 -11
  171. package/src/provider/models.ts +0 -93
  172. package/src/provider/provider.ts +0 -996
  173. package/src/provider/sdk/openai-compatible/src/README.md +0 -5
  174. package/src/provider/sdk/openai-compatible/src/index.ts +0 -2
  175. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +0 -100
  176. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +0 -303
  177. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +0 -27
  178. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +0 -18
  179. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +0 -22
  180. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +0 -207
  181. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +0 -1713
  182. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +0 -177
  183. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +0 -1
  184. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +0 -88
  185. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +0 -128
  186. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +0 -115
  187. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +0 -65
  188. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +0 -104
  189. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +0 -103
  190. package/src/provider/transform.ts +0 -406
  191. package/src/pty/index.ts +0 -226
  192. package/src/ratelimit/index.ts +0 -185
  193. package/src/server/error.ts +0 -36
  194. package/src/server/project.ts +0 -50
  195. package/src/server/server.ts +0 -2463
  196. package/src/server/tui.ts +0 -71
  197. package/src/session/compaction.ts +0 -257
  198. package/src/session/index.ts +0 -470
  199. package/src/session/message-v2.ts +0 -641
  200. package/src/session/message.ts +0 -189
  201. package/src/session/processor.ts +0 -443
  202. package/src/session/prompt/anthropic-20250930.txt +0 -166
  203. package/src/session/prompt/anthropic.txt +0 -105
  204. package/src/session/prompt/anthropic_spoof.txt +0 -1
  205. package/src/session/prompt/beast.txt +0 -147
  206. package/src/session/prompt/build-switch.txt +0 -5
  207. package/src/session/prompt/codex.txt +0 -318
  208. package/src/session/prompt/compaction.txt +0 -12
  209. package/src/session/prompt/copilot-gpt-5.txt +0 -143
  210. package/src/session/prompt/gemini.txt +0 -155
  211. package/src/session/prompt/max-steps.txt +0 -16
  212. package/src/session/prompt/plan-reminder-anthropic.txt +0 -67
  213. package/src/session/prompt/plan.txt +0 -26
  214. package/src/session/prompt/polaris.txt +0 -107
  215. package/src/session/prompt/qwen.txt +0 -109
  216. package/src/session/prompt/summarize.txt +0 -4
  217. package/src/session/prompt/title.txt +0 -36
  218. package/src/session/prompt.ts +0 -1541
  219. package/src/session/retry.ts +0 -82
  220. package/src/session/revert.ts +0 -108
  221. package/src/session/status.ts +0 -75
  222. package/src/session/summary.ts +0 -203
  223. package/src/session/system.ts +0 -148
  224. package/src/session/todo.ts +0 -36
  225. package/src/share/share-next.ts +0 -195
  226. package/src/share/share.ts +0 -87
  227. package/src/snapshot/index.ts +0 -197
  228. package/src/storage/storage.ts +0 -226
  229. package/src/telemetry/index.ts +0 -232
  230. package/src/tool/bash.ts +0 -365
  231. package/src/tool/bash.txt +0 -128
  232. package/src/tool/batch.ts +0 -173
  233. package/src/tool/batch.txt +0 -28
  234. package/src/tool/codesearch.ts +0 -138
  235. package/src/tool/codesearch.txt +0 -12
  236. package/src/tool/edit.ts +0 -674
  237. package/src/tool/edit.txt +0 -10
  238. package/src/tool/glob.ts +0 -65
  239. package/src/tool/glob.txt +0 -6
  240. package/src/tool/grep.ts +0 -120
  241. package/src/tool/grep.txt +0 -8
  242. package/src/tool/invalid.ts +0 -17
  243. package/src/tool/ls.ts +0 -110
  244. package/src/tool/ls.txt +0 -1
  245. package/src/tool/lsp-diagnostics.ts +0 -26
  246. package/src/tool/lsp-diagnostics.txt +0 -1
  247. package/src/tool/lsp-hover.ts +0 -31
  248. package/src/tool/lsp-hover.txt +0 -1
  249. package/src/tool/multiedit.ts +0 -46
  250. package/src/tool/multiedit.txt +0 -41
  251. package/src/tool/patch.ts +0 -233
  252. package/src/tool/patch.txt +0 -1
  253. package/src/tool/read.ts +0 -217
  254. package/src/tool/read.txt +0 -12
  255. package/src/tool/registry.ts +0 -148
  256. package/src/tool/task.ts +0 -135
  257. package/src/tool/task.txt +0 -60
  258. package/src/tool/todo.ts +0 -39
  259. package/src/tool/todoread.txt +0 -14
  260. package/src/tool/todowrite.txt +0 -167
  261. package/src/tool/tool.ts +0 -66
  262. package/src/tool/webfetch.ts +0 -187
  263. package/src/tool/webfetch.txt +0 -14
  264. package/src/tool/websearch.ts +0 -150
  265. package/src/tool/websearch.txt +0 -11
  266. package/src/tool/write.ts +0 -99
  267. package/src/tool/write.txt +0 -8
  268. package/src/types/shims.d.ts +0 -3
  269. package/src/util/color.ts +0 -19
  270. package/src/util/context.ts +0 -25
  271. package/src/util/defer.ts +0 -12
  272. package/src/util/eventloop.ts +0 -20
  273. package/src/util/filesystem.ts +0 -69
  274. package/src/util/fn.ts +0 -11
  275. package/src/util/iife.ts +0 -3
  276. package/src/util/keybind.ts +0 -79
  277. package/src/util/lazy.ts +0 -11
  278. package/src/util/locale.ts +0 -81
  279. package/src/util/lock.ts +0 -98
  280. package/src/util/log.ts +0 -177
  281. package/src/util/queue.ts +0 -32
  282. package/src/util/rpc.ts +0 -42
  283. package/src/util/scrap.ts +0 -10
  284. package/src/util/signal.ts +0 -12
  285. package/src/util/timeout.ts +0 -14
  286. package/src/util/token.ts +0 -7
  287. package/src/util/wildcard.ts +0 -54
  288. package/sst-env.d.ts +0 -9
  289. package/test/bun.test.ts +0 -53
  290. package/test/config/agent-color.test.ts +0 -66
  291. package/test/config/config.test.ts +0 -503
  292. package/test/config/markdown.test.ts +0 -89
  293. package/test/file/ignore.test.ts +0 -10
  294. package/test/fixture/fixture.ts +0 -28
  295. package/test/fixture/lsp/fake-lsp-server.js +0 -77
  296. package/test/ide/ide.test.ts +0 -82
  297. package/test/keybind.test.ts +0 -317
  298. package/test/lsp/client.test.ts +0 -95
  299. package/test/patch/patch.test.ts +0 -348
  300. package/test/preload.ts +0 -38
  301. package/test/project/project.test.ts +0 -42
  302. package/test/provider/provider.test.ts +0 -1809
  303. package/test/provider/transform.test.ts +0 -305
  304. package/test/session/retry.test.ts +0 -61
  305. package/test/session/session.test.ts +0 -71
  306. package/test/snapshot/snapshot.test.ts +0 -939
  307. package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
  308. package/test/tool/bash.test.ts +0 -55
  309. package/test/tool/patch.test.ts +0 -259
  310. package/test/util/iife.test.ts +0 -36
  311. package/test/util/lazy.test.ts +0 -50
  312. package/test/util/timeout.test.ts +0 -21
  313. package/test/util/wildcard.test.ts +0 -55
  314. 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
- }