smoltalk 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/clients/anthropic.d.ts +1 -0
- package/dist/clients/anthropic.js +43 -18
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import type { MessageParam, Tool } from "@anthropic-ai/sdk/resources/messages.js
|
|
|
2
2
|
import { PromptResult, Result, SmolClient, SmolConfig, StreamChunk, HostedToolResult } from "../types.js";
|
|
3
3
|
import { BaseClient } from "./baseClient.js";
|
|
4
4
|
import { ModelName } from "../models.js";
|
|
5
|
+
export declare function mergeConsecutiveMessages(messages: MessageParam[]): MessageParam[];
|
|
5
6
|
export declare function anthropicWebSearchEntries(hostedTools?: string[]): any[];
|
|
6
7
|
export declare function parseAnthropicHostedTools(response: any, provider: string): HostedToolResult[];
|
|
7
8
|
type EphemeralCacheControl = {
|
|
@@ -12,6 +12,40 @@ import { BaseClient } from "./baseClient.js";
|
|
|
12
12
|
import { getModel, isTextModel } from "../models.js";
|
|
13
13
|
import { Model } from "../model.js";
|
|
14
14
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
15
|
+
// Normalize an Anthropic message's content to a block array for merging.
|
|
16
|
+
// Array content is passed through; a non-empty string becomes a single text
|
|
17
|
+
// block; an empty string (or any other unexpected value) becomes no blocks
|
|
18
|
+
// (Anthropic rejects empty text blocks). Guarding non-array values keeps the
|
|
19
|
+
// exported merge helper from throwing on a spread if a caller passes content
|
|
20
|
+
// that isn't the string | block[] the type promises.
|
|
21
|
+
function anthropicContentToBlocks(content) {
|
|
22
|
+
if (Array.isArray(content)) {
|
|
23
|
+
return content;
|
|
24
|
+
}
|
|
25
|
+
if (typeof content === "string" && content.length > 0) {
|
|
26
|
+
return [{ type: "text", text: content }];
|
|
27
|
+
}
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
// Anthropic requires strict user/assistant turn alternation. Collapse any run of
|
|
31
|
+
// consecutive messages that share a role into a single message by concatenating
|
|
32
|
+
// their content blocks. Does not mutate the input.
|
|
33
|
+
export function mergeConsecutiveMessages(messages) {
|
|
34
|
+
const merged = [];
|
|
35
|
+
for (const msg of messages) {
|
|
36
|
+
const last = merged[merged.length - 1];
|
|
37
|
+
if (last && last.role === msg.role) {
|
|
38
|
+
const content = [
|
|
39
|
+
...anthropicContentToBlocks(last.content),
|
|
40
|
+
...anthropicContentToBlocks(msg.content),
|
|
41
|
+
];
|
|
42
|
+
merged[merged.length - 1] = { ...last, content };
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
merged.push(msg);
|
|
46
|
+
}
|
|
47
|
+
return merged;
|
|
48
|
+
}
|
|
15
49
|
export function anthropicWebSearchEntries(hostedTools) {
|
|
16
50
|
if (hostedTools && hostedTools.includes(WEB_SEARCH)) {
|
|
17
51
|
return [{ type: "web_search_20250305", name: "web_search" }];
|
|
@@ -187,30 +221,21 @@ export class SmolAnthropic extends BaseClient {
|
|
|
187
221
|
.filter((m) => m instanceof SystemMessage || m instanceof DeveloperMessage)
|
|
188
222
|
.map((m) => m.content);
|
|
189
223
|
const system = systemParts.length > 0 ? systemParts.join("\n") : undefined;
|
|
190
|
-
// Convert remaining messages
|
|
191
|
-
const
|
|
224
|
+
// Convert remaining messages into Anthropic message params.
|
|
225
|
+
const converted = [];
|
|
192
226
|
for (const msg of config.messages) {
|
|
193
227
|
if (msg instanceof SystemMessage || msg instanceof DeveloperMessage) {
|
|
194
228
|
continue;
|
|
195
229
|
}
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
230
|
+
const c = msg.toAnthropicMessage();
|
|
231
|
+
if (c === null)
|
|
198
232
|
continue;
|
|
199
|
-
|
|
200
|
-
if (converted.role === "user" &&
|
|
201
|
-
Array.isArray(converted.content) &&
|
|
202
|
-
converted.content.every((c) => c.type === "tool_result")) {
|
|
203
|
-
const last = anthropicMessages[anthropicMessages.length - 1];
|
|
204
|
-
if (last &&
|
|
205
|
-
last.role === "user" &&
|
|
206
|
-
Array.isArray(last.content) &&
|
|
207
|
-
last.content.every((c) => c.type === "tool_result")) {
|
|
208
|
-
last.content.push(...converted.content);
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
anthropicMessages.push(converted);
|
|
233
|
+
converted.push(c);
|
|
213
234
|
}
|
|
235
|
+
// Anthropic requires strict user/assistant alternation — merge any run of
|
|
236
|
+
// consecutive same-role messages into one (e.g. two user turns, or a user
|
|
237
|
+
// turn followed by tool results, which also map to role "user").
|
|
238
|
+
const anthropicMessages = mergeConsecutiveMessages(converted);
|
|
214
239
|
const functionTools = config.tools && config.tools.length > 0
|
|
215
240
|
? config.tools.map((tool) => zodToAnthropicTool(tool.name, tool.schema, {
|
|
216
241
|
description: tool.description,
|