@oh-my-pi/pi-ai 8.4.5 → 8.6.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/package.json +5 -3
- package/src/models.generated.ts +97 -80
- package/src/providers/amazon-bedrock.ts +36 -4
- package/src/providers/anthropic.ts +1 -1
- package/src/providers/azure-openai-responses.ts +1 -1
- package/src/providers/cursor.ts +1 -2
- package/src/providers/google-gemini-cli.ts +9 -5
- package/src/providers/google-shared.ts +11 -2
- package/src/providers/google.ts +1 -1
- package/src/providers/openai-completions.ts +55 -9
- package/src/providers/openai-responses.ts +1 -1
- package/src/stream.ts +9 -0
- package/src/types.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-ai",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.0",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"test": "bun test"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@oh-my-pi/pi-utils": "8.4.5",
|
|
60
59
|
"@anthropic-ai/sdk": "^0.71.2",
|
|
61
60
|
"@aws-sdk/client-bedrock-runtime": "^3.975.0",
|
|
62
61
|
"@bufbuild/protobuf": "^2.10.2",
|
|
@@ -64,13 +63,16 @@
|
|
|
64
63
|
"@connectrpc/connect-node": "^2.1.1",
|
|
65
64
|
"@google/genai": "^1.38.0",
|
|
66
65
|
"@mistralai/mistralai": "^1.13.0",
|
|
66
|
+
"@oh-my-pi/pi-utils": "8.6.0",
|
|
67
67
|
"@sinclair/typebox": "^0.34.41",
|
|
68
|
+
"@smithy/node-http-handler": "4.4.8",
|
|
68
69
|
"ajv": "^8.17.1",
|
|
69
70
|
"ajv-formats": "^3.0.1",
|
|
70
71
|
"chalk": "^5.6.2",
|
|
71
|
-
"json5": "^2.2.3",
|
|
72
72
|
"openai": "^6.16.0",
|
|
73
73
|
"partial-json": "^0.1.7",
|
|
74
|
+
"proxy-agent": "^6.5.0",
|
|
75
|
+
"undici": "^7.19.1",
|
|
74
76
|
"zod-to-json-schema": "^3.24.6"
|
|
75
77
|
},
|
|
76
78
|
"keywords": [
|
package/src/models.generated.ts
CHANGED
|
@@ -4279,23 +4279,6 @@ export const MODELS = {
|
|
|
4279
4279
|
contextWindow: 204800,
|
|
4280
4280
|
maxTokens: 131072,
|
|
4281
4281
|
} satisfies Model<"openai-completions">,
|
|
4282
|
-
"glm-4.7-free": {
|
|
4283
|
-
id: "glm-4.7-free",
|
|
4284
|
-
name: "GLM-4.7",
|
|
4285
|
-
api: "openai-completions",
|
|
4286
|
-
provider: "opencode",
|
|
4287
|
-
baseUrl: "https://opencode.ai/zen/v1",
|
|
4288
|
-
reasoning: true,
|
|
4289
|
-
input: ["text"],
|
|
4290
|
-
cost: {
|
|
4291
|
-
input: 0,
|
|
4292
|
-
output: 0,
|
|
4293
|
-
cacheRead: 0,
|
|
4294
|
-
cacheWrite: 0,
|
|
4295
|
-
},
|
|
4296
|
-
contextWindow: 204800,
|
|
4297
|
-
maxTokens: 131072,
|
|
4298
|
-
} satisfies Model<"openai-completions">,
|
|
4299
4282
|
"gpt-5": {
|
|
4300
4283
|
id: "gpt-5",
|
|
4301
4284
|
name: "GPT-5",
|
|
@@ -4449,23 +4432,6 @@ export const MODELS = {
|
|
|
4449
4432
|
contextWindow: 400000,
|
|
4450
4433
|
maxTokens: 128000,
|
|
4451
4434
|
} satisfies Model<"openai-responses">,
|
|
4452
|
-
"grok-code": {
|
|
4453
|
-
id: "grok-code",
|
|
4454
|
-
name: "Grok Code Fast 1",
|
|
4455
|
-
api: "openai-completions",
|
|
4456
|
-
provider: "opencode",
|
|
4457
|
-
baseUrl: "https://opencode.ai/zen/v1",
|
|
4458
|
-
reasoning: true,
|
|
4459
|
-
input: ["text"],
|
|
4460
|
-
cost: {
|
|
4461
|
-
input: 0,
|
|
4462
|
-
output: 0,
|
|
4463
|
-
cacheRead: 0,
|
|
4464
|
-
cacheWrite: 0,
|
|
4465
|
-
},
|
|
4466
|
-
contextWindow: 256000,
|
|
4467
|
-
maxTokens: 256000,
|
|
4468
|
-
} satisfies Model<"openai-completions">,
|
|
4469
4435
|
"kimi-k2": {
|
|
4470
4436
|
id: "kimi-k2",
|
|
4471
4437
|
name: "Kimi K2",
|
|
@@ -4500,23 +4466,40 @@ export const MODELS = {
|
|
|
4500
4466
|
contextWindow: 262144,
|
|
4501
4467
|
maxTokens: 262144,
|
|
4502
4468
|
} satisfies Model<"openai-completions">,
|
|
4503
|
-
"
|
|
4504
|
-
id: "
|
|
4469
|
+
"kimi-k2.5": {
|
|
4470
|
+
id: "kimi-k2.5",
|
|
4471
|
+
name: "Kimi K2.5",
|
|
4472
|
+
api: "openai-completions",
|
|
4473
|
+
provider: "opencode",
|
|
4474
|
+
baseUrl: "https://opencode.ai/zen/v1",
|
|
4475
|
+
reasoning: true,
|
|
4476
|
+
input: ["text", "image"],
|
|
4477
|
+
cost: {
|
|
4478
|
+
input: 1.2,
|
|
4479
|
+
output: 1.2,
|
|
4480
|
+
cacheRead: 0.6,
|
|
4481
|
+
cacheWrite: 0,
|
|
4482
|
+
},
|
|
4483
|
+
contextWindow: 262144,
|
|
4484
|
+
maxTokens: 262144,
|
|
4485
|
+
} satisfies Model<"openai-completions">,
|
|
4486
|
+
"minimax-m2.1": {
|
|
4487
|
+
id: "minimax-m2.1",
|
|
4505
4488
|
name: "MiniMax M2.1",
|
|
4506
|
-
api: "
|
|
4489
|
+
api: "openai-completions",
|
|
4507
4490
|
provider: "opencode",
|
|
4508
|
-
baseUrl: "https://opencode.ai/zen",
|
|
4491
|
+
baseUrl: "https://opencode.ai/zen/v1",
|
|
4509
4492
|
reasoning: true,
|
|
4510
4493
|
input: ["text"],
|
|
4511
4494
|
cost: {
|
|
4512
|
-
input: 0,
|
|
4513
|
-
output:
|
|
4514
|
-
cacheRead: 0,
|
|
4495
|
+
input: 0.3,
|
|
4496
|
+
output: 1.2,
|
|
4497
|
+
cacheRead: 0.1,
|
|
4515
4498
|
cacheWrite: 0,
|
|
4516
4499
|
},
|
|
4517
4500
|
contextWindow: 204800,
|
|
4518
4501
|
maxTokens: 131072,
|
|
4519
|
-
} satisfies Model<"
|
|
4502
|
+
} satisfies Model<"openai-completions">,
|
|
4520
4503
|
"qwen3-coder": {
|
|
4521
4504
|
id: "qwen3-coder",
|
|
4522
4505
|
name: "Qwen3 Coder",
|
|
@@ -5367,7 +5350,7 @@ export const MODELS = {
|
|
|
5367
5350
|
cacheWrite: 0.08333333333333334,
|
|
5368
5351
|
},
|
|
5369
5352
|
contextWindow: 1048576,
|
|
5370
|
-
maxTokens:
|
|
5353
|
+
maxTokens: 65536,
|
|
5371
5354
|
} satisfies Model<"openai-completions">,
|
|
5372
5355
|
"google/gemini-2.5-pro": {
|
|
5373
5356
|
id: "google/gemini-2.5-pro",
|
|
@@ -5760,23 +5743,6 @@ export const MODELS = {
|
|
|
5760
5743
|
contextWindow: 262144,
|
|
5761
5744
|
maxTokens: 65536,
|
|
5762
5745
|
} satisfies Model<"openai-completions">,
|
|
5763
|
-
"mistralai/devstral-2512:free": {
|
|
5764
|
-
id: "mistralai/devstral-2512:free",
|
|
5765
|
-
name: "Mistral: Devstral 2 2512 (free)",
|
|
5766
|
-
api: "openai-completions",
|
|
5767
|
-
provider: "openrouter",
|
|
5768
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
5769
|
-
reasoning: false,
|
|
5770
|
-
input: ["text"],
|
|
5771
|
-
cost: {
|
|
5772
|
-
input: 0,
|
|
5773
|
-
output: 0,
|
|
5774
|
-
cacheRead: 0,
|
|
5775
|
-
cacheWrite: 0,
|
|
5776
|
-
},
|
|
5777
|
-
contextWindow: 262144,
|
|
5778
|
-
maxTokens: 4096,
|
|
5779
|
-
} satisfies Model<"openai-completions">,
|
|
5780
5746
|
"mistralai/devstral-medium": {
|
|
5781
5747
|
id: "mistralai/devstral-medium",
|
|
5782
5748
|
name: "Mistral: Devstral Medium",
|
|
@@ -6287,6 +6253,23 @@ export const MODELS = {
|
|
|
6287
6253
|
contextWindow: 262144,
|
|
6288
6254
|
maxTokens: 65535,
|
|
6289
6255
|
} satisfies Model<"openai-completions">,
|
|
6256
|
+
"moonshotai/kimi-k2.5": {
|
|
6257
|
+
id: "moonshotai/kimi-k2.5",
|
|
6258
|
+
name: "MoonshotAI: Kimi K2.5",
|
|
6259
|
+
api: "openai-completions",
|
|
6260
|
+
provider: "openrouter",
|
|
6261
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
6262
|
+
reasoning: true,
|
|
6263
|
+
input: ["text", "image"],
|
|
6264
|
+
cost: {
|
|
6265
|
+
input: 0.6,
|
|
6266
|
+
output: 3,
|
|
6267
|
+
cacheRead: 0.09999999999999999,
|
|
6268
|
+
cacheWrite: 0,
|
|
6269
|
+
},
|
|
6270
|
+
contextWindow: 262144,
|
|
6271
|
+
maxTokens: 4096,
|
|
6272
|
+
} satisfies Model<"openai-completions">,
|
|
6290
6273
|
"nex-agi/deepseek-v3.1-nex-n1": {
|
|
6291
6274
|
id: "nex-agi/deepseek-v3.1-nex-n1",
|
|
6292
6275
|
name: "Nex AGI: DeepSeek V3.1 Nex N1",
|
|
@@ -7726,7 +7709,7 @@ export const MODELS = {
|
|
|
7726
7709
|
cost: {
|
|
7727
7710
|
input: 0.22,
|
|
7728
7711
|
output: 1.7999999999999998,
|
|
7729
|
-
cacheRead: 0,
|
|
7712
|
+
cacheRead: 0.022,
|
|
7730
7713
|
cacheWrite: 0,
|
|
7731
7714
|
},
|
|
7732
7715
|
contextWindow: 262144,
|
|
@@ -7862,7 +7845,7 @@ export const MODELS = {
|
|
|
7862
7845
|
cost: {
|
|
7863
7846
|
input: 0.15,
|
|
7864
7847
|
output: 0.6,
|
|
7865
|
-
cacheRead: 0,
|
|
7848
|
+
cacheRead: 0.075,
|
|
7866
7849
|
cacheWrite: 0,
|
|
7867
7850
|
},
|
|
7868
7851
|
contextWindow: 262144,
|
|
@@ -8089,6 +8072,23 @@ export const MODELS = {
|
|
|
8089
8072
|
contextWindow: 163840,
|
|
8090
8073
|
maxTokens: 65536,
|
|
8091
8074
|
} satisfies Model<"openai-completions">,
|
|
8075
|
+
"upstage/solar-pro-3:free": {
|
|
8076
|
+
id: "upstage/solar-pro-3:free",
|
|
8077
|
+
name: "Upstage: Solar Pro 3 (free)",
|
|
8078
|
+
api: "openai-completions",
|
|
8079
|
+
provider: "openrouter",
|
|
8080
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
8081
|
+
reasoning: true,
|
|
8082
|
+
input: ["text"],
|
|
8083
|
+
cost: {
|
|
8084
|
+
input: 0,
|
|
8085
|
+
output: 0,
|
|
8086
|
+
cacheRead: 0,
|
|
8087
|
+
cacheWrite: 0,
|
|
8088
|
+
},
|
|
8089
|
+
contextWindow: 128000,
|
|
8090
|
+
maxTokens: 4096,
|
|
8091
|
+
} satisfies Model<"openai-completions">,
|
|
8092
8092
|
"x-ai/grok-3": {
|
|
8093
8093
|
id: "x-ai/grok-3",
|
|
8094
8094
|
name: "xAI: Grok 3",
|
|
@@ -8242,23 +8242,6 @@ export const MODELS = {
|
|
|
8242
8242
|
contextWindow: 262144,
|
|
8243
8243
|
maxTokens: 4096,
|
|
8244
8244
|
} satisfies Model<"openai-completions">,
|
|
8245
|
-
"xiaomi/mimo-v2-flash:free": {
|
|
8246
|
-
id: "xiaomi/mimo-v2-flash:free",
|
|
8247
|
-
name: "Xiaomi: MiMo-V2-Flash (free)",
|
|
8248
|
-
api: "openai-completions",
|
|
8249
|
-
provider: "openrouter",
|
|
8250
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
8251
|
-
reasoning: true,
|
|
8252
|
-
input: ["text"],
|
|
8253
|
-
cost: {
|
|
8254
|
-
input: 0,
|
|
8255
|
-
output: 0,
|
|
8256
|
-
cacheRead: 0,
|
|
8257
|
-
cacheWrite: 0,
|
|
8258
|
-
},
|
|
8259
|
-
contextWindow: 262144,
|
|
8260
|
-
maxTokens: 65536,
|
|
8261
|
-
} satisfies Model<"openai-completions">,
|
|
8262
8245
|
"z-ai/glm-4-32b": {
|
|
8263
8246
|
id: "z-ai/glm-4-32b",
|
|
8264
8247
|
name: "Z.AI: GLM 4 32B ",
|
|
@@ -8372,7 +8355,7 @@ export const MODELS = {
|
|
|
8372
8355
|
cost: {
|
|
8373
8356
|
input: 0.44,
|
|
8374
8357
|
output: 1.76,
|
|
8375
|
-
cacheRead: 0,
|
|
8358
|
+
cacheRead: 0.11,
|
|
8376
8359
|
cacheWrite: 0,
|
|
8377
8360
|
},
|
|
8378
8361
|
contextWindow: 204800,
|
|
@@ -8601,6 +8584,23 @@ export const MODELS = {
|
|
|
8601
8584
|
contextWindow: 262144,
|
|
8602
8585
|
maxTokens: 32768,
|
|
8603
8586
|
} satisfies Model<"anthropic-messages">,
|
|
8587
|
+
"alibaba/qwen3-max-thinking": {
|
|
8588
|
+
id: "alibaba/qwen3-max-thinking",
|
|
8589
|
+
name: "Qwen 3 Max Thinking",
|
|
8590
|
+
api: "anthropic-messages",
|
|
8591
|
+
provider: "vercel-ai-gateway",
|
|
8592
|
+
baseUrl: "https://ai-gateway.vercel.sh",
|
|
8593
|
+
reasoning: true,
|
|
8594
|
+
input: ["text"],
|
|
8595
|
+
cost: {
|
|
8596
|
+
input: 1.2,
|
|
8597
|
+
output: 6,
|
|
8598
|
+
cacheRead: 0.24,
|
|
8599
|
+
cacheWrite: 0,
|
|
8600
|
+
},
|
|
8601
|
+
contextWindow: 256000,
|
|
8602
|
+
maxTokens: 256000,
|
|
8603
|
+
} satisfies Model<"anthropic-messages">,
|
|
8604
8604
|
"anthropic/claude-3-haiku": {
|
|
8605
8605
|
id: "anthropic/claude-3-haiku",
|
|
8606
8606
|
name: "Claude 3 Haiku",
|
|
@@ -9485,6 +9485,23 @@ export const MODELS = {
|
|
|
9485
9485
|
contextWindow: 256000,
|
|
9486
9486
|
maxTokens: 16384,
|
|
9487
9487
|
} satisfies Model<"anthropic-messages">,
|
|
9488
|
+
"moonshotai/kimi-k2.5": {
|
|
9489
|
+
id: "moonshotai/kimi-k2.5",
|
|
9490
|
+
name: "Kimi K2.5",
|
|
9491
|
+
api: "anthropic-messages",
|
|
9492
|
+
provider: "vercel-ai-gateway",
|
|
9493
|
+
baseUrl: "https://ai-gateway.vercel.sh",
|
|
9494
|
+
reasoning: true,
|
|
9495
|
+
input: ["text", "image"],
|
|
9496
|
+
cost: {
|
|
9497
|
+
input: 1.2,
|
|
9498
|
+
output: 1.2,
|
|
9499
|
+
cacheRead: 0.6,
|
|
9500
|
+
cacheWrite: 0,
|
|
9501
|
+
},
|
|
9502
|
+
contextWindow: 256000,
|
|
9503
|
+
maxTokens: 256000,
|
|
9504
|
+
} satisfies Model<"anthropic-messages">,
|
|
9488
9505
|
"nvidia/nemotron-nano-12b-v2-vl": {
|
|
9489
9506
|
id: "nvidia/nemotron-nano-12b-v2-vl",
|
|
9490
9507
|
name: "Nvidia Nemotron Nano 12B V2 VL",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BedrockRuntimeClient,
|
|
3
|
+
type BedrockRuntimeClientConfig,
|
|
3
4
|
StopReason as BedrockStopReason,
|
|
4
5
|
type Tool as BedrockTool,
|
|
5
6
|
CachePointType,
|
|
@@ -89,11 +90,42 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
|
|
|
89
90
|
|
|
90
91
|
const blocks = output.content as Block[];
|
|
91
92
|
|
|
93
|
+
const config: BedrockRuntimeClientConfig = {
|
|
94
|
+
region: options.region,
|
|
95
|
+
profile: options.profile,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// in Node.js/Bun environment only
|
|
99
|
+
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
|
100
|
+
config.region = config.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION;
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
process.env.HTTP_PROXY ||
|
|
104
|
+
process.env.HTTPS_PROXY ||
|
|
105
|
+
process.env.NO_PROXY ||
|
|
106
|
+
process.env.http_proxy ||
|
|
107
|
+
process.env.https_proxy ||
|
|
108
|
+
process.env.no_proxy
|
|
109
|
+
) {
|
|
110
|
+
const nodeHttpHandler = await import("@smithy/node-http-handler");
|
|
111
|
+
const proxyAgent = await import("proxy-agent");
|
|
112
|
+
|
|
113
|
+
const agent = new proxyAgent.ProxyAgent();
|
|
114
|
+
|
|
115
|
+
// Bedrock runtime uses NodeHttp2Handler by default since v3.798.0, which is based
|
|
116
|
+
// on `http2` module and has no support for http agent.
|
|
117
|
+
// Use NodeHttpHandler to support http agent.
|
|
118
|
+
config.requestHandler = new nodeHttpHandler.NodeHttpHandler({
|
|
119
|
+
httpAgent: agent,
|
|
120
|
+
httpsAgent: agent,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
config.region = config.region || "us-east-1";
|
|
126
|
+
|
|
92
127
|
try {
|
|
93
|
-
const client = new BedrockRuntimeClient(
|
|
94
|
-
region: options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || "us-east-1",
|
|
95
|
-
profile: options.profile,
|
|
96
|
-
});
|
|
128
|
+
const client = new BedrockRuntimeClient(config);
|
|
97
129
|
|
|
98
130
|
const commandInput = {
|
|
99
131
|
modelId: model.id,
|
|
@@ -321,7 +321,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
324
|
-
throw new Error("An
|
|
324
|
+
throw new Error("An unknown error occurred");
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
output.duration = Date.now() - startTime;
|
|
@@ -337,7 +337,7 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
340
|
-
throw new Error("An
|
|
340
|
+
throw new Error("An unknown error occurred");
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
output.duration = Date.now() - startTime;
|
package/src/providers/cursor.ts
CHANGED
|
@@ -3,7 +3,6 @@ import * as fs from "node:fs/promises";
|
|
|
3
3
|
import http2 from "node:http2";
|
|
4
4
|
import { create, fromBinary, fromJson, type JsonValue, toBinary, toJson } from "@bufbuild/protobuf";
|
|
5
5
|
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
6
|
-
import JSON5 from "json5";
|
|
7
6
|
import { calculateCost } from "../models";
|
|
8
7
|
import type {
|
|
9
8
|
Api,
|
|
@@ -1509,7 +1508,7 @@ function parseToolArgsJson(text: string): unknown {
|
|
|
1509
1508
|
.replace(/\bNone\b/g, "null")
|
|
1510
1509
|
.replace(/\bTrue\b/g, "true")
|
|
1511
1510
|
.replace(/\bFalse\b/g, "false");
|
|
1512
|
-
return JSON5.parse(normalized);
|
|
1511
|
+
return Bun.JSON5.parse(normalized);
|
|
1513
1512
|
} catch {}
|
|
1514
1513
|
return text;
|
|
1515
1514
|
}
|
|
@@ -527,6 +527,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
527
527
|
const reader = activeResponse.body.getReader();
|
|
528
528
|
const decoder = new TextDecoder();
|
|
529
529
|
let buffer = "";
|
|
530
|
+
let jsonlBuffer = "";
|
|
530
531
|
|
|
531
532
|
// Set up abort handler to cancel reader when signal fires
|
|
532
533
|
const abortHandler = () => {
|
|
@@ -553,14 +554,17 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
553
554
|
|
|
554
555
|
const jsonStr = line.slice(5).trim();
|
|
555
556
|
if (!jsonStr) continue;
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
557
|
+
jsonlBuffer += `${jsonStr}\n`;
|
|
558
|
+
const parsed = Bun.JSONL.parseChunk(jsonlBuffer);
|
|
559
|
+
jsonlBuffer = jsonlBuffer.slice(parsed.read);
|
|
560
|
+
if (parsed.error) {
|
|
561
|
+
jsonlBuffer = "";
|
|
561
562
|
continue;
|
|
562
563
|
}
|
|
563
564
|
|
|
565
|
+
const chunk = parsed.values[0] as CloudCodeAssistResponseChunk | undefined;
|
|
566
|
+
if (!chunk) continue;
|
|
567
|
+
|
|
564
568
|
// Unwrap the response
|
|
565
569
|
const responseData = chunk.response;
|
|
566
570
|
if (!responseData) continue;
|
|
@@ -145,10 +145,19 @@ export function convertMessages<T extends GoogleApiType>(model: Model<T>, contex
|
|
|
145
145
|
} else if (block.type === "toolCall") {
|
|
146
146
|
const thoughtSignature = resolveThoughtSignature(isSameProviderAndModel, block.thoughtSignature);
|
|
147
147
|
if (isGemini3Model(model.id) && !thoughtSignature) {
|
|
148
|
-
const
|
|
148
|
+
const params = Object.entries(block.arguments ?? {})
|
|
149
|
+
.map(([key, value]) => {
|
|
150
|
+
const valueStr = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
151
|
+
return `<parameter name="${key}">${valueStr}</parameter>`;
|
|
152
|
+
})
|
|
153
|
+
.join("\n");
|
|
154
|
+
|
|
149
155
|
parts.push({
|
|
150
156
|
text: sanitizeSurrogates(
|
|
151
|
-
|
|
157
|
+
`<call_record tool="${block.name}">
|
|
158
|
+
<critical>Historical context only. You cannot invoke tools this way—use proper function calling.</critical>
|
|
159
|
+
${params}
|
|
160
|
+
</call_record>`,
|
|
152
161
|
),
|
|
153
162
|
});
|
|
154
163
|
continue;
|
package/src/providers/google.ts
CHANGED
|
@@ -246,7 +246,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = (
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
249
|
-
throw new Error("An
|
|
249
|
+
throw new Error("An unknown error occurred");
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
output.duration = Date.now() - startTime;
|
|
@@ -309,7 +309,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
312
|
-
throw new Error("An
|
|
312
|
+
throw new Error("An unknown error occurred");
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
output.duration = Date.now() - startTime;
|
|
@@ -504,26 +504,31 @@ export function convertMessages(
|
|
|
504
504
|
|
|
505
505
|
if (msg.role === "user") {
|
|
506
506
|
if (typeof msg.content === "string") {
|
|
507
|
+
const text = sanitizeSurrogates(msg.content);
|
|
508
|
+
if (text.trim().length === 0) continue;
|
|
507
509
|
params.push({
|
|
508
510
|
role: "user",
|
|
509
|
-
content:
|
|
511
|
+
content: text,
|
|
510
512
|
});
|
|
511
513
|
} else {
|
|
512
|
-
const content: ChatCompletionContentPart[] =
|
|
514
|
+
const content: ChatCompletionContentPart[] = [];
|
|
515
|
+
for (const item of msg.content) {
|
|
513
516
|
if (item.type === "text") {
|
|
514
|
-
|
|
517
|
+
const text = sanitizeSurrogates(item.text);
|
|
518
|
+
if (text.trim().length === 0) continue;
|
|
519
|
+
content.push({
|
|
515
520
|
type: "text",
|
|
516
|
-
text
|
|
517
|
-
} satisfies ChatCompletionContentPartText;
|
|
521
|
+
text,
|
|
522
|
+
} satisfies ChatCompletionContentPartText);
|
|
518
523
|
} else {
|
|
519
|
-
|
|
524
|
+
content.push({
|
|
520
525
|
type: "image_url",
|
|
521
526
|
image_url: {
|
|
522
527
|
url: `data:${item.mimeType};base64,${item.data}`,
|
|
523
528
|
},
|
|
524
|
-
} satisfies ChatCompletionContentPartImage;
|
|
529
|
+
} satisfies ChatCompletionContentPartImage);
|
|
525
530
|
}
|
|
526
|
-
}
|
|
531
|
+
}
|
|
527
532
|
const filteredContent = !model.input.includes("image")
|
|
528
533
|
? content.filter(c => c.type !== "image_url")
|
|
529
534
|
: content;
|
|
@@ -578,7 +583,36 @@ export function convertMessages(
|
|
|
578
583
|
}
|
|
579
584
|
}
|
|
580
585
|
|
|
586
|
+
if (compat.thinkingFormat === "openai") {
|
|
587
|
+
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
|
588
|
+
const reasoningContent = (assistantMsg as any)[reasoningField];
|
|
589
|
+
if (!reasoningContent) {
|
|
590
|
+
const reasoning = (assistantMsg as any).reasoning;
|
|
591
|
+
const reasoningText = (assistantMsg as any).reasoning_text;
|
|
592
|
+
if (reasoning && reasoningField !== "reasoning") {
|
|
593
|
+
(assistantMsg as any)[reasoningField] = reasoning;
|
|
594
|
+
} else if (reasoningText && reasoningField !== "reasoning_text") {
|
|
595
|
+
(assistantMsg as any)[reasoningField] = reasoningText;
|
|
596
|
+
} else if (nonEmptyThinkingBlocks.length > 0) {
|
|
597
|
+
(assistantMsg as any)[reasoningField] = nonEmptyThinkingBlocks.map(b => b.thinking).join("\n");
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
581
602
|
const toolCalls = msg.content.filter(b => b.type === "toolCall") as ToolCall[];
|
|
603
|
+
const hasReasoningField =
|
|
604
|
+
(assistantMsg as any).reasoning_content !== undefined ||
|
|
605
|
+
(assistantMsg as any).reasoning !== undefined ||
|
|
606
|
+
(assistantMsg as any).reasoning_text !== undefined;
|
|
607
|
+
if (
|
|
608
|
+
toolCalls.length > 0 &&
|
|
609
|
+
compat.requiresReasoningContentForToolCalls &&
|
|
610
|
+
compat.thinkingFormat === "openai" &&
|
|
611
|
+
!hasReasoningField
|
|
612
|
+
) {
|
|
613
|
+
const reasoningField = compat.reasoningContentField ?? "reasoning_content";
|
|
614
|
+
(assistantMsg as any)[reasoningField] = ".";
|
|
615
|
+
}
|
|
582
616
|
if (toolCalls.length > 0) {
|
|
583
617
|
assistantMsg.tool_calls = toolCalls.map(tc => ({
|
|
584
618
|
id: normalizeMistralToolId(tc.id, compat.requiresMistralToolIds),
|
|
@@ -611,6 +645,9 @@ export function convertMessages(
|
|
|
611
645
|
content !== null &&
|
|
612
646
|
content !== undefined &&
|
|
613
647
|
(typeof content === "string" ? content.length > 0 : content.length > 0);
|
|
648
|
+
if (!hasContent && assistantMsg.tool_calls && compat.requiresAssistantContentForToolCalls) {
|
|
649
|
+
assistantMsg.content = ".";
|
|
650
|
+
}
|
|
614
651
|
if (!hasContent && !assistantMsg.tool_calls) {
|
|
615
652
|
continue;
|
|
616
653
|
}
|
|
@@ -732,6 +769,7 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAICompat
|
|
|
732
769
|
const baseUrl = model.baseUrl;
|
|
733
770
|
|
|
734
771
|
const isZai = provider === "zai" || baseUrl.includes("api.z.ai");
|
|
772
|
+
const isOpenRouterKimi = provider === "openrouter" && model.id.includes("moonshotai/kimi");
|
|
735
773
|
|
|
736
774
|
const isNonStandard =
|
|
737
775
|
provider === "cerebras" ||
|
|
@@ -762,6 +800,9 @@ function detectCompat(model: Model<"openai-completions">): ResolvedOpenAICompat
|
|
|
762
800
|
requiresThinkingAsText: isMistral,
|
|
763
801
|
requiresMistralToolIds: isMistral,
|
|
764
802
|
thinkingFormat: isZai ? "zai" : "openai",
|
|
803
|
+
reasoningContentField: "reasoning_content",
|
|
804
|
+
requiresReasoningContentForToolCalls: isOpenRouterKimi,
|
|
805
|
+
requiresAssistantContentForToolCalls: isOpenRouterKimi,
|
|
765
806
|
openRouterRouting: undefined,
|
|
766
807
|
};
|
|
767
808
|
}
|
|
@@ -786,6 +827,11 @@ function getCompat(model: Model<"openai-completions">): ResolvedOpenAICompat {
|
|
|
786
827
|
requiresThinkingAsText: model.compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
|
|
787
828
|
requiresMistralToolIds: model.compat.requiresMistralToolIds ?? detected.requiresMistralToolIds,
|
|
788
829
|
thinkingFormat: model.compat.thinkingFormat ?? detected.thinkingFormat,
|
|
830
|
+
reasoningContentField: model.compat.reasoningContentField ?? detected.reasoningContentField,
|
|
831
|
+
requiresReasoningContentForToolCalls:
|
|
832
|
+
model.compat.requiresReasoningContentForToolCalls ?? detected.requiresReasoningContentForToolCalls,
|
|
833
|
+
requiresAssistantContentForToolCalls:
|
|
834
|
+
model.compat.requiresAssistantContentForToolCalls ?? detected.requiresAssistantContentForToolCalls,
|
|
789
835
|
openRouterRouting: model.compat.openRouterRouting ?? detected.openRouterRouting,
|
|
790
836
|
};
|
|
791
837
|
}
|
|
@@ -313,7 +313,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|
|
313
313
|
}
|
|
314
314
|
|
|
315
315
|
if (output.stopReason === "aborted" || output.stopReason === "error") {
|
|
316
|
-
throw new Error("An
|
|
316
|
+
throw new Error("An unknown error occurred");
|
|
317
317
|
}
|
|
318
318
|
|
|
319
319
|
output.duration = Date.now() - startTime;
|
package/src/stream.ts
CHANGED
|
@@ -29,6 +29,15 @@ import type {
|
|
|
29
29
|
ThinkingLevel,
|
|
30
30
|
} from "./types";
|
|
31
31
|
|
|
32
|
+
// Set up http proxy according to env variables for `fetch` based SDKs in Node.js.
|
|
33
|
+
// Bun has builtin support for this.
|
|
34
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
35
|
+
import("undici").then(m => {
|
|
36
|
+
const { EnvHttpProxyAgent, setGlobalDispatcher } = m;
|
|
37
|
+
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
let cachedVertexAdcCredentialsExists: boolean | null = null;
|
|
33
42
|
|
|
34
43
|
function hasVertexAdcCredentials(): boolean {
|
package/src/types.ts
CHANGED
|
@@ -292,6 +292,12 @@ export interface OpenAICompat {
|
|
|
292
292
|
requiresMistralToolIds?: boolean;
|
|
293
293
|
/** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "zai" uses thinking: { type: "enabled" }. Default: "openai". */
|
|
294
294
|
thinkingFormat?: "openai" | "zai";
|
|
295
|
+
/** Which reasoning content field to emit on assistant messages. Default: auto-detected. */
|
|
296
|
+
reasoningContentField?: "reasoning_content" | "reasoning" | "reasoning_text";
|
|
297
|
+
/** Whether assistant tool-call messages must include reasoning content. Default: false. */
|
|
298
|
+
requiresReasoningContentForToolCalls?: boolean;
|
|
299
|
+
/** Whether assistant tool-call messages must include non-empty content. Default: false. */
|
|
300
|
+
requiresAssistantContentForToolCalls?: boolean;
|
|
295
301
|
/** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */
|
|
296
302
|
openRouterRouting?: OpenRouterRouting;
|
|
297
303
|
}
|