Skip to content

Commit cde105e

Browse files
authored
refactor: unwrap CopilotModels namespace + self-reexport (#22947)
1 parent 1291e82 commit cde105e

1 file changed

Lines changed: 127 additions & 127 deletions

File tree

  • packages/opencode/src/plugin/github-copilot
Lines changed: 127 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,146 @@
11
import { z } from "zod"
22
import type { Model } from "@opencode-ai/sdk/v2"
33

4-
export namespace CopilotModels {
5-
export const schema = z.object({
6-
data: z.array(
7-
z.object({
8-
model_picker_enabled: z.boolean(),
9-
id: z.string(),
10-
name: z.string(),
11-
// every version looks like: `{model.id}-YYYY-MM-DD`
12-
version: z.string(),
13-
supported_endpoints: z.array(z.string()).optional(),
14-
capabilities: z.object({
15-
family: z.string(),
16-
limits: z.object({
17-
max_context_window_tokens: z.number(),
18-
max_output_tokens: z.number(),
19-
max_prompt_tokens: z.number(),
20-
vision: z
21-
.object({
22-
max_prompt_image_size: z.number(),
23-
max_prompt_images: z.number(),
24-
supported_media_types: z.array(z.string()),
25-
})
26-
.optional(),
27-
}),
28-
supports: z.object({
29-
adaptive_thinking: z.boolean().optional(),
30-
max_thinking_budget: z.number().optional(),
31-
min_thinking_budget: z.number().optional(),
32-
reasoning_effort: z.array(z.string()).optional(),
33-
streaming: z.boolean(),
34-
structured_outputs: z.boolean().optional(),
35-
tool_calls: z.boolean(),
36-
vision: z.boolean().optional(),
37-
}),
4+
export const schema = z.object({
5+
data: z.array(
6+
z.object({
7+
model_picker_enabled: z.boolean(),
8+
id: z.string(),
9+
name: z.string(),
10+
// every version looks like: `{model.id}-YYYY-MM-DD`
11+
version: z.string(),
12+
supported_endpoints: z.array(z.string()).optional(),
13+
capabilities: z.object({
14+
family: z.string(),
15+
limits: z.object({
16+
max_context_window_tokens: z.number(),
17+
max_output_tokens: z.number(),
18+
max_prompt_tokens: z.number(),
19+
vision: z
20+
.object({
21+
max_prompt_image_size: z.number(),
22+
max_prompt_images: z.number(),
23+
supported_media_types: z.array(z.string()),
24+
})
25+
.optional(),
26+
}),
27+
supports: z.object({
28+
adaptive_thinking: z.boolean().optional(),
29+
max_thinking_budget: z.number().optional(),
30+
min_thinking_budget: z.number().optional(),
31+
reasoning_effort: z.array(z.string()).optional(),
32+
streaming: z.boolean(),
33+
structured_outputs: z.boolean().optional(),
34+
tool_calls: z.boolean(),
35+
vision: z.boolean().optional(),
3836
}),
3937
}),
40-
),
41-
})
38+
}),
39+
),
40+
})
4241

43-
type Item = z.infer<typeof schema>["data"][number]
42+
type Item = z.infer<typeof schema>["data"][number]
4443

45-
function build(key: string, remote: Item, url: string, prev?: Model): Model {
46-
const reasoning =
47-
!!remote.capabilities.supports.adaptive_thinking ||
48-
!!remote.capabilities.supports.reasoning_effort?.length ||
49-
remote.capabilities.supports.max_thinking_budget !== undefined ||
50-
remote.capabilities.supports.min_thinking_budget !== undefined
51-
const image =
52-
(remote.capabilities.supports.vision ?? false) ||
53-
(remote.capabilities.limits.vision?.supported_media_types ?? []).some((item) => item.startsWith("image/"))
44+
function build(key: string, remote: Item, url: string, prev?: Model): Model {
45+
const reasoning =
46+
!!remote.capabilities.supports.adaptive_thinking ||
47+
!!remote.capabilities.supports.reasoning_effort?.length ||
48+
remote.capabilities.supports.max_thinking_budget !== undefined ||
49+
remote.capabilities.supports.min_thinking_budget !== undefined
50+
const image =
51+
(remote.capabilities.supports.vision ?? false) ||
52+
(remote.capabilities.limits.vision?.supported_media_types ?? []).some((item) => item.startsWith("image/"))
5453

55-
const isMsgApi = remote.supported_endpoints?.includes("/v1/messages")
54+
const isMsgApi = remote.supported_endpoints?.includes("/v1/messages")
5655

57-
return {
58-
id: key,
59-
providerID: "github-copilot",
60-
api: {
61-
id: remote.id,
62-
url: isMsgApi ? `${url}/v1` : url,
63-
npm: isMsgApi ? "@ai-sdk/anthropic" : "@ai-sdk/github-copilot",
64-
},
65-
// API response wins
66-
status: "active",
67-
limit: {
68-
context: remote.capabilities.limits.max_context_window_tokens,
69-
input: remote.capabilities.limits.max_prompt_tokens,
70-
output: remote.capabilities.limits.max_output_tokens,
71-
},
72-
capabilities: {
73-
temperature: prev?.capabilities.temperature ?? true,
74-
reasoning: prev?.capabilities.reasoning ?? reasoning,
75-
attachment: prev?.capabilities.attachment ?? true,
76-
toolcall: remote.capabilities.supports.tool_calls,
77-
input: {
78-
text: true,
79-
audio: false,
80-
image,
81-
video: false,
82-
pdf: false,
83-
},
84-
output: {
85-
text: true,
86-
audio: false,
87-
image: false,
88-
video: false,
89-
pdf: false,
90-
},
91-
interleaved: false,
56+
return {
57+
id: key,
58+
providerID: "github-copilot",
59+
api: {
60+
id: remote.id,
61+
url: isMsgApi ? `${url}/v1` : url,
62+
npm: isMsgApi ? "@ai-sdk/anthropic" : "@ai-sdk/github-copilot",
63+
},
64+
// API response wins
65+
status: "active",
66+
limit: {
67+
context: remote.capabilities.limits.max_context_window_tokens,
68+
input: remote.capabilities.limits.max_prompt_tokens,
69+
output: remote.capabilities.limits.max_output_tokens,
70+
},
71+
capabilities: {
72+
temperature: prev?.capabilities.temperature ?? true,
73+
reasoning: prev?.capabilities.reasoning ?? reasoning,
74+
attachment: prev?.capabilities.attachment ?? true,
75+
toolcall: remote.capabilities.supports.tool_calls,
76+
input: {
77+
text: true,
78+
audio: false,
79+
image,
80+
video: false,
81+
pdf: false,
9282
},
93-
// existing wins
94-
family: prev?.family ?? remote.capabilities.family,
95-
name: prev?.name ?? remote.name,
96-
cost: {
97-
input: 0,
98-
output: 0,
99-
cache: { read: 0, write: 0 },
83+
output: {
84+
text: true,
85+
audio: false,
86+
image: false,
87+
video: false,
88+
pdf: false,
10089
},
101-
options: prev?.options ?? {},
102-
headers: prev?.headers ?? {},
103-
release_date:
104-
prev?.release_date ??
105-
(remote.version.startsWith(`${remote.id}-`) ? remote.version.slice(remote.id.length + 1) : remote.version),
106-
variants: prev?.variants ?? {},
107-
}
90+
interleaved: false,
91+
},
92+
// existing wins
93+
family: prev?.family ?? remote.capabilities.family,
94+
name: prev?.name ?? remote.name,
95+
cost: {
96+
input: 0,
97+
output: 0,
98+
cache: { read: 0, write: 0 },
99+
},
100+
options: prev?.options ?? {},
101+
headers: prev?.headers ?? {},
102+
release_date:
103+
prev?.release_date ??
104+
(remote.version.startsWith(`${remote.id}-`) ? remote.version.slice(remote.id.length + 1) : remote.version),
105+
variants: prev?.variants ?? {},
108106
}
107+
}
109108

110-
export async function get(
111-
baseURL: string,
112-
headers: HeadersInit = {},
113-
existing: Record<string, Model> = {},
114-
): Promise<Record<string, Model>> {
115-
const data = await fetch(`${baseURL}/models`, {
116-
headers,
117-
signal: AbortSignal.timeout(5_000),
118-
}).then(async (res) => {
119-
if (!res.ok) {
120-
throw new Error(`Failed to fetch models: ${res.status}`)
121-
}
122-
return schema.parse(await res.json())
123-
})
124-
125-
const result = { ...existing }
126-
const remote = new Map(data.data.filter((m) => m.model_picker_enabled).map((m) => [m.id, m] as const))
127-
128-
// prune existing models whose api.id isn't in the endpoint response
129-
for (const [key, model] of Object.entries(result)) {
130-
const m = remote.get(model.api.id)
131-
if (!m) {
132-
delete result[key]
133-
continue
134-
}
135-
result[key] = build(key, m, baseURL, model)
109+
export async function get(
110+
baseURL: string,
111+
headers: HeadersInit = {},
112+
existing: Record<string, Model> = {},
113+
): Promise<Record<string, Model>> {
114+
const data = await fetch(`${baseURL}/models`, {
115+
headers,
116+
signal: AbortSignal.timeout(5_000),
117+
}).then(async (res) => {
118+
if (!res.ok) {
119+
throw new Error(`Failed to fetch models: ${res.status}`)
136120
}
121+
return schema.parse(await res.json())
122+
})
123+
124+
const result = { ...existing }
125+
const remote = new Map(data.data.filter((m) => m.model_picker_enabled).map((m) => [m.id, m] as const))
137126

138-
// add new endpoint models not already keyed in result
139-
for (const [id, m] of remote) {
140-
if (id in result) continue
141-
result[id] = build(id, m, baseURL)
127+
// prune existing models whose api.id isn't in the endpoint response
128+
for (const [key, model] of Object.entries(result)) {
129+
const m = remote.get(model.api.id)
130+
if (!m) {
131+
delete result[key]
132+
continue
142133
}
134+
result[key] = build(key, m, baseURL, model)
135+
}
143136

144-
return result
137+
// add new endpoint models not already keyed in result
138+
for (const [id, m] of remote) {
139+
if (id in result) continue
140+
result[id] = build(id, m, baseURL)
145141
}
142+
143+
return result
146144
}
145+
146+
export * as CopilotModels from "./models"

0 commit comments

Comments
 (0)