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.
@@ -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, merging consecutive tool_result user messages
191
- const anthropicMessages = [];
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 converted = msg.toAnthropicMessage();
197
- if (converted === null)
230
+ const c = msg.toAnthropicMessage();
231
+ if (c === null)
198
232
  continue;
199
- // Merge consecutive tool_result user messages into one (required by Anthropic)
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoltalk",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "A common interface for LLM APIs",
5
5
  "homepage": "https://github.com/egonSchiele/smoltalk",
6
6
  "files": [