Skip to content

Commit d579c5e

Browse files
committed
support global config for providers
1 parent ee91f31 commit d579c5e

File tree

4 files changed

+84
-22
lines changed

4 files changed

+84
-22
lines changed

README.md

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,51 @@ The Models.dev dataset is also used to detect common environment variables like
6161

6262
If there are additional providers you want to use you can submit a PR to the [Models.dev repo](https://github.com/sst/models.dev). If configuring just for yourself check out the Config section below.
6363

64+
### Global Config
65+
66+
Some basic configuration is available in the global config file.
67+
68+
```toml title="~/.config/opencode/config.toml"
69+
theme = "opencode"
70+
provider = "anthropic"
71+
model = "claude-sonnet-4-20250514"
72+
autoupdate = true
73+
```
74+
75+
You can also extend the models.dev database with your own providers and models by placing a `provider.toml` file in `~/.config/opencode/providers`.
76+
77+
```toml title="~/.config/opencode/providers/openrouter/provider.toml"
78+
[provider]
79+
name = "OpenRouter"
80+
env = ["OPENROUTER_API_KEY"]
81+
id = "openrouter"
82+
npm = "@openrouter/ai-sdk-provider"
83+
```
84+
85+
And models in `~/.config/opencode/providers/openrouter/models/[model-id]`.
86+
87+
```toml title="~/.config/opencode/providers/openrouter/models/anthropic/claude-3.5-sonnet.toml"
88+
name = "Claude 4 Sonnet"
89+
attachment = true
90+
reasoning = false
91+
temperature = true
92+
93+
[cost]
94+
input = 3.00
95+
output = 15.00
96+
inputCached = 3.75
97+
outputCached = 0.30
98+
99+
[limit]
100+
context = 200_000
101+
output = 50_000
102+
```
103+
104+
This mirrors the structure found [here](https://github.com/sst/models.dev/tree/dev/providers/anthropic)
105+
64106
### Project Config
65107

66-
Project configuration is optional. You can place an `opencode.json` file in the root of your repo, and it'll be loaded.
108+
Project configuration is optional. You can place an `opencode.json` file in the root of your repo and is meant to be checked in and shared with your team.
67109

68110
```json title="opencode.json"
69111
{
@@ -106,9 +148,7 @@ You can use opencode with any provider listed at [here](https://ai-sdk.dev/provi
106148
"baseURL": "http://localhost:11434/v1"
107149
},
108150
"models": {
109-
"llama2": {
110-
"name": "llama2"
111-
}
151+
"llama2": {}
112152
}
113153
}
114154
}

opencode.json

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
{
22
"$schema": "https://opencode.ai/config.json",
33
"mcp": {},
4-
"provider": {
5-
"openrouter": {
6-
"npm": "@openrouter/ai-sdk-provider",
7-
"name": "OpenRouter",
8-
"models": {
9-
"anthropic/claude-sonnet-4": {}
10-
}
11-
}
12-
}
4+
"provider": {}
135
}

packages/opencode/src/global/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ const data = path.join(xdgData!, app)
88
const cache = path.join(xdgCache!, app)
99
const config = path.join(xdgConfig!, app)
1010

11-
await Promise.all([
12-
fs.mkdir(data, { recursive: true }),
13-
fs.mkdir(config, { recursive: true }),
14-
fs.mkdir(cache, { recursive: true }),
15-
])
16-
1711
export namespace Global {
1812
export const Path = {
1913
data,
2014
bin: path.join(data, "bin"),
15+
providers: path.join(config, "providers"),
2116
cache,
2217
config,
2318
} as const
2419
}
20+
21+
await Promise.all([
22+
fs.mkdir(data, { recursive: true }),
23+
fs.mkdir(config, { recursive: true }),
24+
fs.mkdir(cache, { recursive: true }),
25+
fs.mkdir(Global.Path.providers, { recursive: true }),
26+
])

packages/opencode/src/provider/provider.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import z from "zod"
2+
import path from "path"
23
import { App } from "../app/app"
34
import { Config } from "../config/config"
45
import { mergeDeep, sortBy } from "remeda"
@@ -24,6 +25,7 @@ import { NamedError } from "../util/error"
2425
import { Auth } from "../auth"
2526
import { TaskTool } from "../tool/task"
2627
import { GlobalConfig } from "../global/config"
28+
import { Global } from "../global"
2729

2830
export namespace Provider {
2931
const log = Log.create({ service: "provider" })
@@ -103,9 +105,35 @@ export namespace Provider {
103105
provider.source = source
104106
}
105107

106-
for (const [providerID, provider] of Object.entries(
107-
config.provider ?? {},
108-
)) {
108+
const configProviders = Object.entries(config.provider ?? {})
109+
for await (const providerPath of new Bun.Glob("*/provider.toml").scan({
110+
cwd: Global.Path.providers,
111+
})) {
112+
const [providerID] = providerPath.split("/")
113+
const toml = await import(
114+
path.join(Global.Path.providers, providerPath),
115+
{
116+
with: {
117+
type: "toml",
118+
},
119+
}
120+
).then((mod) => mod.default)
121+
toml.models = {}
122+
const modelsPath = path.join(Global.Path.providers, providerID, "models")
123+
for await (const modelPath of new Bun.Glob("**/*.toml").scan({
124+
cwd: modelsPath,
125+
})) {
126+
const modelID = modelPath.slice(0, -5)
127+
toml.models[modelID] = await import(path.join(modelsPath, modelPath), {
128+
with: {
129+
type: "toml",
130+
},
131+
})
132+
}
133+
configProviders.unshift([providerID, toml])
134+
}
135+
136+
for (const [providerID, provider] of configProviders) {
109137
const existing = database[providerID]
110138
const parsed: ModelsDev.Provider = {
111139
id: providerID,

0 commit comments

Comments
 (0)