| 199 | } |
| 200 | |
| 201 | export async function get( |
| 202 | baseURL: string, |
| 203 | headers: HeadersInit = {}, |
| 204 | existing: Record<string, Model> = {}, |
| 205 | ): Promise<{ models: Record<string, Model>; pickerEnabled: Set<string> }> { |
| 206 | const data = await fetch(`${baseURL}/models`, { |
| 207 | headers, |
| 208 | signal: AbortSignal.timeout(5_000), |
| 209 | }).then(async (res) => { |
| 210 | if (!res.ok) { |
| 211 | throw new Error(`Failed to fetch models: ${res.status}`) |
| 212 | } |
| 213 | return decodeModels(await res.json()) |
| 214 | }) |
| 215 | |
| 216 | const result = { ...existing } |
| 217 | const remote = new Map( |
| 218 | data.data.flatMap((raw) => { |
| 219 | const item = Option.getOrUndefined(decodeItem(raw)) |
| 220 | return item && usable(item) ? ([[item.id, item]] as const) : [] |
| 221 | }), |
| 222 | ) |
| 223 | |
| 224 | // prune existing models whose api.id isn't in the endpoint response |
| 225 | for (const [key, model] of Object.entries(result)) { |
| 226 | const m = remote.get(model.api.id) |
| 227 | if (!m) { |
| 228 | delete result[key] |
| 229 | continue |
| 230 | } |
| 231 | result[key] = build(key, m, baseURL, model) |
| 232 | } |
| 233 | |
| 234 | // add new endpoint models not already keyed in result |
| 235 | for (const [id, m] of remote) { |
| 236 | if (id in result) continue |
| 237 | result[id] = build(id, m, baseURL) |
| 238 | } |
| 239 | |
| 240 | return { |
| 241 | models: result, |
| 242 | pickerEnabled: new Set([...remote].filter(([, item]) => item.model_picker_enabled).map(([id]) => id)), |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | export * as CopilotModels from "./models" |