Skip to content

Commit 6d3fc63

Browse files
core: refactor provider and model system (#5033)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com> Co-authored-by: thdxr <thdxr@users.noreply.github.com>
1 parent ee4437f commit 6d3fc63

File tree

20 files changed

+891
-719
lines changed

20 files changed

+891
-719
lines changed

packages/desktop/src/components/prompt-input.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -456,9 +456,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
456456
<img src={`https://models.dev/logos/${i.provider.id}.svg`} class="size-6 p-0.5 shrink-0" />
457457
<div class="flex gap-x-3 items-baseline flex-[1_0_0]">
458458
<span class="text-14-medium text-text-strong overflow-hidden text-ellipsis">{i.name}</span>
459-
<Show when={i.release_date}>
459+
<Show when={false}>
460460
<span class="text-12-medium text-text-weak overflow-hidden text-ellipsis truncate min-w-0">
461-
{DateTime.fromFormat(i.release_date, "yyyy-MM-dd").toFormat("LLL yyyy")}
461+
{DateTime.fromFormat("unknown", "yyyy-MM-dd").toFormat("LLL yyyy")}
462462
</span>
463463
</Show>
464464
</div>

packages/opencode/src/agent/agent.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ export namespace Agent {
224224
export async function generate(input: { description: string }) {
225225
const defaultModel = await Provider.defaultModel()
226226
const model = await Provider.getModel(defaultModel.providerID, defaultModel.modelID)
227+
const language = await Provider.getLanguage(model)
227228
const system = SystemPrompt.header(defaultModel.providerID)
228229
system.push(PROMPT_GENERATE)
229230
const existing = await list()
@@ -241,7 +242,7 @@ export namespace Agent {
241242
content: `Create an agent configuration based on this request: \"${input.description}\".\n\nIMPORTANT: The following identifiers already exist and must NOT be used: ${existing.map((i) => i.name).join(", ")}\n Return ONLY the JSON object, no other text, do not wrap in backticks`,
242243
},
243244
],
244-
model: model.language,
245+
model: language,
245246
schema: z.object({
246247
identifier: z.string(),
247248
whenToUse: z.string(),

packages/opencode/src/cli/cmd/models.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const ModelsCommand = cmd({
3838

3939
function printModels(providerID: string, verbose?: boolean) {
4040
const provider = providers[providerID]
41-
const sortedModels = Object.entries(provider.info.models).sort(([a], [b]) => a.localeCompare(b))
41+
const sortedModels = Object.entries(provider.models).sort(([a], [b]) => a.localeCompare(b))
4242
for (const [modelID, model] of sortedModels) {
4343
process.stdout.write(`${providerID}/${modelID}`)
4444
process.stdout.write(EOL)

packages/opencode/src/config/config.ts

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,42 @@ export namespace Config {
470470
})
471471
export type Layout = z.infer<typeof Layout>
472472

473+
export const Provider = ModelsDev.Provider.partial()
474+
.extend({
475+
whitelist: z.array(z.string()).optional(),
476+
blacklist: z.array(z.string()).optional(),
477+
models: z.record(z.string(), ModelsDev.Model.partial()).optional(),
478+
options: z
479+
.object({
480+
apiKey: z.string().optional(),
481+
baseURL: z.string().optional(),
482+
enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
483+
setCacheKey: z.boolean().optional().describe("Enable promptCacheKey for this provider (default false)"),
484+
timeout: z
485+
.union([
486+
z
487+
.number()
488+
.int()
489+
.positive()
490+
.describe(
491+
"Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
492+
),
493+
z.literal(false).describe("Disable timeout for this provider entirely."),
494+
])
495+
.optional()
496+
.describe(
497+
"Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
498+
),
499+
})
500+
.catchall(z.any())
501+
.optional(),
502+
})
503+
.strict()
504+
.meta({
505+
ref: "ProviderConfig",
506+
})
507+
export type Provider = z.infer<typeof Provider>
508+
473509
export const Info = z
474510
.object({
475511
$schema: z.string().optional().describe("JSON schema reference for configuration validation"),
@@ -536,43 +572,7 @@ export namespace Config {
536572
.optional()
537573
.describe("Agent configuration, see https://opencode.ai/docs/agent"),
538574
provider: z
539-
.record(
540-
z.string(),
541-
ModelsDev.Provider.partial()
542-
.extend({
543-
whitelist: z.array(z.string()).optional(),
544-
blacklist: z.array(z.string()).optional(),
545-
models: z.record(z.string(), ModelsDev.Model.partial()).optional(),
546-
options: z
547-
.object({
548-
apiKey: z.string().optional(),
549-
baseURL: z.string().optional(),
550-
enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"),
551-
setCacheKey: z
552-
.boolean()
553-
.optional()
554-
.describe("Enable promptCacheKey for this provider (default false)"),
555-
timeout: z
556-
.union([
557-
z
558-
.number()
559-
.int()
560-
.positive()
561-
.describe(
562-
"Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
563-
),
564-
z.literal(false).describe("Disable timeout for this provider entirely."),
565-
])
566-
.optional()
567-
.describe(
568-
"Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.",
569-
),
570-
})
571-
.catchall(z.any())
572-
.optional(),
573-
})
574-
.strict(),
575-
)
575+
.record(z.string(), Provider)
576576
.optional()
577577
.describe("Custom provider configurations and model overrides"),
578578
mcp: z.record(z.string(), Mcp).optional().describe("MCP (Model Context Protocol) server configurations"),

packages/opencode/src/provider/models.ts

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ export namespace ModelsDev {
99
const log = Log.create({ service: "models.dev" })
1010
const filepath = path.join(Global.Path.cache, "models.json")
1111

12-
export const Model = z
13-
.object({
14-
id: z.string(),
15-
name: z.string(),
16-
release_date: z.string(),
17-
attachment: z.boolean(),
18-
reasoning: z.boolean(),
19-
temperature: z.boolean(),
20-
tool_call: z.boolean(),
21-
cost: z.object({
12+
export const Model = z.object({
13+
id: z.string(),
14+
name: z.string(),
15+
release_date: z.string(),
16+
attachment: z.boolean(),
17+
reasoning: z.boolean(),
18+
temperature: z.boolean(),
19+
tool_call: z.boolean(),
20+
cost: z
21+
.object({
2222
input: z.number(),
2323
output: z.number(),
2424
cache_read: z.number().optional(),
@@ -31,40 +31,34 @@ export namespace ModelsDev {
3131
cache_write: z.number().optional(),
3232
})
3333
.optional(),
34-
}),
35-
limit: z.object({
36-
context: z.number(),
37-
output: z.number(),
38-
}),
39-
modalities: z
40-
.object({
41-
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
42-
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
43-
})
44-
.optional(),
45-
experimental: z.boolean().optional(),
46-
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
47-
options: z.record(z.string(), z.any()),
48-
headers: z.record(z.string(), z.string()).optional(),
49-
provider: z.object({ npm: z.string() }).optional(),
50-
})
51-
.meta({
52-
ref: "Model",
53-
})
34+
})
35+
.optional(),
36+
limit: z.object({
37+
context: z.number(),
38+
output: z.number(),
39+
}),
40+
modalities: z
41+
.object({
42+
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
43+
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
44+
})
45+
.optional(),
46+
experimental: z.boolean().optional(),
47+
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
48+
options: z.record(z.string(), z.any()),
49+
headers: z.record(z.string(), z.string()).optional(),
50+
provider: z.object({ npm: z.string() }).optional(),
51+
})
5452
export type Model = z.infer<typeof Model>
5553

56-
export const Provider = z
57-
.object({
58-
api: z.string().optional(),
59-
name: z.string(),
60-
env: z.array(z.string()),
61-
id: z.string(),
62-
npm: z.string().optional(),
63-
models: z.record(z.string(), Model),
64-
})
65-
.meta({
66-
ref: "Provider",
67-
})
54+
export const Provider = z.object({
55+
api: z.string().optional(),
56+
name: z.string(),
57+
env: z.array(z.string()),
58+
id: z.string(),
59+
npm: z.string().optional(),
60+
models: z.record(z.string(), Model),
61+
})
6862

6963
export type Provider = z.infer<typeof Provider>
7064

0 commit comments

Comments
 (0)