agent-accelerator 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +1220 -0
- package/SYSTEM_PROMPT.md +14 -0
- package/SYSTEM_PROMPT_AGENT.md +34 -0
- package/SYSTEM_PROMPT_TOOLS.md +9 -0
- package/bunfig.toml +2 -0
- package/package.json +59 -0
- package/src/agent/agent.ts +615 -0
- package/src/agent/context.ts +161 -0
- package/src/agent/delegation.ts +481 -0
- package/src/agent/loop.ts +569 -0
- package/src/agent/subagent.ts +83 -0
- package/src/ai-sdk/converters.ts +342 -0
- package/src/ai-sdk/errors.ts +122 -0
- package/src/ai-sdk/executor.ts +454 -0
- package/src/ai-sdk/index.ts +55 -0
- package/src/ai-sdk/model-provider.ts +303 -0
- package/src/ai-sdk/options.ts +306 -0
- package/src/ai-sdk/provider.ts +415 -0
- package/src/ai-sdk/registry.ts +416 -0
- package/src/data/README.md +84 -0
- package/src/index.ts +190 -0
- package/src/models/catalog-cache.ts +273 -0
- package/src/models/catalog.ts +503 -0
- package/src/streaming/event-stream.ts +211 -0
- package/src/streaming/sse-parser.ts +97 -0
- package/src/tokens/counter.ts +136 -0
- package/src/tools/executor.ts +365 -0
- package/src/tools/schema.ts +221 -0
- package/src/tools/tool.ts +101 -0
- package/src/types/agent.ts +87 -0
- package/src/types/core.ts +86 -0
- package/src/types/message.ts +115 -0
- package/src/types/model.ts +212 -0
- package/src/types/provider-payloads.ts +434 -0
- package/src/types/response.ts +158 -0
- package/src/types/tool.ts +61 -0
- package/src/utils/base64.ts +27 -0
- package/src/utils/cache.ts +146 -0
- package/src/utils/env.ts +78 -0
- package/src/utils/headers.ts +110 -0
- package/src/utils/media.ts +137 -0
- package/src/utils/serialization.ts +91 -0
- package/src/utils/session.ts +26 -0
- package/src/utils/thought-signature.ts +27 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { createGoogleGenerativeAI, type GoogleGenerativeAIProvider } from "@ai-sdk/google";
|
|
2
|
+
import { createOpenAI, type OpenAIProvider as VercelOpenAIProvider } from "@ai-sdk/openai";
|
|
3
|
+
import { createOpenAICompatible, type OpenAICompatibleProvider as VercelOpenAICompatibleProvider } from "@ai-sdk/openai-compatible";
|
|
4
|
+
import type { LanguageModelV4 } from "@ai-sdk/provider";
|
|
5
|
+
import { getApiKey, getEnv } from "../utils/env.ts";
|
|
6
|
+
|
|
7
|
+
export interface AiSdkModelOptions {
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
baseUrl?: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
fetch?: typeof fetch;
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type AnyAiSdkProvider =
|
|
16
|
+
| GoogleGenerativeAIProvider
|
|
17
|
+
| VercelOpenAIProvider
|
|
18
|
+
| VercelOpenAICompatibleProvider;
|
|
19
|
+
|
|
20
|
+
export const streamThoughtSignatures = new Map<string, string>();
|
|
21
|
+
|
|
22
|
+
function isBrowserFetchRuntime(): boolean {
|
|
23
|
+
try {
|
|
24
|
+
return typeof (globalThis as any).window !== "undefined" && typeof (globalThis as any).window.document !== "undefined";
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function requestUrlOf(input: any): string {
|
|
31
|
+
try {
|
|
32
|
+
if (typeof input === "string") return input;
|
|
33
|
+
if (input instanceof URL) return input.href;
|
|
34
|
+
if (input && typeof input.url === "string") return input.url;
|
|
35
|
+
} catch {}
|
|
36
|
+
return "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function createAiSdkFetch(customFetch?: typeof fetch): typeof fetch {
|
|
40
|
+
const fetchFn = async (input: any, init?: any) => {
|
|
41
|
+
const baseFetch = customFetch || globalThis.fetch;
|
|
42
|
+
if (init && init.headers) {
|
|
43
|
+
const h: any = init.headers;
|
|
44
|
+
if (h instanceof Headers) {
|
|
45
|
+
h.delete("x-multimodal-user-content");
|
|
46
|
+
h.delete("x-thought-signature-map");
|
|
47
|
+
h.delete("x-cached-content-id");
|
|
48
|
+
if (isBrowserFetchRuntime()) {
|
|
49
|
+
for (const k of ["user-agent", "x-session-id", "x-client-request-id", "session_id", "x-opencode-session", "x-opencode-client", "x-goog-api-client"]) {
|
|
50
|
+
try { h.delete(k); } catch {}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else if (typeof h === "object" && !Array.isArray(h)) {
|
|
54
|
+
if (h.authorization && !h.Authorization) {
|
|
55
|
+
const token = h.authorization;
|
|
56
|
+
delete h.authorization;
|
|
57
|
+
h.Authorization = token;
|
|
58
|
+
Object.defineProperty(h, "authorization", {
|
|
59
|
+
get() {
|
|
60
|
+
return this.Authorization;
|
|
61
|
+
},
|
|
62
|
+
set(v) {
|
|
63
|
+
this.Authorization = v;
|
|
64
|
+
},
|
|
65
|
+
enumerable: false,
|
|
66
|
+
configurable: true,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Drop legacy multimodal header (Vercel serializes image/file parts natively)
|
|
71
|
+
if ((h as any)["x-multimodal-user-content"]) delete (h as any)["x-multimodal-user-content"];
|
|
72
|
+
|
|
73
|
+
// Browser: drop SDK-internal / session headers that force a CORS
|
|
74
|
+
// preflight the provider never allow-lists (surfaces as bare
|
|
75
|
+
// `TypeError: Failed to fetch`). Session affinity still flows via
|
|
76
|
+
// providerOptions.promptCacheKey, never via headers.
|
|
77
|
+
if (isBrowserFetchRuntime()) {
|
|
78
|
+
for (const k of ["User-Agent", "user-agent", "x-session-id", "x-client-request-id", "session_id", "x-opencode-session", "x-opencode-client", "x-goog-api-client"]) {
|
|
79
|
+
try { delete (h as any)[k]; } catch {}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Replay Google thought_signature on OpenAI-compat Turn 2
|
|
84
|
+
const sigMapHeader = h["x-thought-signature-map"];
|
|
85
|
+
if (sigMapHeader) {
|
|
86
|
+
delete h["x-thought-signature-map"];
|
|
87
|
+
if (init.body && typeof init.body === "string") {
|
|
88
|
+
try {
|
|
89
|
+
const sigMap = JSON.parse(sigMapHeader);
|
|
90
|
+
const bodyObj = JSON.parse(init.body);
|
|
91
|
+
if (Array.isArray(bodyObj.messages)) {
|
|
92
|
+
for (const msg of bodyObj.messages) {
|
|
93
|
+
if (msg.role === "assistant" && Array.isArray(msg.tool_calls)) {
|
|
94
|
+
for (const tc of msg.tool_calls) {
|
|
95
|
+
const sig = sigMap[tc.id] || sigMap["default"];
|
|
96
|
+
if (sig) {
|
|
97
|
+
tc.extra_content = { google: { thought_signature: sig } };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
init.body = JSON.stringify(bodyObj);
|
|
103
|
+
}
|
|
104
|
+
} catch {}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Add extra_body cached_content if specified
|
|
109
|
+
const cacheId = h["x-cached-content-id"];
|
|
110
|
+
if (cacheId) {
|
|
111
|
+
delete h["x-cached-content-id"];
|
|
112
|
+
if (init.body && typeof init.body === "string") {
|
|
113
|
+
try {
|
|
114
|
+
const bodyObj = JSON.parse(init.body);
|
|
115
|
+
bodyObj.extra_body = {
|
|
116
|
+
...(bodyObj.extra_body || {}),
|
|
117
|
+
google: { cached_content: cacheId },
|
|
118
|
+
};
|
|
119
|
+
init.body = JSON.stringify(bodyObj);
|
|
120
|
+
} catch {}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let res: Response;
|
|
127
|
+
try {
|
|
128
|
+
res = await baseFetch(input, init);
|
|
129
|
+
} catch (e: any) {
|
|
130
|
+
const msg = e?.message ?? String(e);
|
|
131
|
+
if (e?.name === "AbortError" || msg.includes("aborted") || msg.includes("abort")) throw e;
|
|
132
|
+
if (msg.includes("Failed to fetch") || msg.includes("NetworkError") || msg.includes("Load failed") || e instanceof TypeError) {
|
|
133
|
+
const url = requestUrlOf(input);
|
|
134
|
+
const host = (() => { try { return new URL(url).host; } catch { return ""; } })();
|
|
135
|
+
throw new Error(
|
|
136
|
+
`Network request failed (browser could not reach ${host || url || "provider"}). ` +
|
|
137
|
+
`URL: ${url || "(unknown)"}. ` +
|
|
138
|
+
`Usual browser causes: CORS preflight blocked (custom header not allow-listed), ad-blocker, offline, wrong Base URL, or page not on http://localhost. ` +
|
|
139
|
+
`Check F12 Network tab for the failed OPTIONS/POST request. Original: ${msg}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
throw e;
|
|
143
|
+
}
|
|
144
|
+
const contentType = res.headers.get("content-type") || "";
|
|
145
|
+
|
|
146
|
+
// 1. JSON normalization: ensure choices[i].index exists (strict Zod schema tolerance)
|
|
147
|
+
if (contentType.includes("application/json")) {
|
|
148
|
+
try {
|
|
149
|
+
const text = await res.text();
|
|
150
|
+
const data = JSON.parse(text);
|
|
151
|
+
let mutated = false;
|
|
152
|
+
if (data && Array.isArray(data.choices)) {
|
|
153
|
+
for (let i = 0; i < data.choices.length; i++) {
|
|
154
|
+
if (data.choices[i] && data.choices[i].index === undefined) {
|
|
155
|
+
data.choices[i].index = i;
|
|
156
|
+
mutated = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (mutated) {
|
|
161
|
+
return new Response(JSON.stringify(data), {
|
|
162
|
+
status: res.status,
|
|
163
|
+
statusText: res.statusText,
|
|
164
|
+
headers: res.headers,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return new Response(text, {
|
|
168
|
+
status: res.status,
|
|
169
|
+
statusText: res.statusText,
|
|
170
|
+
headers: res.headers,
|
|
171
|
+
});
|
|
172
|
+
} catch {
|
|
173
|
+
return res;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 2. SSE stream normalization: ensure choices[i].index, finish_reason, and extract thought signatures
|
|
178
|
+
if (contentType.includes("text/event-stream") && res.body) {
|
|
179
|
+
let hasSeenFinishReason = false;
|
|
180
|
+
const transformStream = new TransformStream({
|
|
181
|
+
transform(chunk, controller) {
|
|
182
|
+
const text = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
|
|
183
|
+
const lines = text.split("\n");
|
|
184
|
+
const modifiedLines: string[] = [];
|
|
185
|
+
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
if (line.startsWith("data: ")) {
|
|
188
|
+
const dataPayload = line.slice(6).trim();
|
|
189
|
+
if (dataPayload === "[DONE]") {
|
|
190
|
+
if (!hasSeenFinishReason) {
|
|
191
|
+
hasSeenFinishReason = true;
|
|
192
|
+
modifiedLines.push(`data: ${JSON.stringify({
|
|
193
|
+
id: "finish-synth",
|
|
194
|
+
choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
|
|
195
|
+
})}`);
|
|
196
|
+
}
|
|
197
|
+
modifiedLines.push(line);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const json = JSON.parse(dataPayload);
|
|
203
|
+
let lineMutated = false;
|
|
204
|
+
if (json && Array.isArray(json.choices)) {
|
|
205
|
+
for (let i = 0; i < json.choices.length; i++) {
|
|
206
|
+
const c = json.choices[i];
|
|
207
|
+
if (c) {
|
|
208
|
+
if (c.index === undefined) {
|
|
209
|
+
c.index = i;
|
|
210
|
+
lineMutated = true;
|
|
211
|
+
}
|
|
212
|
+
if (c.finish_reason) {
|
|
213
|
+
hasSeenFinishReason = true;
|
|
214
|
+
} else if (json.finish_reason) {
|
|
215
|
+
c.finish_reason = json.finish_reason;
|
|
216
|
+
hasSeenFinishReason = true;
|
|
217
|
+
lineMutated = true;
|
|
218
|
+
}
|
|
219
|
+
if (c.delta?.tool_calls && Array.isArray(c.delta.tool_calls)) {
|
|
220
|
+
for (let j = 0; j < c.delta.tool_calls.length; j++) {
|
|
221
|
+
const tc = c.delta.tool_calls[j];
|
|
222
|
+
if (tc) {
|
|
223
|
+
if (tc.index === undefined) {
|
|
224
|
+
tc.index = j;
|
|
225
|
+
lineMutated = true;
|
|
226
|
+
}
|
|
227
|
+
const sig = tc.extra_content?.google?.thought_signature;
|
|
228
|
+
if (sig) {
|
|
229
|
+
if (tc.id) streamThoughtSignatures.set(tc.id, sig);
|
|
230
|
+
streamThoughtSignatures.set("latest", sig);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (lineMutated) {
|
|
238
|
+
modifiedLines.push(`data: ${JSON.stringify(json)}`);
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch {}
|
|
243
|
+
}
|
|
244
|
+
modifiedLines.push(line);
|
|
245
|
+
}
|
|
246
|
+
controller.enqueue(new TextEncoder().encode(modifiedLines.join("\n")));
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return new Response(res.body.pipeThrough(transformStream), {
|
|
251
|
+
status: res.status,
|
|
252
|
+
statusText: res.statusText,
|
|
253
|
+
headers: res.headers,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return res;
|
|
258
|
+
};
|
|
259
|
+
return fetchFn as typeof fetch;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Creates or retrieves a Vercel AI SDK provider instance for any given provider ID.
|
|
264
|
+
*/
|
|
265
|
+
export function getAiSdkProvider(
|
|
266
|
+
providerId: string,
|
|
267
|
+
options?: AiSdkModelOptions
|
|
268
|
+
): AnyAiSdkProvider {
|
|
269
|
+
const normId = providerId.toLowerCase().trim().replace(/\/.*$/, "");
|
|
270
|
+
const effectiveFetch = createAiSdkFetch(options?.fetch);
|
|
271
|
+
|
|
272
|
+
if (normId === "google") {
|
|
273
|
+
const apiKey = options?.apiKey || getApiKey("google", undefined, options?.env);
|
|
274
|
+
const baseURL = options?.baseUrl || "https://generativelanguage.googleapis.com/v1beta";
|
|
275
|
+
return createGoogleGenerativeAI({
|
|
276
|
+
apiKey,
|
|
277
|
+
baseURL,
|
|
278
|
+
headers: options?.headers,
|
|
279
|
+
fetch: effectiveFetch,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (normId === "openai") {
|
|
284
|
+
const apiKey =
|
|
285
|
+
options?.apiKey ||
|
|
286
|
+
options?.env?.["OPENAI_BASE_API_KEY"] ||
|
|
287
|
+
options?.env?.["OPENAI_API_KEY"] ||
|
|
288
|
+
getEnv("OPENAI_BASE_API_KEY") ||
|
|
289
|
+
getEnv("OPENAI_API_KEY") ||
|
|
290
|
+
getApiKey("openai", undefined, options?.env);
|
|
291
|
+
|
|
292
|
+
const baseURL =
|
|
293
|
+
options?.baseUrl ||
|
|
294
|
+
options?.env?.["OPENAI_BASE_URL"] ||
|
|
295
|
+
getEnv("OPENAI_BASE_URL") ||
|
|
296
|
+
getEnv("OPENAI_API_BASE") ||
|
|
297
|
+
"https://api.openai.com/v1";
|
|
298
|
+
|
|
299
|
+
if (baseURL && !baseURL.includes("api.openai.com")) {
|
|
300
|
+
return createOpenAICompatible({
|
|
301
|
+
name: "openai",
|
|
302
|
+
baseURL,
|
|
303
|
+
apiKey: apiKey || "dummy-key",
|
|
304
|
+
headers: options?.headers,
|
|
305
|
+
fetch: effectiveFetch,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return createOpenAI({
|
|
310
|
+
apiKey: apiKey || "dummy-key",
|
|
311
|
+
baseURL,
|
|
312
|
+
headers: options?.headers,
|
|
313
|
+
fetch: effectiveFetch,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (normId === "opencode") {
|
|
318
|
+
const apiKey = options?.apiKey || getApiKey("opencode", undefined, options?.env);
|
|
319
|
+
const baseURL =
|
|
320
|
+
options?.baseUrl ||
|
|
321
|
+
options?.env?.["OPENCODE_BASE_URL"] ||
|
|
322
|
+
getEnv("OPENCODE_BASE_URL") ||
|
|
323
|
+
"https://opencode.ai/zen/v1";
|
|
324
|
+
|
|
325
|
+
return createOpenAICompatible({
|
|
326
|
+
name: "opencode",
|
|
327
|
+
baseURL,
|
|
328
|
+
apiKey: apiKey || "dummy-key",
|
|
329
|
+
headers: options?.headers,
|
|
330
|
+
fetch: effectiveFetch,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (normId === "openrouter") {
|
|
335
|
+
const apiKey = options?.apiKey || getApiKey("openrouter", undefined, options?.env);
|
|
336
|
+
const baseURL =
|
|
337
|
+
options?.baseUrl ||
|
|
338
|
+
options?.env?.["OPENROUTER_BASE_URL"] ||
|
|
339
|
+
getEnv("OPENROUTER_BASE_URL") ||
|
|
340
|
+
"https://openrouter.ai/api/v1";
|
|
341
|
+
|
|
342
|
+
return createOpenAICompatible({
|
|
343
|
+
name: "openrouter",
|
|
344
|
+
baseURL,
|
|
345
|
+
apiKey: apiKey || "dummy-key",
|
|
346
|
+
headers: options?.headers,
|
|
347
|
+
fetch: effectiveFetch,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Custom / OpenAI-compatible provider (groq, cerebras, together, fireworks, ollama, etc.)
|
|
352
|
+
const envPrefix = normId.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
353
|
+
const apiKey =
|
|
354
|
+
options?.apiKey ||
|
|
355
|
+
options?.env?.[`${envPrefix}_API_KEY`] ||
|
|
356
|
+
options?.env?.[`${envPrefix}_BASE_API_KEY`] ||
|
|
357
|
+
options?.env?.["OPENAI_BASE_API_KEY"] ||
|
|
358
|
+
options?.env?.["OPENAI_API_KEY"] ||
|
|
359
|
+
getEnv(`${envPrefix}_API_KEY`) ||
|
|
360
|
+
getEnv(`${envPrefix}_BASE_API_KEY`) ||
|
|
361
|
+
getEnv("OPENAI_BASE_API_KEY") ||
|
|
362
|
+
getEnv("OPENAI_API_KEY") ||
|
|
363
|
+
getApiKey(normId, undefined, options?.env) ||
|
|
364
|
+
"dummy-key";
|
|
365
|
+
|
|
366
|
+
const baseURL =
|
|
367
|
+
options?.baseUrl ||
|
|
368
|
+
options?.env?.[`${envPrefix}_BASE_URL`] ||
|
|
369
|
+
options?.env?.[`${envPrefix}_BASEURL`] ||
|
|
370
|
+
options?.env?.[`${envPrefix}_API_BASE`] ||
|
|
371
|
+
getEnv(`${envPrefix}_BASE_URL`) ||
|
|
372
|
+
getEnv(`${envPrefix}_BASEURL`) ||
|
|
373
|
+
getEnv(`${envPrefix}_API_BASE`) ||
|
|
374
|
+
getEnv("OPENAI_BASE_URL") ||
|
|
375
|
+
getEnv("OPENAI_API_BASE") ||
|
|
376
|
+
"https://api.openai.com/v1";
|
|
377
|
+
|
|
378
|
+
return createOpenAICompatible({
|
|
379
|
+
name: normId,
|
|
380
|
+
baseURL,
|
|
381
|
+
apiKey,
|
|
382
|
+
headers: options?.headers,
|
|
383
|
+
fetch: effectiveFetch,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Returns a Vercel AI SDK LanguageModel instance for any provider and model.
|
|
389
|
+
*/
|
|
390
|
+
export function getAiSdkModel(
|
|
391
|
+
providerId: string,
|
|
392
|
+
modelId: string,
|
|
393
|
+
options?: AiSdkModelOptions
|
|
394
|
+
): LanguageModelV4 {
|
|
395
|
+
const cleanId = modelId.replace(new RegExp(`^(${providerId}|google|openai|opencode|openrouter|models?)/`, "i"), "");
|
|
396
|
+
const provider = getAiSdkProvider(providerId, options);
|
|
397
|
+
|
|
398
|
+
if ("chat" in provider && typeof (provider as any).chat === "function") {
|
|
399
|
+
return (provider as any).chat(cleanId);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (typeof provider === "function") {
|
|
403
|
+
return (provider as any)(cleanId);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if ("chatModel" in provider && typeof (provider as any).chatModel === "function") {
|
|
407
|
+
return (provider as any).chatModel(cleanId);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if ("languageModel" in provider && typeof (provider as any).languageModel === "function") {
|
|
411
|
+
return (provider as any).languageModel(cleanId);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
throw new Error(`Unsupported Vercel AI SDK provider instance for ${providerId}`);
|
|
415
|
+
}
|