@mariozechner/pi-ai 0.5.26 → 0.5.28
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 +355 -275
- package/dist/generate.d.ts +22 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +204 -0
- package/dist/generate.js.map +1 -0
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -12
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +10 -71
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +3056 -2644
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +3063 -2648
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +17 -59
- package/dist/models.js.map +1 -1
- package/dist/providers/anthropic.d.ts +5 -18
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +249 -227
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/google.d.ts +3 -14
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +215 -220
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +4 -14
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +247 -215
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses.d.ts +6 -13
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +242 -244
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/utils.d.ts +2 -14
- package/dist/providers/utils.d.ts.map +1 -1
- package/dist/providers/utils.js +2 -15
- package/dist/providers/utils.js.map +1 -1
- package/dist/types.d.ts +39 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,60 +1,16 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { QueuedGenerateStream } from "../generate.js";
|
|
2
3
|
import { calculateCost } from "../models.js";
|
|
3
4
|
import { transformMessages } from "./utils.js";
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
isOAuthToken = false;
|
|
8
|
-
constructor(model, apiKey) {
|
|
9
|
-
if (!apiKey) {
|
|
10
|
-
if (!process.env.ANTHROPIC_API_KEY) {
|
|
11
|
-
throw new Error("Anthropic API key is required. Set ANTHROPIC_API_KEY environment variable or pass it as an argument.");
|
|
12
|
-
}
|
|
13
|
-
apiKey = process.env.ANTHROPIC_API_KEY;
|
|
14
|
-
}
|
|
15
|
-
if (apiKey.includes("sk-ant-oat")) {
|
|
16
|
-
const defaultHeaders = {
|
|
17
|
-
accept: "application/json",
|
|
18
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
19
|
-
"anthropic-beta": "oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14",
|
|
20
|
-
};
|
|
21
|
-
// Clear the env var if we're in Node.js to prevent SDK from using it
|
|
22
|
-
if (typeof process !== "undefined" && process.env) {
|
|
23
|
-
process.env.ANTHROPIC_API_KEY = undefined;
|
|
24
|
-
}
|
|
25
|
-
this.client = new Anthropic({
|
|
26
|
-
apiKey: null,
|
|
27
|
-
authToken: apiKey,
|
|
28
|
-
baseURL: model.baseUrl,
|
|
29
|
-
defaultHeaders,
|
|
30
|
-
dangerouslyAllowBrowser: true,
|
|
31
|
-
});
|
|
32
|
-
this.isOAuthToken = true;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
const defaultHeaders = {
|
|
36
|
-
accept: "application/json",
|
|
37
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
38
|
-
"anthropic-beta": "fine-grained-tool-streaming-2025-05-14",
|
|
39
|
-
};
|
|
40
|
-
this.client = new Anthropic({ apiKey, baseURL: model.baseUrl, dangerouslyAllowBrowser: true, defaultHeaders });
|
|
41
|
-
this.isOAuthToken = false;
|
|
42
|
-
}
|
|
43
|
-
this.modelInfo = model;
|
|
44
|
-
}
|
|
45
|
-
getModel() {
|
|
46
|
-
return this.modelInfo;
|
|
47
|
-
}
|
|
48
|
-
getApi() {
|
|
49
|
-
return "anthropic-messages";
|
|
50
|
-
}
|
|
51
|
-
async generate(context, options) {
|
|
5
|
+
export const streamAnthropic = (model, context, options) => {
|
|
6
|
+
const stream = new QueuedGenerateStream();
|
|
7
|
+
(async () => {
|
|
52
8
|
const output = {
|
|
53
9
|
role: "assistant",
|
|
54
10
|
content: [],
|
|
55
|
-
api:
|
|
56
|
-
provider:
|
|
57
|
-
model:
|
|
11
|
+
api: "anthropic-messages",
|
|
12
|
+
provider: model.provider,
|
|
13
|
+
model: model.id,
|
|
58
14
|
usage: {
|
|
59
15
|
input: 0,
|
|
60
16
|
output: 0,
|
|
@@ -65,67 +21,12 @@ export class AnthropicLLM {
|
|
|
65
21
|
stopReason: "stop",
|
|
66
22
|
};
|
|
67
23
|
try {
|
|
68
|
-
const
|
|
69
|
-
const params =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
max_tokens: options?.maxTokens || 4096,
|
|
73
|
-
stream: true,
|
|
74
|
-
};
|
|
75
|
-
// For OAuth tokens, we MUST include Claude Code identity
|
|
76
|
-
if (this.isOAuthToken) {
|
|
77
|
-
params.system = [
|
|
78
|
-
{
|
|
79
|
-
type: "text",
|
|
80
|
-
text: "You are Claude Code, Anthropic's official CLI for Claude.",
|
|
81
|
-
cache_control: {
|
|
82
|
-
type: "ephemeral",
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
];
|
|
86
|
-
if (context.systemPrompt) {
|
|
87
|
-
params.system.push({
|
|
88
|
-
type: "text",
|
|
89
|
-
text: context.systemPrompt,
|
|
90
|
-
cache_control: {
|
|
91
|
-
type: "ephemeral",
|
|
92
|
-
},
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else if (context.systemPrompt) {
|
|
97
|
-
params.system = context.systemPrompt;
|
|
98
|
-
}
|
|
99
|
-
if (options?.temperature !== undefined) {
|
|
100
|
-
params.temperature = options?.temperature;
|
|
101
|
-
}
|
|
102
|
-
if (context.tools) {
|
|
103
|
-
params.tools = this.convertTools(context.tools);
|
|
104
|
-
}
|
|
105
|
-
// Only enable thinking if the model supports it
|
|
106
|
-
if (options?.thinking?.enabled && this.modelInfo.reasoning) {
|
|
107
|
-
params.thinking = {
|
|
108
|
-
type: "enabled",
|
|
109
|
-
budget_tokens: options.thinking.budgetTokens || 1024,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
if (options?.toolChoice) {
|
|
113
|
-
if (typeof options.toolChoice === "string") {
|
|
114
|
-
params.tool_choice = { type: options.toolChoice };
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
params.tool_choice = options.toolChoice;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const stream = this.client.messages.stream({
|
|
121
|
-
...params,
|
|
122
|
-
stream: true,
|
|
123
|
-
}, {
|
|
124
|
-
signal: options?.signal,
|
|
125
|
-
});
|
|
126
|
-
options?.onEvent?.({ type: "start", model: this.modelInfo.id, provider: this.modelInfo.provider });
|
|
24
|
+
const { client, isOAuthToken } = createClient(model, options?.apiKey);
|
|
25
|
+
const params = buildParams(model, context, isOAuthToken, options);
|
|
26
|
+
const anthropicStream = client.messages.stream({ ...params, stream: true }, { signal: options?.signal });
|
|
27
|
+
stream.push({ type: "start", partial: output });
|
|
127
28
|
let currentBlock = null;
|
|
128
|
-
for await (const event of
|
|
29
|
+
for await (const event of anthropicStream) {
|
|
129
30
|
if (event.type === "content_block_start") {
|
|
130
31
|
if (event.content_block.type === "text") {
|
|
131
32
|
currentBlock = {
|
|
@@ -133,7 +34,7 @@ export class AnthropicLLM {
|
|
|
133
34
|
text: "",
|
|
134
35
|
};
|
|
135
36
|
output.content.push(currentBlock);
|
|
136
|
-
|
|
37
|
+
stream.push({ type: "text_start", partial: output });
|
|
137
38
|
}
|
|
138
39
|
else if (event.content_block.type === "thinking") {
|
|
139
40
|
currentBlock = {
|
|
@@ -142,10 +43,10 @@ export class AnthropicLLM {
|
|
|
142
43
|
thinkingSignature: "",
|
|
143
44
|
};
|
|
144
45
|
output.content.push(currentBlock);
|
|
145
|
-
|
|
46
|
+
stream.push({ type: "thinking_start", partial: output });
|
|
146
47
|
}
|
|
147
48
|
else if (event.content_block.type === "tool_use") {
|
|
148
|
-
// We wait for the full tool use to be streamed
|
|
49
|
+
// We wait for the full tool use to be streamed
|
|
149
50
|
currentBlock = {
|
|
150
51
|
type: "toolCall",
|
|
151
52
|
id: event.content_block.id,
|
|
@@ -159,16 +60,20 @@ export class AnthropicLLM {
|
|
|
159
60
|
if (event.delta.type === "text_delta") {
|
|
160
61
|
if (currentBlock && currentBlock.type === "text") {
|
|
161
62
|
currentBlock.text += event.delta.text;
|
|
162
|
-
|
|
63
|
+
stream.push({
|
|
64
|
+
type: "text_delta",
|
|
65
|
+
delta: event.delta.text,
|
|
66
|
+
partial: output,
|
|
67
|
+
});
|
|
163
68
|
}
|
|
164
69
|
}
|
|
165
70
|
else if (event.delta.type === "thinking_delta") {
|
|
166
71
|
if (currentBlock && currentBlock.type === "thinking") {
|
|
167
72
|
currentBlock.thinking += event.delta.thinking;
|
|
168
|
-
|
|
73
|
+
stream.push({
|
|
169
74
|
type: "thinking_delta",
|
|
170
|
-
content: currentBlock.thinking,
|
|
171
75
|
delta: event.delta.thinking,
|
|
76
|
+
partial: output,
|
|
172
77
|
});
|
|
173
78
|
}
|
|
174
79
|
}
|
|
@@ -187,10 +92,18 @@ export class AnthropicLLM {
|
|
|
187
92
|
else if (event.type === "content_block_stop") {
|
|
188
93
|
if (currentBlock) {
|
|
189
94
|
if (currentBlock.type === "text") {
|
|
190
|
-
|
|
95
|
+
stream.push({
|
|
96
|
+
type: "text_end",
|
|
97
|
+
content: currentBlock.text,
|
|
98
|
+
partial: output,
|
|
99
|
+
});
|
|
191
100
|
}
|
|
192
101
|
else if (currentBlock.type === "thinking") {
|
|
193
|
-
|
|
102
|
+
stream.push({
|
|
103
|
+
type: "thinking_end",
|
|
104
|
+
content: currentBlock.thinking,
|
|
105
|
+
partial: output,
|
|
106
|
+
});
|
|
194
107
|
}
|
|
195
108
|
else if (currentBlock.type === "toolCall") {
|
|
196
109
|
const finalToolCall = {
|
|
@@ -200,150 +113,259 @@ export class AnthropicLLM {
|
|
|
200
113
|
arguments: JSON.parse(currentBlock.partialJson),
|
|
201
114
|
};
|
|
202
115
|
output.content.push(finalToolCall);
|
|
203
|
-
|
|
116
|
+
stream.push({
|
|
117
|
+
type: "toolCall",
|
|
118
|
+
toolCall: finalToolCall,
|
|
119
|
+
partial: output,
|
|
120
|
+
});
|
|
204
121
|
}
|
|
205
122
|
currentBlock = null;
|
|
206
123
|
}
|
|
207
124
|
}
|
|
208
125
|
else if (event.type === "message_delta") {
|
|
209
126
|
if (event.delta.stop_reason) {
|
|
210
|
-
output.stopReason =
|
|
127
|
+
output.stopReason = mapStopReason(event.delta.stop_reason);
|
|
211
128
|
}
|
|
212
129
|
output.usage.input += event.usage.input_tokens || 0;
|
|
213
130
|
output.usage.output += event.usage.output_tokens || 0;
|
|
214
131
|
output.usage.cacheRead += event.usage.cache_read_input_tokens || 0;
|
|
215
132
|
output.usage.cacheWrite += event.usage.cache_creation_input_tokens || 0;
|
|
216
|
-
calculateCost(
|
|
133
|
+
calculateCost(model, output.usage);
|
|
217
134
|
}
|
|
218
135
|
}
|
|
219
|
-
options?.
|
|
220
|
-
|
|
136
|
+
if (options?.signal?.aborted) {
|
|
137
|
+
throw new Error("Request was aborted");
|
|
138
|
+
}
|
|
139
|
+
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
140
|
+
stream.end();
|
|
221
141
|
}
|
|
222
142
|
catch (error) {
|
|
223
143
|
output.stopReason = "error";
|
|
224
144
|
output.error = error instanceof Error ? error.message : JSON.stringify(error);
|
|
225
|
-
|
|
226
|
-
|
|
145
|
+
stream.push({ type: "error", error: output.error, partial: output });
|
|
146
|
+
stream.end();
|
|
227
147
|
}
|
|
148
|
+
})();
|
|
149
|
+
return stream;
|
|
150
|
+
};
|
|
151
|
+
function createClient(model, apiKey) {
|
|
152
|
+
if (apiKey.includes("sk-ant-oat")) {
|
|
153
|
+
const defaultHeaders = {
|
|
154
|
+
accept: "application/json",
|
|
155
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
156
|
+
"anthropic-beta": "oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14",
|
|
157
|
+
};
|
|
158
|
+
// Clear the env var if we're in Node.js to prevent SDK from using it
|
|
159
|
+
if (typeof process !== "undefined" && process.env) {
|
|
160
|
+
process.env.ANTHROPIC_API_KEY = undefined;
|
|
161
|
+
}
|
|
162
|
+
const client = new Anthropic({
|
|
163
|
+
apiKey: null,
|
|
164
|
+
authToken: apiKey,
|
|
165
|
+
baseURL: model.baseUrl,
|
|
166
|
+
defaultHeaders,
|
|
167
|
+
dangerouslyAllowBrowser: true,
|
|
168
|
+
});
|
|
169
|
+
return { client, isOAuthToken: true };
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const defaultHeaders = {
|
|
173
|
+
accept: "application/json",
|
|
174
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
175
|
+
"anthropic-beta": "fine-grained-tool-streaming-2025-05-14",
|
|
176
|
+
};
|
|
177
|
+
const client = new Anthropic({
|
|
178
|
+
apiKey,
|
|
179
|
+
baseURL: model.baseUrl,
|
|
180
|
+
dangerouslyAllowBrowser: true,
|
|
181
|
+
defaultHeaders,
|
|
182
|
+
});
|
|
183
|
+
return { client, isOAuthToken: false };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function buildParams(model, context, isOAuthToken, options) {
|
|
187
|
+
const params = {
|
|
188
|
+
model: model.id,
|
|
189
|
+
messages: convertMessages(context.messages, model),
|
|
190
|
+
max_tokens: options?.maxTokens || model.maxTokens,
|
|
191
|
+
stream: true,
|
|
192
|
+
};
|
|
193
|
+
// For OAuth tokens, we MUST include Claude Code identity
|
|
194
|
+
if (isOAuthToken) {
|
|
195
|
+
params.system = [
|
|
196
|
+
{
|
|
197
|
+
type: "text",
|
|
198
|
+
text: "You are Claude Code, Anthropic's official CLI for Claude.",
|
|
199
|
+
cache_control: {
|
|
200
|
+
type: "ephemeral",
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
if (context.systemPrompt) {
|
|
205
|
+
params.system.push({
|
|
206
|
+
type: "text",
|
|
207
|
+
text: context.systemPrompt,
|
|
208
|
+
cache_control: {
|
|
209
|
+
type: "ephemeral",
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (context.systemPrompt) {
|
|
215
|
+
params.system = context.systemPrompt;
|
|
216
|
+
}
|
|
217
|
+
if (options?.temperature !== undefined) {
|
|
218
|
+
params.temperature = options.temperature;
|
|
228
219
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
220
|
+
if (context.tools) {
|
|
221
|
+
params.tools = convertTools(context.tools);
|
|
222
|
+
}
|
|
223
|
+
if (options?.thinkingEnabled && model.reasoning) {
|
|
224
|
+
params.thinking = {
|
|
225
|
+
type: "enabled",
|
|
226
|
+
budget_tokens: options.thinkingBudgetTokens || 1024,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (options?.toolChoice) {
|
|
230
|
+
if (typeof options.toolChoice === "string") {
|
|
231
|
+
params.tool_choice = { type: options.toolChoice };
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
params.tool_choice = options.toolChoice;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return params;
|
|
238
|
+
}
|
|
239
|
+
function convertMessages(messages, model) {
|
|
240
|
+
const params = [];
|
|
241
|
+
// Transform messages for cross-provider compatibility
|
|
242
|
+
const transformedMessages = transformMessages(messages, model);
|
|
243
|
+
for (const msg of transformedMessages) {
|
|
244
|
+
if (msg.role === "user") {
|
|
245
|
+
if (typeof msg.content === "string") {
|
|
246
|
+
if (msg.content.trim().length > 0) {
|
|
237
247
|
params.push({
|
|
238
248
|
role: "user",
|
|
239
249
|
content: msg.content,
|
|
240
250
|
});
|
|
241
251
|
}
|
|
242
|
-
else {
|
|
243
|
-
// Convert array content to Anthropic format
|
|
244
|
-
const blocks = msg.content.map((item) => {
|
|
245
|
-
if (item.type === "text") {
|
|
246
|
-
return {
|
|
247
|
-
type: "text",
|
|
248
|
-
text: item.text,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
// Image content
|
|
253
|
-
return {
|
|
254
|
-
type: "image",
|
|
255
|
-
source: {
|
|
256
|
-
type: "base64",
|
|
257
|
-
media_type: item.mimeType,
|
|
258
|
-
data: item.data,
|
|
259
|
-
},
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
const filteredBlocks = !this.modelInfo?.input.includes("image")
|
|
264
|
-
? blocks.filter((b) => b.type !== "image")
|
|
265
|
-
: blocks;
|
|
266
|
-
params.push({
|
|
267
|
-
role: "user",
|
|
268
|
-
content: filteredBlocks,
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
252
|
}
|
|
272
|
-
else
|
|
273
|
-
const blocks =
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
blocks.push({
|
|
253
|
+
else {
|
|
254
|
+
const blocks = msg.content.map((item) => {
|
|
255
|
+
if (item.type === "text") {
|
|
256
|
+
return {
|
|
277
257
|
type: "text",
|
|
278
|
-
text:
|
|
279
|
-
}
|
|
258
|
+
text: item.text,
|
|
259
|
+
};
|
|
280
260
|
}
|
|
281
|
-
else
|
|
282
|
-
|
|
283
|
-
type: "
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
261
|
+
else {
|
|
262
|
+
return {
|
|
263
|
+
type: "image",
|
|
264
|
+
source: {
|
|
265
|
+
type: "base64",
|
|
266
|
+
media_type: item.mimeType,
|
|
267
|
+
data: item.data,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
287
270
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
input: block.arguments,
|
|
294
|
-
});
|
|
271
|
+
});
|
|
272
|
+
let filteredBlocks = !model?.input.includes("image") ? blocks.filter((b) => b.type !== "image") : blocks;
|
|
273
|
+
filteredBlocks = filteredBlocks.filter((b) => {
|
|
274
|
+
if (b.type === "text") {
|
|
275
|
+
return b.text.trim().length > 0;
|
|
295
276
|
}
|
|
296
|
-
|
|
297
|
-
params.push({
|
|
298
|
-
role: "assistant",
|
|
299
|
-
content: blocks,
|
|
277
|
+
return true;
|
|
300
278
|
});
|
|
301
|
-
|
|
302
|
-
|
|
279
|
+
if (filteredBlocks.length === 0)
|
|
280
|
+
continue;
|
|
303
281
|
params.push({
|
|
304
282
|
role: "user",
|
|
305
|
-
content:
|
|
306
|
-
{
|
|
307
|
-
type: "tool_result",
|
|
308
|
-
tool_use_id: msg.toolCallId,
|
|
309
|
-
content: msg.content,
|
|
310
|
-
is_error: msg.isError,
|
|
311
|
-
},
|
|
312
|
-
],
|
|
283
|
+
content: filteredBlocks,
|
|
313
284
|
});
|
|
314
285
|
}
|
|
315
286
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
287
|
+
else if (msg.role === "assistant") {
|
|
288
|
+
const blocks = [];
|
|
289
|
+
for (const block of msg.content) {
|
|
290
|
+
if (block.type === "text") {
|
|
291
|
+
if (block.text.trim().length === 0)
|
|
292
|
+
continue;
|
|
293
|
+
blocks.push({
|
|
294
|
+
type: "text",
|
|
295
|
+
text: block.text,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
else if (block.type === "thinking") {
|
|
299
|
+
if (block.thinking.trim().length === 0)
|
|
300
|
+
continue;
|
|
301
|
+
blocks.push({
|
|
302
|
+
type: "thinking",
|
|
303
|
+
thinking: block.thinking,
|
|
304
|
+
signature: block.thinkingSignature || "",
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
else if (block.type === "toolCall") {
|
|
308
|
+
blocks.push({
|
|
309
|
+
type: "tool_use",
|
|
310
|
+
id: block.id,
|
|
311
|
+
name: block.name,
|
|
312
|
+
input: block.arguments,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (blocks.length === 0)
|
|
317
|
+
continue;
|
|
318
|
+
params.push({
|
|
319
|
+
role: "assistant",
|
|
320
|
+
content: blocks,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else if (msg.role === "toolResult") {
|
|
324
|
+
params.push({
|
|
325
|
+
role: "user",
|
|
326
|
+
content: [
|
|
327
|
+
{
|
|
328
|
+
type: "tool_result",
|
|
329
|
+
tool_use_id: msg.toolCallId,
|
|
330
|
+
content: msg.content,
|
|
331
|
+
is_error: msg.isError,
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
});
|
|
335
|
+
}
|
|
330
336
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
337
|
+
return params;
|
|
338
|
+
}
|
|
339
|
+
function convertTools(tools) {
|
|
340
|
+
if (!tools)
|
|
341
|
+
return [];
|
|
342
|
+
return tools.map((tool) => ({
|
|
343
|
+
name: tool.name,
|
|
344
|
+
description: tool.description,
|
|
345
|
+
input_schema: {
|
|
346
|
+
type: "object",
|
|
347
|
+
properties: tool.parameters.properties || {},
|
|
348
|
+
required: tool.parameters.required || [],
|
|
349
|
+
},
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
function mapStopReason(reason) {
|
|
353
|
+
switch (reason) {
|
|
354
|
+
case "end_turn":
|
|
355
|
+
return "stop";
|
|
356
|
+
case "max_tokens":
|
|
357
|
+
return "length";
|
|
358
|
+
case "tool_use":
|
|
359
|
+
return "toolUse";
|
|
360
|
+
case "refusal":
|
|
361
|
+
return "safety";
|
|
362
|
+
case "pause_turn": // Stop is good enough -> resubmit
|
|
363
|
+
return "stop";
|
|
364
|
+
case "stop_sequence":
|
|
365
|
+
return "stop"; // We don't supply stop sequences, so this should never happen
|
|
366
|
+
default: {
|
|
367
|
+
const _exhaustive = reason;
|
|
368
|
+
throw new Error(`Unhandled stop reason: ${_exhaustive}`);
|
|
347
369
|
}
|
|
348
370
|
}
|
|
349
371
|
}
|