@oh-my-pi/pi-ai 8.1.0 → 8.2.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 +11 -12
- package/package.json +38 -14
- package/src/cli.ts +6 -6
- package/src/providers/amazon-bedrock.ts +12 -13
- package/src/providers/anthropic.ts +25 -26
- package/src/providers/cursor.ts +57 -57
- package/src/providers/google-gemini-cli-usage.ts +2 -2
- package/src/providers/google-gemini-cli.ts +8 -10
- package/src/providers/google-shared.ts +12 -13
- package/src/providers/google-vertex.ts +7 -7
- package/src/providers/google.ts +8 -8
- package/src/providers/openai-codex/request-transformer.ts +6 -6
- package/src/providers/openai-codex-responses.ts +28 -28
- package/src/providers/openai-completions.ts +39 -39
- package/src/providers/openai-responses.ts +31 -31
- package/src/providers/transform-messages.ts +3 -3
- package/src/storage.ts +29 -19
- package/src/stream.ts +6 -6
- package/src/types.ts +1 -2
- package/src/usage/claude.ts +4 -4
- package/src/usage/github-copilot.ts +3 -4
- package/src/usage/google-antigravity.ts +3 -3
- package/src/usage/openai-codex.ts +4 -4
- package/src/usage/zai.ts +3 -3
- package/src/usage.ts +0 -1
- package/src/utils/event-stream.ts +4 -4
- package/src/utils/oauth/anthropic.ts +0 -1
- package/src/utils/oauth/callback-server.ts +2 -3
- package/src/utils/oauth/github-copilot.ts +2 -3
- package/src/utils/oauth/google-antigravity.ts +0 -1
- package/src/utils/oauth/google-gemini-cli.ts +2 -3
- package/src/utils/oauth/index.ts +11 -12
- package/src/utils/oauth/openai-codex.ts +0 -1
- package/src/utils/overflow.ts +2 -2
- package/src/utils/validation.ts +4 -5
package/README.md
CHANGED
|
@@ -231,7 +231,7 @@ const bookMeetingTool: Tool = {
|
|
|
231
231
|
Tool results use content blocks and can include both text and images:
|
|
232
232
|
|
|
233
233
|
```typescript
|
|
234
|
-
import
|
|
234
|
+
import * as fs from "node:fs";
|
|
235
235
|
|
|
236
236
|
const context: Context = {
|
|
237
237
|
messages: [{ role: "user", content: "What is the weather in London?" }],
|
|
@@ -260,7 +260,7 @@ for (const block of response.content) {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
// Tool results can also include images (for vision-capable models)
|
|
263
|
-
const imageBuffer = readFileSync("chart.png");
|
|
263
|
+
const imageBuffer = fs.readFileSync("chart.png");
|
|
264
264
|
context.messages.push({
|
|
265
265
|
role: "toolResult",
|
|
266
266
|
toolCallId: "tool_xyz",
|
|
@@ -379,7 +379,7 @@ All streaming events emitted during assistant message generation:
|
|
|
379
379
|
Models with vision capabilities can process images. You can check if a model supports images via the `input` property. If you pass images to a non-vision model, they are silently ignored.
|
|
380
380
|
|
|
381
381
|
```typescript
|
|
382
|
-
import
|
|
382
|
+
import * as fs from "node:fs";
|
|
383
383
|
import { getModel, complete } from "@oh-my-pi/pi-ai";
|
|
384
384
|
|
|
385
385
|
const model = getModel("openai", "gpt-4o-mini");
|
|
@@ -389,7 +389,7 @@ if (model.input.includes("image")) {
|
|
|
389
389
|
console.log("Model supports vision");
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
const imageBuffer = readFileSync("image.png");
|
|
392
|
+
const imageBuffer = fs.readFileSync("image.png");
|
|
393
393
|
const base64Image = imageBuffer.toString("base64");
|
|
394
394
|
|
|
395
395
|
const response = await complete(model, {
|
|
@@ -551,10 +551,9 @@ The abort signal allows you to cancel in-progress requests. Aborted requests hav
|
|
|
551
551
|
import { getModel, stream } from "@oh-my-pi/pi-ai";
|
|
552
552
|
|
|
553
553
|
const model = getModel("openai", "gpt-4o-mini");
|
|
554
|
-
const controller = new AbortController();
|
|
555
554
|
|
|
556
555
|
// Abort after 2 seconds
|
|
557
|
-
|
|
556
|
+
const signal = AbortSignal.timeout(2000);
|
|
558
557
|
|
|
559
558
|
const s = stream(
|
|
560
559
|
model,
|
|
@@ -562,7 +561,7 @@ const s = stream(
|
|
|
562
561
|
messages: [{ role: "user", content: "Write a long story" }],
|
|
563
562
|
},
|
|
564
563
|
{
|
|
565
|
-
signal
|
|
564
|
+
signal,
|
|
566
565
|
},
|
|
567
566
|
);
|
|
568
567
|
|
|
@@ -1022,7 +1021,7 @@ await loginOpenAICodex({
|
|
|
1022
1021
|
|
|
1023
1022
|
```typescript
|
|
1024
1023
|
import { loginGitHubCopilot } from "@oh-my-pi/pi-ai";
|
|
1025
|
-
import
|
|
1024
|
+
import * as fs from "node:fs";
|
|
1026
1025
|
|
|
1027
1026
|
const credentials = await loginGitHubCopilot({
|
|
1028
1027
|
onAuth: (url, instructions) => {
|
|
@@ -1037,7 +1036,7 @@ const credentials = await loginGitHubCopilot({
|
|
|
1037
1036
|
|
|
1038
1037
|
// Store credentials yourself
|
|
1039
1038
|
const auth = { "github-copilot": { type: "oauth", ...credentials } };
|
|
1040
|
-
writeFileSync("auth.json", JSON.stringify(auth, null, 2));
|
|
1039
|
+
fs.writeFileSync("auth.json", JSON.stringify(auth, null, 2));
|
|
1041
1040
|
```
|
|
1042
1041
|
|
|
1043
1042
|
### Using OAuth Tokens
|
|
@@ -1046,10 +1045,10 @@ Use `getOAuthApiKey()` to get an API key, automatically refreshing if expired:
|
|
|
1046
1045
|
|
|
1047
1046
|
```typescript
|
|
1048
1047
|
import { getModel, complete, getOAuthApiKey } from "@oh-my-pi/pi-ai";
|
|
1049
|
-
import
|
|
1048
|
+
import * as fs from "node:fs";
|
|
1050
1049
|
|
|
1051
1050
|
// Load your stored credentials
|
|
1052
|
-
const auth = JSON.parse(readFileSync("auth.json", "utf-8"));
|
|
1051
|
+
const auth = JSON.parse(fs.readFileSync("auth.json", "utf-8"));
|
|
1053
1052
|
|
|
1054
1053
|
// Get API key (refreshes if expired)
|
|
1055
1054
|
const result = await getOAuthApiKey("github-copilot", auth);
|
|
@@ -1057,7 +1056,7 @@ if (!result) throw new Error("Not logged in");
|
|
|
1057
1056
|
|
|
1058
1057
|
// Save refreshed credentials
|
|
1059
1058
|
auth["github-copilot"] = { type: "oauth", ...result.newCredentials };
|
|
1060
|
-
writeFileSync("auth.json", JSON.stringify(auth, null, 2));
|
|
1059
|
+
fs.writeFileSync("auth.json", JSON.stringify(auth, null, 2));
|
|
1061
1060
|
|
|
1062
1061
|
// Use the API key
|
|
1063
1062
|
const model = getModel("github-copilot", "gpt-4o");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-ai",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.2.0",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -10,13 +10,37 @@
|
|
|
10
10
|
"types": "./src/index.ts",
|
|
11
11
|
"import": "./src/index.ts"
|
|
12
12
|
},
|
|
13
|
+
"./models": {
|
|
14
|
+
"types": "./src/models.ts",
|
|
15
|
+
"import": "./src/models.ts"
|
|
16
|
+
},
|
|
17
|
+
"./models.generated": {
|
|
18
|
+
"types": "./src/models.generated.ts",
|
|
19
|
+
"import": "./src/models.generated.ts"
|
|
20
|
+
},
|
|
21
|
+
"./stream": {
|
|
22
|
+
"types": "./src/stream.ts",
|
|
23
|
+
"import": "./src/stream.ts"
|
|
24
|
+
},
|
|
25
|
+
"./types": {
|
|
26
|
+
"types": "./src/types.ts",
|
|
27
|
+
"import": "./src/types.ts"
|
|
28
|
+
},
|
|
29
|
+
"./usage": {
|
|
30
|
+
"types": "./src/usage.ts",
|
|
31
|
+
"import": "./src/usage.ts"
|
|
32
|
+
},
|
|
33
|
+
"./storage": {
|
|
34
|
+
"types": "./src/storage.ts",
|
|
35
|
+
"import": "./src/storage.ts"
|
|
36
|
+
},
|
|
37
|
+
"./providers/*": {
|
|
38
|
+
"types": "./src/providers/*.ts",
|
|
39
|
+
"import": "./src/providers/*.ts"
|
|
40
|
+
},
|
|
13
41
|
"./utils/*": {
|
|
14
42
|
"types": "./src/utils/*.ts",
|
|
15
43
|
"import": "./src/utils/*.ts"
|
|
16
|
-
},
|
|
17
|
-
"./*": {
|
|
18
|
-
"types": "./src/*",
|
|
19
|
-
"import": "./src/*"
|
|
20
44
|
}
|
|
21
45
|
},
|
|
22
46
|
"bin": {
|
|
@@ -27,25 +51,25 @@
|
|
|
27
51
|
"README.md"
|
|
28
52
|
],
|
|
29
53
|
"scripts": {
|
|
54
|
+
"check": "tsgo -p tsconfig.json",
|
|
30
55
|
"generate-models": "bun scripts/generate-models.ts",
|
|
31
|
-
"test": "bun test"
|
|
32
|
-
"prepublishOnly": "cp tsconfig.publish.json tsconfig.json"
|
|
56
|
+
"test": "bun test"
|
|
33
57
|
},
|
|
34
58
|
"dependencies": {
|
|
35
|
-
"@oh-my-pi/pi-utils": "
|
|
36
|
-
"@anthropic-ai/sdk": "0.71.2",
|
|
37
|
-
"@aws-sdk/client-bedrock-runtime": "^3.
|
|
59
|
+
"@oh-my-pi/pi-utils": "8.2.0",
|
|
60
|
+
"@anthropic-ai/sdk": "^0.71.2",
|
|
61
|
+
"@aws-sdk/client-bedrock-runtime": "^3.975.0",
|
|
38
62
|
"@bufbuild/protobuf": "^2.10.2",
|
|
39
63
|
"@connectrpc/connect": "^2.1.1",
|
|
40
64
|
"@connectrpc/connect-node": "^2.1.1",
|
|
41
|
-
"@google/genai": "1.
|
|
42
|
-
"@mistralai/mistralai": "1.
|
|
65
|
+
"@google/genai": "^1.38.0",
|
|
66
|
+
"@mistralai/mistralai": "^1.13.0",
|
|
43
67
|
"@sinclair/typebox": "^0.34.41",
|
|
44
68
|
"ajv": "^8.17.1",
|
|
45
69
|
"ajv-formats": "^3.0.1",
|
|
46
70
|
"chalk": "^5.6.2",
|
|
47
71
|
"json5": "^2.2.3",
|
|
48
|
-
"openai": "6.
|
|
72
|
+
"openai": "^6.16.0",
|
|
49
73
|
"partial-json": "^0.1.7",
|
|
50
74
|
"zod-to-json-schema": "^3.24.6"
|
|
51
75
|
},
|
|
@@ -69,6 +93,6 @@
|
|
|
69
93
|
"bun": ">=1.0.0"
|
|
70
94
|
},
|
|
71
95
|
"devDependencies": {
|
|
72
|
-
"@types/node": "^
|
|
96
|
+
"@types/node": "^25.0.10"
|
|
73
97
|
}
|
|
74
98
|
}
|
package/src/cli.ts
CHANGED
|
@@ -23,7 +23,7 @@ async function login(provider: OAuthProvider): Promise<void> {
|
|
|
23
23
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
24
24
|
|
|
25
25
|
const promptFn = (msg: string) => prompt(rl, `${msg} `);
|
|
26
|
-
const storage =
|
|
26
|
+
const storage = await CliAuthStorage.create();
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
let credentials: OAuthCredentials;
|
|
@@ -91,7 +91,7 @@ async function login(provider: OAuthProvider): Promise<void> {
|
|
|
91
91
|
|
|
92
92
|
case "cursor":
|
|
93
93
|
credentials = await loginCursor(
|
|
94
|
-
|
|
94
|
+
url => {
|
|
95
95
|
console.log(`\nOpen this URL in your browser:\n${url}\n`);
|
|
96
96
|
},
|
|
97
97
|
() => {
|
|
@@ -145,7 +145,7 @@ Examples:
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
if (command === "status") {
|
|
148
|
-
const storage =
|
|
148
|
+
const storage = await CliAuthStorage.create();
|
|
149
149
|
try {
|
|
150
150
|
const providers = storage.listProviders();
|
|
151
151
|
if (providers.length === 0) {
|
|
@@ -179,7 +179,7 @@ Examples:
|
|
|
179
179
|
|
|
180
180
|
if (command === "logout") {
|
|
181
181
|
let provider = args[1] as OAuthProvider | undefined;
|
|
182
|
-
const storage =
|
|
182
|
+
const storage = await CliAuthStorage.create();
|
|
183
183
|
|
|
184
184
|
try {
|
|
185
185
|
if (!provider) {
|
|
@@ -243,7 +243,7 @@ Examples:
|
|
|
243
243
|
provider = PROVIDERS[index].id;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
if (!PROVIDERS.some(
|
|
246
|
+
if (!PROVIDERS.some(p => p.id === provider)) {
|
|
247
247
|
console.error(`Unknown provider: ${provider}`);
|
|
248
248
|
console.error(`Use 'bunx @oh-my-pi/pi-ai list' to see available providers`);
|
|
249
249
|
process.exit(1);
|
|
@@ -259,7 +259,7 @@ Examples:
|
|
|
259
259
|
process.exit(1);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
main().catch(
|
|
262
|
+
main().catch(err => {
|
|
263
263
|
console.error("Error:", err.message);
|
|
264
264
|
process.exit(1);
|
|
265
265
|
});
|
|
@@ -17,8 +17,7 @@ import {
|
|
|
17
17
|
type ToolConfiguration,
|
|
18
18
|
ToolResultStatus,
|
|
19
19
|
} from "@aws-sdk/client-bedrock-runtime";
|
|
20
|
-
|
|
21
|
-
import { calculateCost } from "@oh-my-pi/pi-ai/models";
|
|
20
|
+
import { calculateCost } from "../models";
|
|
22
21
|
import type {
|
|
23
22
|
Api,
|
|
24
23
|
AssistantMessage,
|
|
@@ -34,10 +33,10 @@ import type {
|
|
|
34
33
|
Tool,
|
|
35
34
|
ToolCall,
|
|
36
35
|
ToolResultMessage,
|
|
37
|
-
} from "
|
|
38
|
-
import { AssistantMessageEventStream } from "
|
|
39
|
-
import { parseStreamingJson } from "
|
|
40
|
-
import { sanitizeSurrogates } from "
|
|
36
|
+
} from "../types";
|
|
37
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
38
|
+
import { parseStreamingJson } from "../utils/json-parse";
|
|
39
|
+
import { sanitizeSurrogates } from "../utils/sanitize-unicode";
|
|
41
40
|
import { transformMessages } from "./transform-messages";
|
|
42
41
|
|
|
43
42
|
export interface BedrockOptions extends StreamOptions {
|
|
@@ -200,7 +199,7 @@ function handleContentBlockDelta(
|
|
|
200
199
|
): void {
|
|
201
200
|
const contentBlockIndex = event.contentBlockIndex!;
|
|
202
201
|
const delta = event.delta;
|
|
203
|
-
let index = blocks.findIndex(
|
|
202
|
+
let index = blocks.findIndex(b => b.index === contentBlockIndex);
|
|
204
203
|
let block = blocks[index];
|
|
205
204
|
|
|
206
205
|
if (delta?.text !== undefined) {
|
|
@@ -271,7 +270,7 @@ function handleContentBlockStop(
|
|
|
271
270
|
output: AssistantMessage,
|
|
272
271
|
stream: AssistantMessageEventStream,
|
|
273
272
|
): void {
|
|
274
|
-
const index = blocks.findIndex(
|
|
273
|
+
const index = blocks.findIndex(b => b.index === event.contentBlockIndex);
|
|
275
274
|
const block = blocks[index];
|
|
276
275
|
if (!block) return;
|
|
277
276
|
delete (block as Block).index;
|
|
@@ -351,7 +350,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
351
350
|
});
|
|
352
351
|
} else {
|
|
353
352
|
const contentBlocks = m.content
|
|
354
|
-
.map(
|
|
353
|
+
.map(c => {
|
|
355
354
|
switch (c.type) {
|
|
356
355
|
case "text":
|
|
357
356
|
return { text: sanitizeSurrogates(c.text) };
|
|
@@ -361,7 +360,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
361
360
|
throw new Error("Unknown user content type");
|
|
362
361
|
}
|
|
363
362
|
})
|
|
364
|
-
.filter(
|
|
363
|
+
.filter(block => {
|
|
365
364
|
// Filter out empty text blocks
|
|
366
365
|
if ("text" in block && block.text) {
|
|
367
366
|
return block.text.trim().length > 0;
|
|
@@ -442,7 +441,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
442
441
|
toolResults.push({
|
|
443
442
|
toolResult: {
|
|
444
443
|
toolUseId: sanitizeToolCallId(m.toolCallId),
|
|
445
|
-
content: m.content.map(
|
|
444
|
+
content: m.content.map(c =>
|
|
446
445
|
c.type === "image"
|
|
447
446
|
? { image: createImageBlock(c.mimeType, c.data) }
|
|
448
447
|
: { text: sanitizeSurrogates(c.text) },
|
|
@@ -458,7 +457,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
458
457
|
toolResults.push({
|
|
459
458
|
toolResult: {
|
|
460
459
|
toolUseId: sanitizeToolCallId(nextMsg.toolCallId),
|
|
461
|
-
content: nextMsg.content.map(
|
|
460
|
+
content: nextMsg.content.map(c =>
|
|
462
461
|
c.type === "image"
|
|
463
462
|
? { image: createImageBlock(c.mimeType, c.data) }
|
|
464
463
|
: { text: sanitizeSurrogates(c.text) },
|
|
@@ -500,7 +499,7 @@ function convertToolConfig(
|
|
|
500
499
|
): ToolConfiguration | undefined {
|
|
501
500
|
if (!tools?.length || toolChoice === "none") return undefined;
|
|
502
501
|
|
|
503
|
-
const bedrockTools: BedrockTool[] = tools.map(
|
|
502
|
+
const bedrockTools: BedrockTool[] = tools.map(tool => ({
|
|
504
503
|
toolSpec: {
|
|
505
504
|
name: tool.name,
|
|
506
505
|
description: tool.description,
|
|
@@ -4,8 +4,8 @@ import type {
|
|
|
4
4
|
MessageCreateParamsStreaming,
|
|
5
5
|
MessageParam,
|
|
6
6
|
} from "@anthropic-ai/sdk/resources/messages";
|
|
7
|
-
import { calculateCost } from "
|
|
8
|
-
import { getEnvApiKey, OUTPUT_FALLBACK_BUFFER } from "
|
|
7
|
+
import { calculateCost } from "../models";
|
|
8
|
+
import { getEnvApiKey, OUTPUT_FALLBACK_BUFFER } from "../stream";
|
|
9
9
|
import type {
|
|
10
10
|
Api,
|
|
11
11
|
AssistantMessage,
|
|
@@ -21,12 +21,11 @@ import type {
|
|
|
21
21
|
Tool,
|
|
22
22
|
ToolCall,
|
|
23
23
|
ToolResultMessage,
|
|
24
|
-
} from "
|
|
25
|
-
import { AssistantMessageEventStream } from "
|
|
26
|
-
import { parseStreamingJson } from "
|
|
27
|
-
import { formatErrorMessageWithRetryAfter } from "
|
|
28
|
-
import { sanitizeSurrogates } from "
|
|
29
|
-
|
|
24
|
+
} from "../types";
|
|
25
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
26
|
+
import { parseStreamingJson } from "../utils/json-parse";
|
|
27
|
+
import { formatErrorMessageWithRetryAfter } from "../utils/retry-after";
|
|
28
|
+
import { sanitizeSurrogates } from "../utils/sanitize-unicode";
|
|
30
29
|
import { transformMessages } from "./transform-messages";
|
|
31
30
|
|
|
32
31
|
// Stealth mode: Mimic Claude Code headers and tool prefixing.
|
|
@@ -89,13 +88,13 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]):
|
|
|
89
88
|
}
|
|
90
89
|
> {
|
|
91
90
|
// If only text blocks, return as concatenated string for simplicity
|
|
92
|
-
const hasImages = content.some(
|
|
91
|
+
const hasImages = content.some(c => c.type === "image");
|
|
93
92
|
if (!hasImages) {
|
|
94
|
-
return sanitizeSurrogates(content.map(
|
|
93
|
+
return sanitizeSurrogates(content.map(c => (c as TextContent).text).join("\n"));
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
// If we have images, convert to content block array
|
|
98
|
-
const blocks = content.map(
|
|
97
|
+
const blocks = content.map(block => {
|
|
99
98
|
if (block.type === "text") {
|
|
100
99
|
return {
|
|
101
100
|
type: "text" as const,
|
|
@@ -113,7 +112,7 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]):
|
|
|
113
112
|
});
|
|
114
113
|
|
|
115
114
|
// If only images (no text), add placeholder text block
|
|
116
|
-
const hasText = blocks.some(
|
|
115
|
+
const hasText = blocks.some(b => b.type === "text");
|
|
117
116
|
if (!hasText) {
|
|
118
117
|
blocks.unshift({
|
|
119
118
|
type: "text" as const,
|
|
@@ -218,7 +217,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
218
217
|
}
|
|
219
218
|
} else if (event.type === "content_block_delta") {
|
|
220
219
|
if (event.delta.type === "text_delta") {
|
|
221
|
-
const index = blocks.findIndex(
|
|
220
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
222
221
|
const block = blocks[index];
|
|
223
222
|
if (block && block.type === "text") {
|
|
224
223
|
block.text += event.delta.text;
|
|
@@ -230,7 +229,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
230
229
|
});
|
|
231
230
|
}
|
|
232
231
|
} else if (event.delta.type === "thinking_delta") {
|
|
233
|
-
const index = blocks.findIndex(
|
|
232
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
234
233
|
const block = blocks[index];
|
|
235
234
|
if (block && block.type === "thinking") {
|
|
236
235
|
block.thinking += event.delta.thinking;
|
|
@@ -242,7 +241,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
242
241
|
});
|
|
243
242
|
}
|
|
244
243
|
} else if (event.delta.type === "input_json_delta") {
|
|
245
|
-
const index = blocks.findIndex(
|
|
244
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
246
245
|
const block = blocks[index];
|
|
247
246
|
if (block && block.type === "toolCall") {
|
|
248
247
|
block.partialJson += event.delta.partial_json;
|
|
@@ -255,7 +254,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
255
254
|
});
|
|
256
255
|
}
|
|
257
256
|
} else if (event.delta.type === "signature_delta") {
|
|
258
|
-
const index = blocks.findIndex(
|
|
257
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
259
258
|
const block = blocks[index];
|
|
260
259
|
if (block && block.type === "thinking") {
|
|
261
260
|
block.thinkingSignature = block.thinkingSignature || "";
|
|
@@ -263,7 +262,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
263
262
|
}
|
|
264
263
|
}
|
|
265
264
|
} else if (event.type === "content_block_stop") {
|
|
266
|
-
const index = blocks.findIndex(
|
|
265
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
267
266
|
const block = blocks[index];
|
|
268
267
|
if (block) {
|
|
269
268
|
delete (block as any).index;
|
|
@@ -360,7 +359,7 @@ function isAnthropicBaseUrl(baseUrl?: string): boolean {
|
|
|
360
359
|
export function normalizeExtraBetas(betas?: string[] | string): string[] {
|
|
361
360
|
if (!betas) return [];
|
|
362
361
|
const raw = Array.isArray(betas) ? betas : betas.split(",");
|
|
363
|
-
return raw.map(
|
|
362
|
+
return raw.map(beta => beta.trim()).filter(beta => beta.length > 0);
|
|
364
363
|
}
|
|
365
364
|
|
|
366
365
|
// Build deduplicated beta header string
|
|
@@ -406,7 +405,7 @@ export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<s
|
|
|
406
405
|
"X-App",
|
|
407
406
|
"Authorization",
|
|
408
407
|
"X-Api-Key",
|
|
409
|
-
].map(
|
|
408
|
+
].map(key => key.toLowerCase()),
|
|
410
409
|
);
|
|
411
410
|
const modelHeaders = Object.fromEntries(
|
|
412
411
|
Object.entries(options.modelHeaders ?? {}).filter(([key]) => !enforcedHeaderKeys.has(key.toLowerCase())),
|
|
@@ -687,7 +686,7 @@ function applyPromptCaching(params: MessageCreateParamsStreaming): void {
|
|
|
687
686
|
// 3. Cache penultimate user message for conversation history caching
|
|
688
687
|
const userIndexes = params.messages
|
|
689
688
|
.map((message, index) => (message.role === "user" ? index : -1))
|
|
690
|
-
.filter(
|
|
689
|
+
.filter(index => index >= 0);
|
|
691
690
|
|
|
692
691
|
if (userIndexes.length >= 2) {
|
|
693
692
|
const penultimateUserIndex = userIndexes[userIndexes.length - 2];
|
|
@@ -748,7 +747,7 @@ function convertMessages(
|
|
|
748
747
|
});
|
|
749
748
|
}
|
|
750
749
|
} else if (Array.isArray(msg.content)) {
|
|
751
|
-
const blocks: Array<ContentBlockParam & CacheControlBlock> = msg.content.map(
|
|
750
|
+
const blocks: Array<ContentBlockParam & CacheControlBlock> = msg.content.map(item => {
|
|
752
751
|
if (item.type === "text") {
|
|
753
752
|
return {
|
|
754
753
|
type: "text",
|
|
@@ -764,8 +763,8 @@ function convertMessages(
|
|
|
764
763
|
},
|
|
765
764
|
};
|
|
766
765
|
});
|
|
767
|
-
let filteredBlocks = !model?.input.includes("image") ? blocks.filter(
|
|
768
|
-
filteredBlocks = filteredBlocks.filter(
|
|
766
|
+
let filteredBlocks = !model?.input.includes("image") ? blocks.filter(b => b.type !== "image") : blocks;
|
|
767
|
+
filteredBlocks = filteredBlocks.filter(b => {
|
|
769
768
|
if (b.type === "text") {
|
|
770
769
|
return b.text.trim().length > 0;
|
|
771
770
|
}
|
|
@@ -786,7 +785,7 @@ function convertMessages(
|
|
|
786
785
|
// block with a missing/invalid signature (e.g., from aborted stream), we must skip
|
|
787
786
|
// the entire message to avoid API rejection. Checking the first non-empty block.
|
|
788
787
|
const firstContentBlock = msg.content.find(
|
|
789
|
-
|
|
788
|
+
b =>
|
|
790
789
|
(b.type === "text" && b.text.trim().length > 0) ||
|
|
791
790
|
(b.type === "thinking" && b.thinking.trim().length > 0) ||
|
|
792
791
|
b.type === "toolCall",
|
|
@@ -889,7 +888,7 @@ function convertMessages(
|
|
|
889
888
|
}
|
|
890
889
|
|
|
891
890
|
// Final validation: filter out any messages with invalid content
|
|
892
|
-
return params.filter(
|
|
891
|
+
return params.filter(msg => {
|
|
893
892
|
if (!msg.content) return false;
|
|
894
893
|
if (typeof msg.content === "string") return msg.content.length > 0;
|
|
895
894
|
if (Array.isArray(msg.content)) return msg.content.length > 0;
|
|
@@ -900,7 +899,7 @@ function convertMessages(
|
|
|
900
899
|
function convertTools(tools: Tool[], isOAuthToken: boolean): Anthropic.Messages.Tool[] {
|
|
901
900
|
if (!tools) return [];
|
|
902
901
|
|
|
903
|
-
return tools.map(
|
|
902
|
+
return tools.map(tool => {
|
|
904
903
|
const jsonSchema = tool.parameters as any; // TypeBox already generates JSON Schema
|
|
905
904
|
|
|
906
905
|
return {
|