@probeo/anymodel 0.4.0 → 0.5.1
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/README.md +22 -3
- package/dist/cli.cjs +392 -30
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +392 -30
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +401 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +398 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -485,6 +485,25 @@ var Router = class {
|
|
|
485
485
|
}
|
|
486
486
|
};
|
|
487
487
|
|
|
488
|
+
// src/utils/fetch-with-timeout.ts
|
|
489
|
+
var _defaultTimeout = 12e4;
|
|
490
|
+
var _flexTimeout = 6e5;
|
|
491
|
+
function setDefaultTimeout(ms) {
|
|
492
|
+
_defaultTimeout = ms;
|
|
493
|
+
}
|
|
494
|
+
function getFlexTimeout() {
|
|
495
|
+
return _flexTimeout;
|
|
496
|
+
}
|
|
497
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
498
|
+
const ms = timeoutMs ?? _defaultTimeout;
|
|
499
|
+
const signal = AbortSignal.timeout(ms);
|
|
500
|
+
if (init?.signal) {
|
|
501
|
+
const combined = AbortSignal.any([signal, init.signal]);
|
|
502
|
+
return fetch(url, { ...init, signal: combined });
|
|
503
|
+
}
|
|
504
|
+
return fetch(url, { ...init, signal });
|
|
505
|
+
}
|
|
506
|
+
|
|
488
507
|
// src/providers/openai.ts
|
|
489
508
|
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
490
509
|
var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -502,19 +521,20 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
502
521
|
"tools",
|
|
503
522
|
"tool_choice",
|
|
504
523
|
"user",
|
|
505
|
-
"logit_bias"
|
|
524
|
+
"logit_bias",
|
|
525
|
+
"service_tier"
|
|
506
526
|
]);
|
|
507
527
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
508
528
|
const base = baseURL || OPENAI_API_BASE;
|
|
509
|
-
async function makeRequest(path2, body, method = "POST") {
|
|
510
|
-
const res = await
|
|
529
|
+
async function makeRequest(path2, body, method = "POST", timeoutMs) {
|
|
530
|
+
const res = await fetchWithTimeout(`${base}${path2}`, {
|
|
511
531
|
method,
|
|
512
532
|
headers: {
|
|
513
533
|
"Content-Type": "application/json",
|
|
514
534
|
"Authorization": `Bearer ${apiKey}`
|
|
515
535
|
},
|
|
516
536
|
body: body ? JSON.stringify(body) : void 0
|
|
517
|
-
});
|
|
537
|
+
}, timeoutMs);
|
|
518
538
|
if (!res.ok) {
|
|
519
539
|
let errorBody;
|
|
520
540
|
try {
|
|
@@ -562,6 +582,7 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
562
582
|
if (request.tools !== void 0) body.tools = request.tools;
|
|
563
583
|
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
564
584
|
if (request.user !== void 0) body.user = request.user;
|
|
585
|
+
if (request.service_tier !== void 0) body.service_tier = request.service_tier;
|
|
565
586
|
return body;
|
|
566
587
|
}
|
|
567
588
|
const adapter = {
|
|
@@ -663,13 +684,15 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
663
684
|
},
|
|
664
685
|
async sendRequest(request) {
|
|
665
686
|
const body = buildRequestBody(request);
|
|
666
|
-
const
|
|
687
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
688
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
667
689
|
const json = await res.json();
|
|
668
690
|
return adapter.translateResponse(json);
|
|
669
691
|
},
|
|
670
692
|
async sendStreamingRequest(request) {
|
|
671
693
|
const body = buildRequestBody({ ...request, stream: true });
|
|
672
|
-
const
|
|
694
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
695
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
673
696
|
if (!res.body) {
|
|
674
697
|
throw new AnyModelError(502, "No response body for streaming request", {
|
|
675
698
|
provider_name: "openai"
|
|
@@ -716,7 +739,7 @@ var FALLBACK_MODELS = [
|
|
|
716
739
|
];
|
|
717
740
|
function createAnthropicAdapter(apiKey) {
|
|
718
741
|
async function makeRequest(path2, body, stream = false) {
|
|
719
|
-
const res = await
|
|
742
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
720
743
|
method: "POST",
|
|
721
744
|
headers: {
|
|
722
745
|
"Content-Type": "application/json",
|
|
@@ -973,7 +996,7 @@ ${body.system}` : jsonInstruction;
|
|
|
973
996
|
},
|
|
974
997
|
async listModels() {
|
|
975
998
|
try {
|
|
976
|
-
const res = await
|
|
999
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}/models`, {
|
|
977
1000
|
method: "GET",
|
|
978
1001
|
headers: {
|
|
979
1002
|
"x-api-key": apiKey,
|
|
@@ -1258,7 +1281,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1258
1281
|
},
|
|
1259
1282
|
async listModels() {
|
|
1260
1283
|
try {
|
|
1261
|
-
const res = await
|
|
1284
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE}/models?key=${apiKey}`);
|
|
1262
1285
|
if (!res.ok) return FALLBACK_MODELS2;
|
|
1263
1286
|
const data = await res.json();
|
|
1264
1287
|
const models = data.models || [];
|
|
@@ -1293,12 +1316,12 @@ function createGoogleAdapter(apiKey) {
|
|
|
1293
1316
|
return SUPPORTED_PARAMS3.has(param);
|
|
1294
1317
|
},
|
|
1295
1318
|
supportsBatch() {
|
|
1296
|
-
return
|
|
1319
|
+
return true;
|
|
1297
1320
|
},
|
|
1298
1321
|
async sendRequest(request) {
|
|
1299
1322
|
const body = translateRequest(request);
|
|
1300
1323
|
const url = getModelEndpoint(request.model, false);
|
|
1301
|
-
const res = await
|
|
1324
|
+
const res = await fetchWithTimeout(url, {
|
|
1302
1325
|
method: "POST",
|
|
1303
1326
|
headers: { "Content-Type": "application/json" },
|
|
1304
1327
|
body: JSON.stringify(body)
|
|
@@ -1321,7 +1344,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1321
1344
|
async sendStreamingRequest(request) {
|
|
1322
1345
|
const body = translateRequest(request);
|
|
1323
1346
|
const url = getModelEndpoint(request.model, true);
|
|
1324
|
-
const res = await
|
|
1347
|
+
const res = await fetchWithTimeout(url, {
|
|
1325
1348
|
method: "POST",
|
|
1326
1349
|
headers: { "Content-Type": "application/json" },
|
|
1327
1350
|
body: JSON.stringify(body)
|
|
@@ -1371,7 +1394,7 @@ var MODELS = [
|
|
|
1371
1394
|
];
|
|
1372
1395
|
function createPerplexityAdapter(apiKey) {
|
|
1373
1396
|
async function makeRequest(path2, body, method = "POST") {
|
|
1374
|
-
const res = await
|
|
1397
|
+
const res = await fetchWithTimeout(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1375
1398
|
method,
|
|
1376
1399
|
headers: {
|
|
1377
1400
|
"Content-Type": "application/json",
|
|
@@ -1926,6 +1949,17 @@ var BatchStore = class {
|
|
|
1926
1949
|
const entries = await readDirQueued(this.dir);
|
|
1927
1950
|
return entries.filter((d) => d.isDirectory()).map((d) => d.name).sort();
|
|
1928
1951
|
}
|
|
1952
|
+
/**
|
|
1953
|
+
* Stream requests from JSONL one line at a time (memory-efficient).
|
|
1954
|
+
*/
|
|
1955
|
+
async *streamRequests(id) {
|
|
1956
|
+
const p = joinPath(this.batchDir(id), "requests.jsonl");
|
|
1957
|
+
if (!await fileExistsQueued(p)) return;
|
|
1958
|
+
const raw = await readFileQueued(p, "utf8");
|
|
1959
|
+
for (const line of raw.split("\n")) {
|
|
1960
|
+
if (line.trim()) yield JSON.parse(line);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1929
1963
|
/**
|
|
1930
1964
|
* Check if a batch exists.
|
|
1931
1965
|
*/
|
|
@@ -1990,7 +2024,7 @@ var BatchManager = class {
|
|
|
1990
2024
|
this.processNativeBatch(id, request, native.adapter).catch(() => {
|
|
1991
2025
|
});
|
|
1992
2026
|
} else {
|
|
1993
|
-
this.processConcurrentBatch(id, request).catch(() => {
|
|
2027
|
+
this.processConcurrentBatch(id, request.model, request.options).catch(() => {
|
|
1994
2028
|
});
|
|
1995
2029
|
}
|
|
1996
2030
|
return batch;
|
|
@@ -2170,28 +2204,28 @@ var BatchManager = class {
|
|
|
2170
2204
|
}
|
|
2171
2205
|
/**
|
|
2172
2206
|
* Process batch requests concurrently (fallback path).
|
|
2207
|
+
* Streams requests from disk to avoid holding them all in memory.
|
|
2173
2208
|
*/
|
|
2174
|
-
async processConcurrentBatch(batchId,
|
|
2209
|
+
async processConcurrentBatch(batchId, model, options) {
|
|
2175
2210
|
const batch = await this.store.getMeta(batchId);
|
|
2176
2211
|
if (!batch) return;
|
|
2177
2212
|
batch.status = "processing";
|
|
2178
2213
|
await this.store.updateMeta(batch);
|
|
2179
|
-
const items = request.requests;
|
|
2180
2214
|
const active = /* @__PURE__ */ new Set();
|
|
2181
2215
|
const processItem = async (item) => {
|
|
2182
2216
|
const current = await this.store.getMeta(batchId);
|
|
2183
2217
|
if (current?.status === "cancelled") return;
|
|
2184
2218
|
const chatRequest = {
|
|
2185
|
-
model
|
|
2219
|
+
model,
|
|
2186
2220
|
messages: item.messages,
|
|
2187
|
-
max_tokens: item.max_tokens ??
|
|
2188
|
-
temperature: item.temperature ??
|
|
2189
|
-
top_p: item.top_p ??
|
|
2190
|
-
top_k: item.top_k ??
|
|
2191
|
-
stop: item.stop ??
|
|
2192
|
-
response_format: item.response_format ??
|
|
2193
|
-
tools: item.tools ??
|
|
2194
|
-
tool_choice: item.tool_choice ??
|
|
2221
|
+
max_tokens: item.max_tokens ?? options?.max_tokens,
|
|
2222
|
+
temperature: item.temperature ?? options?.temperature,
|
|
2223
|
+
top_p: item.top_p ?? options?.top_p,
|
|
2224
|
+
top_k: item.top_k ?? options?.top_k,
|
|
2225
|
+
stop: item.stop ?? options?.stop,
|
|
2226
|
+
response_format: item.response_format ?? options?.response_format,
|
|
2227
|
+
tools: item.tools ?? options?.tools,
|
|
2228
|
+
tool_choice: item.tool_choice ?? options?.tool_choice
|
|
2195
2229
|
};
|
|
2196
2230
|
let result;
|
|
2197
2231
|
try {
|
|
@@ -2222,7 +2256,7 @@ var BatchManager = class {
|
|
|
2222
2256
|
await this.store.updateMeta(meta);
|
|
2223
2257
|
}
|
|
2224
2258
|
};
|
|
2225
|
-
for (const item of
|
|
2259
|
+
for await (const item of this.store.streamRequests(batchId)) {
|
|
2226
2260
|
const current = await this.store.getMeta(batchId);
|
|
2227
2261
|
if (current?.status === "cancelled") break;
|
|
2228
2262
|
if (active.size >= this.concurrencyLimit) {
|
|
@@ -2243,6 +2277,51 @@ var BatchManager = class {
|
|
|
2243
2277
|
}
|
|
2244
2278
|
};
|
|
2245
2279
|
|
|
2280
|
+
// src/utils/token-estimate.ts
|
|
2281
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
2282
|
+
var MODEL_LIMITS = [
|
|
2283
|
+
// OpenAI
|
|
2284
|
+
{ pattern: "gpt-4o-mini", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2285
|
+
{ pattern: "gpt-4o", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2286
|
+
{ pattern: "gpt-4-turbo", limit: { contextLength: 128e3, maxCompletionTokens: 4096 } },
|
|
2287
|
+
{ pattern: "gpt-3.5-turbo", limit: { contextLength: 16385, maxCompletionTokens: 4096 } },
|
|
2288
|
+
{ pattern: "o1", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2289
|
+
{ pattern: "o3", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2290
|
+
{ pattern: "o4-mini", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2291
|
+
// Anthropic
|
|
2292
|
+
{ pattern: "claude-opus-4", limit: { contextLength: 2e5, maxCompletionTokens: 32768 } },
|
|
2293
|
+
{ pattern: "claude-sonnet-4", limit: { contextLength: 2e5, maxCompletionTokens: 16384 } },
|
|
2294
|
+
{ pattern: "claude-haiku-4", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2295
|
+
{ pattern: "claude-3.5-sonnet", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2296
|
+
{ pattern: "claude-3-opus", limit: { contextLength: 2e5, maxCompletionTokens: 4096 } },
|
|
2297
|
+
// Google
|
|
2298
|
+
{ pattern: "gemini-2.5-pro", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2299
|
+
{ pattern: "gemini-2.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2300
|
+
{ pattern: "gemini-2.0-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2301
|
+
{ pattern: "gemini-1.5-pro", limit: { contextLength: 2097152, maxCompletionTokens: 8192 } },
|
|
2302
|
+
{ pattern: "gemini-1.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 8192 } }
|
|
2303
|
+
];
|
|
2304
|
+
var DEFAULT_LIMIT = { contextLength: 128e3, maxCompletionTokens: 4096 };
|
|
2305
|
+
function getModelLimits(model) {
|
|
2306
|
+
const bare = model.includes("/") ? model.slice(model.indexOf("/") + 1) : model;
|
|
2307
|
+
for (const entry of MODEL_LIMITS) {
|
|
2308
|
+
if (bare.startsWith(entry.pattern) || bare.includes(entry.pattern)) {
|
|
2309
|
+
return entry.limit;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
return DEFAULT_LIMIT;
|
|
2313
|
+
}
|
|
2314
|
+
function resolveMaxTokens(model, messages, userMaxTokens) {
|
|
2315
|
+
if (userMaxTokens !== void 0) return userMaxTokens;
|
|
2316
|
+
const inputChars = JSON.stringify(messages).length;
|
|
2317
|
+
const estimatedInput = Math.ceil(inputChars / CHARS_PER_TOKEN2);
|
|
2318
|
+
const estimatedWithMargin = Math.ceil(estimatedInput * 1.05);
|
|
2319
|
+
const limits = getModelLimits(model);
|
|
2320
|
+
const available = limits.contextLength - estimatedWithMargin;
|
|
2321
|
+
const result = Math.min(limits.maxCompletionTokens, available);
|
|
2322
|
+
return Math.max(1, result);
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2246
2325
|
// src/providers/openai-batch.ts
|
|
2247
2326
|
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2248
2327
|
function createOpenAIBatchAdapter(apiKey) {
|
|
@@ -2257,7 +2336,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2257
2336
|
headers["Content-Type"] = "application/json";
|
|
2258
2337
|
fetchBody = JSON.stringify(options.body);
|
|
2259
2338
|
}
|
|
2260
|
-
const res = await
|
|
2339
|
+
const res = await fetchWithTimeout(`${OPENAI_API_BASE2}${path2}`, {
|
|
2261
2340
|
method: options.method || "GET",
|
|
2262
2341
|
headers,
|
|
2263
2342
|
body: fetchBody
|
|
@@ -2283,7 +2362,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2283
2362
|
model,
|
|
2284
2363
|
messages: req.messages
|
|
2285
2364
|
};
|
|
2286
|
-
|
|
2365
|
+
body.max_tokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2287
2366
|
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2288
2367
|
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2289
2368
|
if (req.stop !== void 0) body.stop = req.stop;
|
|
@@ -2442,7 +2521,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2442
2521
|
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2443
2522
|
"Content-Type": "application/json"
|
|
2444
2523
|
};
|
|
2445
|
-
const res = await
|
|
2524
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2446
2525
|
method: options.method || "GET",
|
|
2447
2526
|
headers,
|
|
2448
2527
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -2465,7 +2544,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2465
2544
|
function translateToAnthropicParams(model, req) {
|
|
2466
2545
|
const params = {
|
|
2467
2546
|
model,
|
|
2468
|
-
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2547
|
+
max_tokens: resolveMaxTokens(model, req.messages, req.max_tokens || DEFAULT_MAX_TOKENS2)
|
|
2469
2548
|
};
|
|
2470
2549
|
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2471
2550
|
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
@@ -2639,6 +2718,284 @@ ${params.system}` : jsonInstruction;
|
|
|
2639
2718
|
};
|
|
2640
2719
|
}
|
|
2641
2720
|
|
|
2721
|
+
// src/providers/google-batch.ts
|
|
2722
|
+
var GEMINI_API_BASE2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
2723
|
+
function createGoogleBatchAdapter(apiKey) {
|
|
2724
|
+
async function apiRequest(path2, options = {}) {
|
|
2725
|
+
const headers = {
|
|
2726
|
+
"Content-Type": "application/json",
|
|
2727
|
+
"x-goog-api-key": apiKey
|
|
2728
|
+
};
|
|
2729
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE2}${path2}`, {
|
|
2730
|
+
method: options.method || "GET",
|
|
2731
|
+
headers,
|
|
2732
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2733
|
+
});
|
|
2734
|
+
if (!res.ok) {
|
|
2735
|
+
let errorBody;
|
|
2736
|
+
try {
|
|
2737
|
+
errorBody = await res.json();
|
|
2738
|
+
} catch {
|
|
2739
|
+
errorBody = { message: res.statusText };
|
|
2740
|
+
}
|
|
2741
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2742
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2743
|
+
provider_name: "google",
|
|
2744
|
+
raw: errorBody
|
|
2745
|
+
});
|
|
2746
|
+
}
|
|
2747
|
+
return res;
|
|
2748
|
+
}
|
|
2749
|
+
function translateRequestToGemini(model, req) {
|
|
2750
|
+
const body = {};
|
|
2751
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2752
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2753
|
+
if (systemMessages.length > 0) {
|
|
2754
|
+
body.systemInstruction = {
|
|
2755
|
+
parts: [{ text: systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n") }]
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
body.contents = nonSystemMessages.map((m) => ({
|
|
2759
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
2760
|
+
parts: typeof m.content === "string" ? [{ text: m.content }] : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? { text: p.text } : { text: "" }) : [{ text: "" }]
|
|
2761
|
+
}));
|
|
2762
|
+
const generationConfig = {};
|
|
2763
|
+
if (req.temperature !== void 0) generationConfig.temperature = req.temperature;
|
|
2764
|
+
generationConfig.maxOutputTokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2765
|
+
if (req.top_p !== void 0) generationConfig.topP = req.top_p;
|
|
2766
|
+
if (req.top_k !== void 0) generationConfig.topK = req.top_k;
|
|
2767
|
+
if (req.stop !== void 0) {
|
|
2768
|
+
generationConfig.stopSequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2769
|
+
}
|
|
2770
|
+
if (req.response_format) {
|
|
2771
|
+
if (req.response_format.type === "json_object") {
|
|
2772
|
+
generationConfig.responseMimeType = "application/json";
|
|
2773
|
+
} else if (req.response_format.type === "json_schema") {
|
|
2774
|
+
generationConfig.responseMimeType = "application/json";
|
|
2775
|
+
generationConfig.responseSchema = req.response_format.json_schema?.schema;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2778
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
2779
|
+
body.generationConfig = generationConfig;
|
|
2780
|
+
}
|
|
2781
|
+
if (req.tools && req.tools.length > 0) {
|
|
2782
|
+
body.tools = [{
|
|
2783
|
+
functionDeclarations: req.tools.map((t) => ({
|
|
2784
|
+
name: t.function.name,
|
|
2785
|
+
description: t.function.description || "",
|
|
2786
|
+
parameters: t.function.parameters || {}
|
|
2787
|
+
}))
|
|
2788
|
+
}];
|
|
2789
|
+
if (req.tool_choice) {
|
|
2790
|
+
if (req.tool_choice === "auto") {
|
|
2791
|
+
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2792
|
+
} else if (req.tool_choice === "required") {
|
|
2793
|
+
body.toolConfig = { functionCallingConfig: { mode: "ANY" } };
|
|
2794
|
+
} else if (req.tool_choice === "none") {
|
|
2795
|
+
body.toolConfig = { functionCallingConfig: { mode: "NONE" } };
|
|
2796
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2797
|
+
body.toolConfig = {
|
|
2798
|
+
functionCallingConfig: {
|
|
2799
|
+
mode: "ANY",
|
|
2800
|
+
allowedFunctionNames: [req.tool_choice.function.name]
|
|
2801
|
+
}
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
return body;
|
|
2807
|
+
}
|
|
2808
|
+
function mapFinishReason(reason) {
|
|
2809
|
+
switch (reason) {
|
|
2810
|
+
case "STOP":
|
|
2811
|
+
return "stop";
|
|
2812
|
+
case "MAX_TOKENS":
|
|
2813
|
+
return "length";
|
|
2814
|
+
case "SAFETY":
|
|
2815
|
+
return "content_filter";
|
|
2816
|
+
case "RECITATION":
|
|
2817
|
+
return "content_filter";
|
|
2818
|
+
default:
|
|
2819
|
+
return "stop";
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
function translateGeminiResponse(response, model) {
|
|
2823
|
+
const candidate = response.candidates?.[0];
|
|
2824
|
+
let content = "";
|
|
2825
|
+
const toolCalls = [];
|
|
2826
|
+
for (const part of candidate?.content?.parts || []) {
|
|
2827
|
+
if (part.text) {
|
|
2828
|
+
content += part.text;
|
|
2829
|
+
} else if (part.functionCall) {
|
|
2830
|
+
toolCalls.push({
|
|
2831
|
+
id: generateId("call"),
|
|
2832
|
+
type: "function",
|
|
2833
|
+
function: {
|
|
2834
|
+
name: part.functionCall.name,
|
|
2835
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
2836
|
+
}
|
|
2837
|
+
});
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
const message = { role: "assistant", content };
|
|
2841
|
+
if (toolCalls.length > 0) {
|
|
2842
|
+
message.tool_calls = toolCalls;
|
|
2843
|
+
}
|
|
2844
|
+
const finishReason = toolCalls.length > 0 ? "tool_calls" : mapFinishReason(candidate?.finishReason || "STOP");
|
|
2845
|
+
return {
|
|
2846
|
+
id: generateId(),
|
|
2847
|
+
object: "chat.completion",
|
|
2848
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2849
|
+
model: `google/${model}`,
|
|
2850
|
+
choices: [{ index: 0, message, finish_reason: finishReason }],
|
|
2851
|
+
usage: {
|
|
2852
|
+
prompt_tokens: response.usageMetadata?.promptTokenCount || 0,
|
|
2853
|
+
completion_tokens: response.usageMetadata?.candidatesTokenCount || 0,
|
|
2854
|
+
total_tokens: response.usageMetadata?.totalTokenCount || 0
|
|
2855
|
+
}
|
|
2856
|
+
};
|
|
2857
|
+
}
|
|
2858
|
+
function mapBatchState(state) {
|
|
2859
|
+
switch (state) {
|
|
2860
|
+
case "JOB_STATE_PENDING":
|
|
2861
|
+
return "pending";
|
|
2862
|
+
case "JOB_STATE_RUNNING":
|
|
2863
|
+
return "processing";
|
|
2864
|
+
case "JOB_STATE_SUCCEEDED":
|
|
2865
|
+
return "completed";
|
|
2866
|
+
case "JOB_STATE_FAILED":
|
|
2867
|
+
return "failed";
|
|
2868
|
+
case "JOB_STATE_CANCELLED":
|
|
2869
|
+
return "cancelled";
|
|
2870
|
+
case "JOB_STATE_EXPIRED":
|
|
2871
|
+
return "failed";
|
|
2872
|
+
default:
|
|
2873
|
+
return "pending";
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
return {
|
|
2877
|
+
async createBatch(model, requests, _options) {
|
|
2878
|
+
const batchRequests = requests.map((req) => ({
|
|
2879
|
+
request: translateRequestToGemini(model, req),
|
|
2880
|
+
metadata: { key: req.custom_id }
|
|
2881
|
+
}));
|
|
2882
|
+
const res = await apiRequest(`/models/${model}:batchGenerateContent`, {
|
|
2883
|
+
method: "POST",
|
|
2884
|
+
body: {
|
|
2885
|
+
batch: {
|
|
2886
|
+
display_name: `anymodel-batch-${Date.now()}`,
|
|
2887
|
+
input_config: {
|
|
2888
|
+
requests: {
|
|
2889
|
+
requests: batchRequests
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
});
|
|
2895
|
+
const data = await res.json();
|
|
2896
|
+
const batchName = data.name || data.batch?.name;
|
|
2897
|
+
if (!batchName) {
|
|
2898
|
+
throw new AnyModelError(502, "No batch name in Google response", {
|
|
2899
|
+
provider_name: "google",
|
|
2900
|
+
raw: data
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
return {
|
|
2904
|
+
providerBatchId: batchName,
|
|
2905
|
+
metadata: {
|
|
2906
|
+
model,
|
|
2907
|
+
total_requests: requests.length
|
|
2908
|
+
}
|
|
2909
|
+
};
|
|
2910
|
+
},
|
|
2911
|
+
async pollBatch(providerBatchId) {
|
|
2912
|
+
const res = await apiRequest(`/${providerBatchId}`);
|
|
2913
|
+
const data = await res.json();
|
|
2914
|
+
const state = data.state || "JOB_STATE_PENDING";
|
|
2915
|
+
const status = mapBatchState(state);
|
|
2916
|
+
const totalCount = data.totalCount || data.metadata?.total_requests || 0;
|
|
2917
|
+
const successCount = data.succeededCount || 0;
|
|
2918
|
+
const failedCount = data.failedCount || 0;
|
|
2919
|
+
return {
|
|
2920
|
+
status,
|
|
2921
|
+
total: totalCount || successCount + failedCount,
|
|
2922
|
+
completed: successCount,
|
|
2923
|
+
failed: failedCount
|
|
2924
|
+
};
|
|
2925
|
+
},
|
|
2926
|
+
async getBatchResults(providerBatchId) {
|
|
2927
|
+
const batchRes = await apiRequest(`/${providerBatchId}`);
|
|
2928
|
+
const batchData = await batchRes.json();
|
|
2929
|
+
const results = [];
|
|
2930
|
+
const model = batchData.metadata?.model || "unknown";
|
|
2931
|
+
if (batchData.response?.inlinedResponses) {
|
|
2932
|
+
for (const item of batchData.response.inlinedResponses) {
|
|
2933
|
+
const customId = item.metadata?.key || `request-${results.length}`;
|
|
2934
|
+
if (item.response) {
|
|
2935
|
+
results.push({
|
|
2936
|
+
custom_id: customId,
|
|
2937
|
+
status: "success",
|
|
2938
|
+
response: translateGeminiResponse(item.response, model),
|
|
2939
|
+
error: null
|
|
2940
|
+
});
|
|
2941
|
+
} else if (item.error) {
|
|
2942
|
+
results.push({
|
|
2943
|
+
custom_id: customId,
|
|
2944
|
+
status: "error",
|
|
2945
|
+
response: null,
|
|
2946
|
+
error: {
|
|
2947
|
+
code: item.error.code || 500,
|
|
2948
|
+
message: item.error.message || "Batch item failed"
|
|
2949
|
+
}
|
|
2950
|
+
});
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
return results;
|
|
2954
|
+
}
|
|
2955
|
+
const responsesFile = batchData.response?.responsesFileName || batchData.outputConfig?.file_name;
|
|
2956
|
+
if (responsesFile) {
|
|
2957
|
+
const downloadUrl = `${GEMINI_API_BASE2}/${responsesFile}:download?alt=media`;
|
|
2958
|
+
const fileRes = await fetchWithTimeout(downloadUrl, {
|
|
2959
|
+
headers: { "x-goog-api-key": apiKey }
|
|
2960
|
+
});
|
|
2961
|
+
if (!fileRes.ok) {
|
|
2962
|
+
throw new AnyModelError(502, "Failed to download batch results file", {
|
|
2963
|
+
provider_name: "google"
|
|
2964
|
+
});
|
|
2965
|
+
}
|
|
2966
|
+
const text = await fileRes.text();
|
|
2967
|
+
for (const line of text.trim().split("\n")) {
|
|
2968
|
+
if (!line) continue;
|
|
2969
|
+
const item = JSON.parse(line);
|
|
2970
|
+
const customId = item.key || item.metadata?.key || `request-${results.length}`;
|
|
2971
|
+
if (item.response) {
|
|
2972
|
+
results.push({
|
|
2973
|
+
custom_id: customId,
|
|
2974
|
+
status: "success",
|
|
2975
|
+
response: translateGeminiResponse(item.response, model),
|
|
2976
|
+
error: null
|
|
2977
|
+
});
|
|
2978
|
+
} else if (item.error) {
|
|
2979
|
+
results.push({
|
|
2980
|
+
custom_id: customId,
|
|
2981
|
+
status: "error",
|
|
2982
|
+
response: null,
|
|
2983
|
+
error: {
|
|
2984
|
+
code: item.error.code || 500,
|
|
2985
|
+
message: item.error.message || "Batch item failed"
|
|
2986
|
+
}
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
return results;
|
|
2992
|
+
},
|
|
2993
|
+
async cancelBatch(providerBatchId) {
|
|
2994
|
+
await apiRequest(`/${providerBatchId}:cancel`, { method: "POST" });
|
|
2995
|
+
}
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
|
|
2642
2999
|
// src/client.ts
|
|
2643
3000
|
var AnyModel = class {
|
|
2644
3001
|
registry;
|
|
@@ -2654,6 +3011,7 @@ var AnyModel = class {
|
|
|
2654
3011
|
constructor(config = {}) {
|
|
2655
3012
|
this.config = resolveConfig(config);
|
|
2656
3013
|
this.registry = new ProviderRegistry();
|
|
3014
|
+
setDefaultTimeout((this.config.defaults?.timeout ?? 120) * 1e3);
|
|
2657
3015
|
if (this.config.io) {
|
|
2658
3016
|
configureFsIO(this.config.io);
|
|
2659
3017
|
}
|
|
@@ -2774,6 +3132,10 @@ var AnyModel = class {
|
|
|
2774
3132
|
if (anthropicKey) {
|
|
2775
3133
|
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2776
3134
|
}
|
|
3135
|
+
const googleKey = config.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
3136
|
+
if (googleKey) {
|
|
3137
|
+
this.batchManager.registerBatchAdapter("google", createGoogleBatchAdapter(googleKey));
|
|
3138
|
+
}
|
|
2777
3139
|
}
|
|
2778
3140
|
applyDefaults(request) {
|
|
2779
3141
|
const defaults = this.config.defaults;
|