@probeo/anymodel 0.4.0 → 0.5.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/README.md +21 -3
- package/dist/cli.cjs +368 -17
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +368 -17
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +377 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +374 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -77,6 +77,7 @@ interface ChatCompletionRequest {
|
|
|
77
77
|
tools?: Tool[];
|
|
78
78
|
tool_choice?: ToolChoice;
|
|
79
79
|
user?: string;
|
|
80
|
+
service_tier?: 'auto' | 'flex';
|
|
80
81
|
models?: string[];
|
|
81
82
|
route?: 'fallback';
|
|
82
83
|
transforms?: string[];
|
|
@@ -629,4 +630,21 @@ declare function createOpenAIBatchAdapter(apiKey: string): BatchAdapter;
|
|
|
629
630
|
|
|
630
631
|
declare function createAnthropicBatchAdapter(apiKey: string): BatchAdapter;
|
|
631
632
|
|
|
632
|
-
|
|
633
|
+
declare function createGoogleBatchAdapter(apiKey: string): BatchAdapter;
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Rough token estimation and model-limit lookups for automatic max_tokens calculation.
|
|
637
|
+
*/
|
|
638
|
+
/** Estimate the number of tokens in a string (~4 chars per token). */
|
|
639
|
+
declare function estimateTokenCount(text: string): number;
|
|
640
|
+
/**
|
|
641
|
+
* Determine the best max_tokens value for a request.
|
|
642
|
+
*
|
|
643
|
+
* - If the caller already supplied a value, return it unchanged.
|
|
644
|
+
* - Otherwise estimate input tokens, apply a 5 % safety margin, then
|
|
645
|
+
* return min(maxCompletionTokens, contextLength - estimatedInput).
|
|
646
|
+
* - The result is clamped to at least 1.
|
|
647
|
+
*/
|
|
648
|
+
declare function resolveMaxTokens(model: string, messages: unknown[], userMaxTokens?: number): number;
|
|
649
|
+
|
|
650
|
+
export { AnyModel, type AnyModelConfig, AnyModelError, type AnyModelErrorMetadata, type BatchAdapter, type BatchCreateRequest, BatchManager, type BatchMode, type BatchObject, type BatchPollOptions, type BatchRequestItem, type BatchResultItem, type BatchResults, type BatchStatus, BatchStore, type BatchUsageSummary, type ChatCompletion, type ChatCompletionChoice, type ChatCompletionChunk, type ChatCompletionRequest, type ChunkChoice, type ChunkDelta, type ContentPart, type CustomProviderConfig, type FinishReason, type GenerationStats, GenerationStatsStore, type Message, type ModelArchitecture, type ModelInfo, type ModelPricing, type ModelTopProvider, type NativeBatchStatus, type ProviderAdapter, type ProviderConfig, type ProviderPreferences, type ResponseFormat, type Role, type ServerOptions, type Tool, type ToolCall, type ToolChoice, type Usage, appendFileQueued, configureFsIO, createAnthropicBatchAdapter, createAnyModelServer, createGoogleBatchAdapter, createOpenAIBatchAdapter, ensureDir, estimateTokenCount, getFsQueueStatus, joinPath, readFileQueued, resolveConfig, resolveMaxTokens, startServer, waitForFsQueuesIdle, writeFileFlushedQueued, writeFileQueued };
|
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,7 @@ interface ChatCompletionRequest {
|
|
|
77
77
|
tools?: Tool[];
|
|
78
78
|
tool_choice?: ToolChoice;
|
|
79
79
|
user?: string;
|
|
80
|
+
service_tier?: 'auto' | 'flex';
|
|
80
81
|
models?: string[];
|
|
81
82
|
route?: 'fallback';
|
|
82
83
|
transforms?: string[];
|
|
@@ -629,4 +630,21 @@ declare function createOpenAIBatchAdapter(apiKey: string): BatchAdapter;
|
|
|
629
630
|
|
|
630
631
|
declare function createAnthropicBatchAdapter(apiKey: string): BatchAdapter;
|
|
631
632
|
|
|
632
|
-
|
|
633
|
+
declare function createGoogleBatchAdapter(apiKey: string): BatchAdapter;
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* Rough token estimation and model-limit lookups for automatic max_tokens calculation.
|
|
637
|
+
*/
|
|
638
|
+
/** Estimate the number of tokens in a string (~4 chars per token). */
|
|
639
|
+
declare function estimateTokenCount(text: string): number;
|
|
640
|
+
/**
|
|
641
|
+
* Determine the best max_tokens value for a request.
|
|
642
|
+
*
|
|
643
|
+
* - If the caller already supplied a value, return it unchanged.
|
|
644
|
+
* - Otherwise estimate input tokens, apply a 5 % safety margin, then
|
|
645
|
+
* return min(maxCompletionTokens, contextLength - estimatedInput).
|
|
646
|
+
* - The result is clamped to at least 1.
|
|
647
|
+
*/
|
|
648
|
+
declare function resolveMaxTokens(model: string, messages: unknown[], userMaxTokens?: number): number;
|
|
649
|
+
|
|
650
|
+
export { AnyModel, type AnyModelConfig, AnyModelError, type AnyModelErrorMetadata, type BatchAdapter, type BatchCreateRequest, BatchManager, type BatchMode, type BatchObject, type BatchPollOptions, type BatchRequestItem, type BatchResultItem, type BatchResults, type BatchStatus, BatchStore, type BatchUsageSummary, type ChatCompletion, type ChatCompletionChoice, type ChatCompletionChunk, type ChatCompletionRequest, type ChunkChoice, type ChunkDelta, type ContentPart, type CustomProviderConfig, type FinishReason, type GenerationStats, GenerationStatsStore, type Message, type ModelArchitecture, type ModelInfo, type ModelPricing, type ModelTopProvider, type NativeBatchStatus, type ProviderAdapter, type ProviderConfig, type ProviderPreferences, type ResponseFormat, type Role, type ServerOptions, type Tool, type ToolCall, type ToolChoice, type Usage, appendFileQueued, configureFsIO, createAnthropicBatchAdapter, createAnyModelServer, createGoogleBatchAdapter, createOpenAIBatchAdapter, ensureDir, estimateTokenCount, getFsQueueStatus, joinPath, readFileQueued, resolveConfig, resolveMaxTokens, startServer, waitForFsQueuesIdle, writeFileFlushedQueued, writeFileQueued };
|
package/dist/index.js
CHANGED
|
@@ -480,6 +480,25 @@ var Router = class {
|
|
|
480
480
|
}
|
|
481
481
|
};
|
|
482
482
|
|
|
483
|
+
// src/utils/fetch-with-timeout.ts
|
|
484
|
+
var _defaultTimeout = 12e4;
|
|
485
|
+
var _flexTimeout = 6e5;
|
|
486
|
+
function setDefaultTimeout(ms) {
|
|
487
|
+
_defaultTimeout = ms;
|
|
488
|
+
}
|
|
489
|
+
function getFlexTimeout() {
|
|
490
|
+
return _flexTimeout;
|
|
491
|
+
}
|
|
492
|
+
function fetchWithTimeout(url, init, timeoutMs) {
|
|
493
|
+
const ms = timeoutMs ?? _defaultTimeout;
|
|
494
|
+
const signal = AbortSignal.timeout(ms);
|
|
495
|
+
if (init?.signal) {
|
|
496
|
+
const combined = AbortSignal.any([signal, init.signal]);
|
|
497
|
+
return fetch(url, { ...init, signal: combined });
|
|
498
|
+
}
|
|
499
|
+
return fetch(url, { ...init, signal });
|
|
500
|
+
}
|
|
501
|
+
|
|
483
502
|
// src/providers/openai.ts
|
|
484
503
|
var OPENAI_API_BASE = "https://api.openai.com/v1";
|
|
485
504
|
var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
@@ -497,19 +516,20 @@ var SUPPORTED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
497
516
|
"tools",
|
|
498
517
|
"tool_choice",
|
|
499
518
|
"user",
|
|
500
|
-
"logit_bias"
|
|
519
|
+
"logit_bias",
|
|
520
|
+
"service_tier"
|
|
501
521
|
]);
|
|
502
522
|
function createOpenAIAdapter(apiKey, baseURL) {
|
|
503
523
|
const base = baseURL || OPENAI_API_BASE;
|
|
504
|
-
async function makeRequest(path2, body, method = "POST") {
|
|
505
|
-
const res = await
|
|
524
|
+
async function makeRequest(path2, body, method = "POST", timeoutMs) {
|
|
525
|
+
const res = await fetchWithTimeout(`${base}${path2}`, {
|
|
506
526
|
method,
|
|
507
527
|
headers: {
|
|
508
528
|
"Content-Type": "application/json",
|
|
509
529
|
"Authorization": `Bearer ${apiKey}`
|
|
510
530
|
},
|
|
511
531
|
body: body ? JSON.stringify(body) : void 0
|
|
512
|
-
});
|
|
532
|
+
}, timeoutMs);
|
|
513
533
|
if (!res.ok) {
|
|
514
534
|
let errorBody;
|
|
515
535
|
try {
|
|
@@ -557,6 +577,7 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
557
577
|
if (request.tools !== void 0) body.tools = request.tools;
|
|
558
578
|
if (request.tool_choice !== void 0) body.tool_choice = request.tool_choice;
|
|
559
579
|
if (request.user !== void 0) body.user = request.user;
|
|
580
|
+
if (request.service_tier !== void 0) body.service_tier = request.service_tier;
|
|
560
581
|
return body;
|
|
561
582
|
}
|
|
562
583
|
const adapter = {
|
|
@@ -658,13 +679,15 @@ function createOpenAIAdapter(apiKey, baseURL) {
|
|
|
658
679
|
},
|
|
659
680
|
async sendRequest(request) {
|
|
660
681
|
const body = buildRequestBody(request);
|
|
661
|
-
const
|
|
682
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
683
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
662
684
|
const json = await res.json();
|
|
663
685
|
return adapter.translateResponse(json);
|
|
664
686
|
},
|
|
665
687
|
async sendStreamingRequest(request) {
|
|
666
688
|
const body = buildRequestBody({ ...request, stream: true });
|
|
667
|
-
const
|
|
689
|
+
const timeout = request.service_tier === "flex" ? getFlexTimeout() : void 0;
|
|
690
|
+
const res = await makeRequest("/chat/completions", body, "POST", timeout);
|
|
668
691
|
if (!res.body) {
|
|
669
692
|
throw new AnyModelError(502, "No response body for streaming request", {
|
|
670
693
|
provider_name: "openai"
|
|
@@ -711,7 +734,7 @@ var FALLBACK_MODELS = [
|
|
|
711
734
|
];
|
|
712
735
|
function createAnthropicAdapter(apiKey) {
|
|
713
736
|
async function makeRequest(path2, body, stream = false) {
|
|
714
|
-
const res = await
|
|
737
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}${path2}`, {
|
|
715
738
|
method: "POST",
|
|
716
739
|
headers: {
|
|
717
740
|
"Content-Type": "application/json",
|
|
@@ -968,7 +991,7 @@ ${body.system}` : jsonInstruction;
|
|
|
968
991
|
},
|
|
969
992
|
async listModels() {
|
|
970
993
|
try {
|
|
971
|
-
const res = await
|
|
994
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE}/models`, {
|
|
972
995
|
method: "GET",
|
|
973
996
|
headers: {
|
|
974
997
|
"x-api-key": apiKey,
|
|
@@ -1253,7 +1276,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1253
1276
|
},
|
|
1254
1277
|
async listModels() {
|
|
1255
1278
|
try {
|
|
1256
|
-
const res = await
|
|
1279
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE}/models?key=${apiKey}`);
|
|
1257
1280
|
if (!res.ok) return FALLBACK_MODELS2;
|
|
1258
1281
|
const data = await res.json();
|
|
1259
1282
|
const models = data.models || [];
|
|
@@ -1288,12 +1311,12 @@ function createGoogleAdapter(apiKey) {
|
|
|
1288
1311
|
return SUPPORTED_PARAMS3.has(param);
|
|
1289
1312
|
},
|
|
1290
1313
|
supportsBatch() {
|
|
1291
|
-
return
|
|
1314
|
+
return true;
|
|
1292
1315
|
},
|
|
1293
1316
|
async sendRequest(request) {
|
|
1294
1317
|
const body = translateRequest(request);
|
|
1295
1318
|
const url = getModelEndpoint(request.model, false);
|
|
1296
|
-
const res = await
|
|
1319
|
+
const res = await fetchWithTimeout(url, {
|
|
1297
1320
|
method: "POST",
|
|
1298
1321
|
headers: { "Content-Type": "application/json" },
|
|
1299
1322
|
body: JSON.stringify(body)
|
|
@@ -1316,7 +1339,7 @@ function createGoogleAdapter(apiKey) {
|
|
|
1316
1339
|
async sendStreamingRequest(request) {
|
|
1317
1340
|
const body = translateRequest(request);
|
|
1318
1341
|
const url = getModelEndpoint(request.model, true);
|
|
1319
|
-
const res = await
|
|
1342
|
+
const res = await fetchWithTimeout(url, {
|
|
1320
1343
|
method: "POST",
|
|
1321
1344
|
headers: { "Content-Type": "application/json" },
|
|
1322
1345
|
body: JSON.stringify(body)
|
|
@@ -1366,7 +1389,7 @@ var MODELS = [
|
|
|
1366
1389
|
];
|
|
1367
1390
|
function createPerplexityAdapter(apiKey) {
|
|
1368
1391
|
async function makeRequest(path2, body, method = "POST") {
|
|
1369
|
-
const res = await
|
|
1392
|
+
const res = await fetchWithTimeout(`${PERPLEXITY_API_BASE}${path2}`, {
|
|
1370
1393
|
method,
|
|
1371
1394
|
headers: {
|
|
1372
1395
|
"Content-Type": "application/json",
|
|
@@ -2247,6 +2270,54 @@ var BatchManager = class {
|
|
|
2247
2270
|
}
|
|
2248
2271
|
};
|
|
2249
2272
|
|
|
2273
|
+
// src/utils/token-estimate.ts
|
|
2274
|
+
var CHARS_PER_TOKEN2 = 4;
|
|
2275
|
+
function estimateTokenCount(text) {
|
|
2276
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN2);
|
|
2277
|
+
}
|
|
2278
|
+
var MODEL_LIMITS = [
|
|
2279
|
+
// OpenAI
|
|
2280
|
+
{ pattern: "gpt-4o-mini", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2281
|
+
{ pattern: "gpt-4o", limit: { contextLength: 128e3, maxCompletionTokens: 16384 } },
|
|
2282
|
+
{ pattern: "gpt-4-turbo", limit: { contextLength: 128e3, maxCompletionTokens: 4096 } },
|
|
2283
|
+
{ pattern: "gpt-3.5-turbo", limit: { contextLength: 16385, maxCompletionTokens: 4096 } },
|
|
2284
|
+
{ pattern: "o1", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2285
|
+
{ pattern: "o3", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2286
|
+
{ pattern: "o4-mini", limit: { contextLength: 2e5, maxCompletionTokens: 1e5 } },
|
|
2287
|
+
// Anthropic
|
|
2288
|
+
{ pattern: "claude-opus-4", limit: { contextLength: 2e5, maxCompletionTokens: 32768 } },
|
|
2289
|
+
{ pattern: "claude-sonnet-4", limit: { contextLength: 2e5, maxCompletionTokens: 16384 } },
|
|
2290
|
+
{ pattern: "claude-haiku-4", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2291
|
+
{ pattern: "claude-3.5-sonnet", limit: { contextLength: 2e5, maxCompletionTokens: 8192 } },
|
|
2292
|
+
{ pattern: "claude-3-opus", limit: { contextLength: 2e5, maxCompletionTokens: 4096 } },
|
|
2293
|
+
// Google
|
|
2294
|
+
{ pattern: "gemini-2.5-pro", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2295
|
+
{ pattern: "gemini-2.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2296
|
+
{ pattern: "gemini-2.0-flash", limit: { contextLength: 1048576, maxCompletionTokens: 65536 } },
|
|
2297
|
+
{ pattern: "gemini-1.5-pro", limit: { contextLength: 2097152, maxCompletionTokens: 8192 } },
|
|
2298
|
+
{ pattern: "gemini-1.5-flash", limit: { contextLength: 1048576, maxCompletionTokens: 8192 } }
|
|
2299
|
+
];
|
|
2300
|
+
var DEFAULT_LIMIT = { contextLength: 128e3, maxCompletionTokens: 4096 };
|
|
2301
|
+
function getModelLimits(model) {
|
|
2302
|
+
const bare = model.includes("/") ? model.slice(model.indexOf("/") + 1) : model;
|
|
2303
|
+
for (const entry of MODEL_LIMITS) {
|
|
2304
|
+
if (bare.startsWith(entry.pattern) || bare.includes(entry.pattern)) {
|
|
2305
|
+
return entry.limit;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
return DEFAULT_LIMIT;
|
|
2309
|
+
}
|
|
2310
|
+
function resolveMaxTokens(model, messages, userMaxTokens) {
|
|
2311
|
+
if (userMaxTokens !== void 0) return userMaxTokens;
|
|
2312
|
+
const inputChars = JSON.stringify(messages).length;
|
|
2313
|
+
const estimatedInput = Math.ceil(inputChars / CHARS_PER_TOKEN2);
|
|
2314
|
+
const estimatedWithMargin = Math.ceil(estimatedInput * 1.05);
|
|
2315
|
+
const limits = getModelLimits(model);
|
|
2316
|
+
const available = limits.contextLength - estimatedWithMargin;
|
|
2317
|
+
const result = Math.min(limits.maxCompletionTokens, available);
|
|
2318
|
+
return Math.max(1, result);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2250
2321
|
// src/providers/openai-batch.ts
|
|
2251
2322
|
var OPENAI_API_BASE2 = "https://api.openai.com/v1";
|
|
2252
2323
|
function createOpenAIBatchAdapter(apiKey) {
|
|
@@ -2261,7 +2332,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2261
2332
|
headers["Content-Type"] = "application/json";
|
|
2262
2333
|
fetchBody = JSON.stringify(options.body);
|
|
2263
2334
|
}
|
|
2264
|
-
const res = await
|
|
2335
|
+
const res = await fetchWithTimeout(`${OPENAI_API_BASE2}${path2}`, {
|
|
2265
2336
|
method: options.method || "GET",
|
|
2266
2337
|
headers,
|
|
2267
2338
|
body: fetchBody
|
|
@@ -2287,7 +2358,7 @@ function createOpenAIBatchAdapter(apiKey) {
|
|
|
2287
2358
|
model,
|
|
2288
2359
|
messages: req.messages
|
|
2289
2360
|
};
|
|
2290
|
-
|
|
2361
|
+
body.max_tokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2291
2362
|
if (req.temperature !== void 0) body.temperature = req.temperature;
|
|
2292
2363
|
if (req.top_p !== void 0) body.top_p = req.top_p;
|
|
2293
2364
|
if (req.stop !== void 0) body.stop = req.stop;
|
|
@@ -2446,7 +2517,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2446
2517
|
"anthropic-version": ANTHROPIC_VERSION2,
|
|
2447
2518
|
"Content-Type": "application/json"
|
|
2448
2519
|
};
|
|
2449
|
-
const res = await
|
|
2520
|
+
const res = await fetchWithTimeout(`${ANTHROPIC_API_BASE2}${path2}`, {
|
|
2450
2521
|
method: options.method || "GET",
|
|
2451
2522
|
headers,
|
|
2452
2523
|
body: options.body ? JSON.stringify(options.body) : void 0
|
|
@@ -2469,7 +2540,7 @@ function createAnthropicBatchAdapter(apiKey) {
|
|
|
2469
2540
|
function translateToAnthropicParams(model, req) {
|
|
2470
2541
|
const params = {
|
|
2471
2542
|
model,
|
|
2472
|
-
max_tokens: req.max_tokens || DEFAULT_MAX_TOKENS2
|
|
2543
|
+
max_tokens: resolveMaxTokens(model, req.messages, req.max_tokens || DEFAULT_MAX_TOKENS2)
|
|
2473
2544
|
};
|
|
2474
2545
|
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2475
2546
|
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
@@ -2643,6 +2714,284 @@ ${params.system}` : jsonInstruction;
|
|
|
2643
2714
|
};
|
|
2644
2715
|
}
|
|
2645
2716
|
|
|
2717
|
+
// src/providers/google-batch.ts
|
|
2718
|
+
var GEMINI_API_BASE2 = "https://generativelanguage.googleapis.com/v1beta";
|
|
2719
|
+
function createGoogleBatchAdapter(apiKey) {
|
|
2720
|
+
async function apiRequest(path2, options = {}) {
|
|
2721
|
+
const headers = {
|
|
2722
|
+
"Content-Type": "application/json",
|
|
2723
|
+
"x-goog-api-key": apiKey
|
|
2724
|
+
};
|
|
2725
|
+
const res = await fetchWithTimeout(`${GEMINI_API_BASE2}${path2}`, {
|
|
2726
|
+
method: options.method || "GET",
|
|
2727
|
+
headers,
|
|
2728
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
2729
|
+
});
|
|
2730
|
+
if (!res.ok) {
|
|
2731
|
+
let errorBody;
|
|
2732
|
+
try {
|
|
2733
|
+
errorBody = await res.json();
|
|
2734
|
+
} catch {
|
|
2735
|
+
errorBody = { message: res.statusText };
|
|
2736
|
+
}
|
|
2737
|
+
const msg = errorBody?.error?.message || errorBody?.message || res.statusText;
|
|
2738
|
+
throw new AnyModelError(res.status >= 500 ? 502 : res.status, msg, {
|
|
2739
|
+
provider_name: "google",
|
|
2740
|
+
raw: errorBody
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
return res;
|
|
2744
|
+
}
|
|
2745
|
+
function translateRequestToGemini(model, req) {
|
|
2746
|
+
const body = {};
|
|
2747
|
+
const systemMessages = req.messages.filter((m) => m.role === "system");
|
|
2748
|
+
const nonSystemMessages = req.messages.filter((m) => m.role !== "system");
|
|
2749
|
+
if (systemMessages.length > 0) {
|
|
2750
|
+
body.systemInstruction = {
|
|
2751
|
+
parts: [{ text: systemMessages.map((m) => typeof m.content === "string" ? m.content : "").join("\n") }]
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
body.contents = nonSystemMessages.map((m) => ({
|
|
2755
|
+
role: m.role === "assistant" ? "model" : "user",
|
|
2756
|
+
parts: typeof m.content === "string" ? [{ text: m.content }] : Array.isArray(m.content) ? m.content.map((p) => p.type === "text" ? { text: p.text } : { text: "" }) : [{ text: "" }]
|
|
2757
|
+
}));
|
|
2758
|
+
const generationConfig = {};
|
|
2759
|
+
if (req.temperature !== void 0) generationConfig.temperature = req.temperature;
|
|
2760
|
+
generationConfig.maxOutputTokens = req.max_tokens !== void 0 ? req.max_tokens : resolveMaxTokens(model, req.messages);
|
|
2761
|
+
if (req.top_p !== void 0) generationConfig.topP = req.top_p;
|
|
2762
|
+
if (req.top_k !== void 0) generationConfig.topK = req.top_k;
|
|
2763
|
+
if (req.stop !== void 0) {
|
|
2764
|
+
generationConfig.stopSequences = Array.isArray(req.stop) ? req.stop : [req.stop];
|
|
2765
|
+
}
|
|
2766
|
+
if (req.response_format) {
|
|
2767
|
+
if (req.response_format.type === "json_object") {
|
|
2768
|
+
generationConfig.responseMimeType = "application/json";
|
|
2769
|
+
} else if (req.response_format.type === "json_schema") {
|
|
2770
|
+
generationConfig.responseMimeType = "application/json";
|
|
2771
|
+
generationConfig.responseSchema = req.response_format.json_schema?.schema;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
if (Object.keys(generationConfig).length > 0) {
|
|
2775
|
+
body.generationConfig = generationConfig;
|
|
2776
|
+
}
|
|
2777
|
+
if (req.tools && req.tools.length > 0) {
|
|
2778
|
+
body.tools = [{
|
|
2779
|
+
functionDeclarations: req.tools.map((t) => ({
|
|
2780
|
+
name: t.function.name,
|
|
2781
|
+
description: t.function.description || "",
|
|
2782
|
+
parameters: t.function.parameters || {}
|
|
2783
|
+
}))
|
|
2784
|
+
}];
|
|
2785
|
+
if (req.tool_choice) {
|
|
2786
|
+
if (req.tool_choice === "auto") {
|
|
2787
|
+
body.toolConfig = { functionCallingConfig: { mode: "AUTO" } };
|
|
2788
|
+
} else if (req.tool_choice === "required") {
|
|
2789
|
+
body.toolConfig = { functionCallingConfig: { mode: "ANY" } };
|
|
2790
|
+
} else if (req.tool_choice === "none") {
|
|
2791
|
+
body.toolConfig = { functionCallingConfig: { mode: "NONE" } };
|
|
2792
|
+
} else if (typeof req.tool_choice === "object") {
|
|
2793
|
+
body.toolConfig = {
|
|
2794
|
+
functionCallingConfig: {
|
|
2795
|
+
mode: "ANY",
|
|
2796
|
+
allowedFunctionNames: [req.tool_choice.function.name]
|
|
2797
|
+
}
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
return body;
|
|
2803
|
+
}
|
|
2804
|
+
function mapFinishReason(reason) {
|
|
2805
|
+
switch (reason) {
|
|
2806
|
+
case "STOP":
|
|
2807
|
+
return "stop";
|
|
2808
|
+
case "MAX_TOKENS":
|
|
2809
|
+
return "length";
|
|
2810
|
+
case "SAFETY":
|
|
2811
|
+
return "content_filter";
|
|
2812
|
+
case "RECITATION":
|
|
2813
|
+
return "content_filter";
|
|
2814
|
+
default:
|
|
2815
|
+
return "stop";
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
function translateGeminiResponse(response, model) {
|
|
2819
|
+
const candidate = response.candidates?.[0];
|
|
2820
|
+
let content = "";
|
|
2821
|
+
const toolCalls = [];
|
|
2822
|
+
for (const part of candidate?.content?.parts || []) {
|
|
2823
|
+
if (part.text) {
|
|
2824
|
+
content += part.text;
|
|
2825
|
+
} else if (part.functionCall) {
|
|
2826
|
+
toolCalls.push({
|
|
2827
|
+
id: generateId("call"),
|
|
2828
|
+
type: "function",
|
|
2829
|
+
function: {
|
|
2830
|
+
name: part.functionCall.name,
|
|
2831
|
+
arguments: JSON.stringify(part.functionCall.args || {})
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
const message = { role: "assistant", content };
|
|
2837
|
+
if (toolCalls.length > 0) {
|
|
2838
|
+
message.tool_calls = toolCalls;
|
|
2839
|
+
}
|
|
2840
|
+
const finishReason = toolCalls.length > 0 ? "tool_calls" : mapFinishReason(candidate?.finishReason || "STOP");
|
|
2841
|
+
return {
|
|
2842
|
+
id: generateId(),
|
|
2843
|
+
object: "chat.completion",
|
|
2844
|
+
created: Math.floor(Date.now() / 1e3),
|
|
2845
|
+
model: `google/${model}`,
|
|
2846
|
+
choices: [{ index: 0, message, finish_reason: finishReason }],
|
|
2847
|
+
usage: {
|
|
2848
|
+
prompt_tokens: response.usageMetadata?.promptTokenCount || 0,
|
|
2849
|
+
completion_tokens: response.usageMetadata?.candidatesTokenCount || 0,
|
|
2850
|
+
total_tokens: response.usageMetadata?.totalTokenCount || 0
|
|
2851
|
+
}
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
function mapBatchState(state) {
|
|
2855
|
+
switch (state) {
|
|
2856
|
+
case "JOB_STATE_PENDING":
|
|
2857
|
+
return "pending";
|
|
2858
|
+
case "JOB_STATE_RUNNING":
|
|
2859
|
+
return "processing";
|
|
2860
|
+
case "JOB_STATE_SUCCEEDED":
|
|
2861
|
+
return "completed";
|
|
2862
|
+
case "JOB_STATE_FAILED":
|
|
2863
|
+
return "failed";
|
|
2864
|
+
case "JOB_STATE_CANCELLED":
|
|
2865
|
+
return "cancelled";
|
|
2866
|
+
case "JOB_STATE_EXPIRED":
|
|
2867
|
+
return "failed";
|
|
2868
|
+
default:
|
|
2869
|
+
return "pending";
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
return {
|
|
2873
|
+
async createBatch(model, requests, _options) {
|
|
2874
|
+
const batchRequests = requests.map((req) => ({
|
|
2875
|
+
request: translateRequestToGemini(model, req),
|
|
2876
|
+
metadata: { key: req.custom_id }
|
|
2877
|
+
}));
|
|
2878
|
+
const res = await apiRequest(`/models/${model}:batchGenerateContent`, {
|
|
2879
|
+
method: "POST",
|
|
2880
|
+
body: {
|
|
2881
|
+
batch: {
|
|
2882
|
+
display_name: `anymodel-batch-${Date.now()}`,
|
|
2883
|
+
input_config: {
|
|
2884
|
+
requests: {
|
|
2885
|
+
requests: batchRequests
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
});
|
|
2891
|
+
const data = await res.json();
|
|
2892
|
+
const batchName = data.name || data.batch?.name;
|
|
2893
|
+
if (!batchName) {
|
|
2894
|
+
throw new AnyModelError(502, "No batch name in Google response", {
|
|
2895
|
+
provider_name: "google",
|
|
2896
|
+
raw: data
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
return {
|
|
2900
|
+
providerBatchId: batchName,
|
|
2901
|
+
metadata: {
|
|
2902
|
+
model,
|
|
2903
|
+
total_requests: requests.length
|
|
2904
|
+
}
|
|
2905
|
+
};
|
|
2906
|
+
},
|
|
2907
|
+
async pollBatch(providerBatchId) {
|
|
2908
|
+
const res = await apiRequest(`/${providerBatchId}`);
|
|
2909
|
+
const data = await res.json();
|
|
2910
|
+
const state = data.state || "JOB_STATE_PENDING";
|
|
2911
|
+
const status = mapBatchState(state);
|
|
2912
|
+
const totalCount = data.totalCount || data.metadata?.total_requests || 0;
|
|
2913
|
+
const successCount = data.succeededCount || 0;
|
|
2914
|
+
const failedCount = data.failedCount || 0;
|
|
2915
|
+
return {
|
|
2916
|
+
status,
|
|
2917
|
+
total: totalCount || successCount + failedCount,
|
|
2918
|
+
completed: successCount,
|
|
2919
|
+
failed: failedCount
|
|
2920
|
+
};
|
|
2921
|
+
},
|
|
2922
|
+
async getBatchResults(providerBatchId) {
|
|
2923
|
+
const batchRes = await apiRequest(`/${providerBatchId}`);
|
|
2924
|
+
const batchData = await batchRes.json();
|
|
2925
|
+
const results = [];
|
|
2926
|
+
const model = batchData.metadata?.model || "unknown";
|
|
2927
|
+
if (batchData.response?.inlinedResponses) {
|
|
2928
|
+
for (const item of batchData.response.inlinedResponses) {
|
|
2929
|
+
const customId = item.metadata?.key || `request-${results.length}`;
|
|
2930
|
+
if (item.response) {
|
|
2931
|
+
results.push({
|
|
2932
|
+
custom_id: customId,
|
|
2933
|
+
status: "success",
|
|
2934
|
+
response: translateGeminiResponse(item.response, model),
|
|
2935
|
+
error: null
|
|
2936
|
+
});
|
|
2937
|
+
} else if (item.error) {
|
|
2938
|
+
results.push({
|
|
2939
|
+
custom_id: customId,
|
|
2940
|
+
status: "error",
|
|
2941
|
+
response: null,
|
|
2942
|
+
error: {
|
|
2943
|
+
code: item.error.code || 500,
|
|
2944
|
+
message: item.error.message || "Batch item failed"
|
|
2945
|
+
}
|
|
2946
|
+
});
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
return results;
|
|
2950
|
+
}
|
|
2951
|
+
const responsesFile = batchData.response?.responsesFileName || batchData.outputConfig?.file_name;
|
|
2952
|
+
if (responsesFile) {
|
|
2953
|
+
const downloadUrl = `${GEMINI_API_BASE2}/${responsesFile}:download?alt=media`;
|
|
2954
|
+
const fileRes = await fetchWithTimeout(downloadUrl, {
|
|
2955
|
+
headers: { "x-goog-api-key": apiKey }
|
|
2956
|
+
});
|
|
2957
|
+
if (!fileRes.ok) {
|
|
2958
|
+
throw new AnyModelError(502, "Failed to download batch results file", {
|
|
2959
|
+
provider_name: "google"
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
const text = await fileRes.text();
|
|
2963
|
+
for (const line of text.trim().split("\n")) {
|
|
2964
|
+
if (!line) continue;
|
|
2965
|
+
const item = JSON.parse(line);
|
|
2966
|
+
const customId = item.key || item.metadata?.key || `request-${results.length}`;
|
|
2967
|
+
if (item.response) {
|
|
2968
|
+
results.push({
|
|
2969
|
+
custom_id: customId,
|
|
2970
|
+
status: "success",
|
|
2971
|
+
response: translateGeminiResponse(item.response, model),
|
|
2972
|
+
error: null
|
|
2973
|
+
});
|
|
2974
|
+
} else if (item.error) {
|
|
2975
|
+
results.push({
|
|
2976
|
+
custom_id: customId,
|
|
2977
|
+
status: "error",
|
|
2978
|
+
response: null,
|
|
2979
|
+
error: {
|
|
2980
|
+
code: item.error.code || 500,
|
|
2981
|
+
message: item.error.message || "Batch item failed"
|
|
2982
|
+
}
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
return results;
|
|
2988
|
+
},
|
|
2989
|
+
async cancelBatch(providerBatchId) {
|
|
2990
|
+
await apiRequest(`/${providerBatchId}:cancel`, { method: "POST" });
|
|
2991
|
+
}
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2646
2995
|
// src/client.ts
|
|
2647
2996
|
var AnyModel = class {
|
|
2648
2997
|
registry;
|
|
@@ -2658,6 +3007,7 @@ var AnyModel = class {
|
|
|
2658
3007
|
constructor(config = {}) {
|
|
2659
3008
|
this.config = resolveConfig(config);
|
|
2660
3009
|
this.registry = new ProviderRegistry();
|
|
3010
|
+
setDefaultTimeout((this.config.defaults?.timeout ?? 120) * 1e3);
|
|
2661
3011
|
if (this.config.io) {
|
|
2662
3012
|
configureFsIO(this.config.io);
|
|
2663
3013
|
}
|
|
@@ -2778,6 +3128,10 @@ var AnyModel = class {
|
|
|
2778
3128
|
if (anthropicKey) {
|
|
2779
3129
|
this.batchManager.registerBatchAdapter("anthropic", createAnthropicBatchAdapter(anthropicKey));
|
|
2780
3130
|
}
|
|
3131
|
+
const googleKey = config.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
3132
|
+
if (googleKey) {
|
|
3133
|
+
this.batchManager.registerBatchAdapter("google", createGoogleBatchAdapter(googleKey));
|
|
3134
|
+
}
|
|
2781
3135
|
}
|
|
2782
3136
|
applyDefaults(request) {
|
|
2783
3137
|
const defaults = this.config.defaults;
|
|
@@ -2956,12 +3310,15 @@ export {
|
|
|
2956
3310
|
configureFsIO,
|
|
2957
3311
|
createAnthropicBatchAdapter,
|
|
2958
3312
|
createAnyModelServer,
|
|
3313
|
+
createGoogleBatchAdapter,
|
|
2959
3314
|
createOpenAIBatchAdapter,
|
|
2960
3315
|
ensureDir,
|
|
3316
|
+
estimateTokenCount,
|
|
2961
3317
|
getFsQueueStatus,
|
|
2962
3318
|
joinPath,
|
|
2963
3319
|
readFileQueued,
|
|
2964
3320
|
resolveConfig,
|
|
3321
|
+
resolveMaxTokens,
|
|
2965
3322
|
startServer,
|
|
2966
3323
|
waitForFsQueuesIdle,
|
|
2967
3324
|
writeFileFlushedQueued,
|