smoltalk 0.0.30 → 0.0.32

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.
@@ -42,4 +42,16 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
42
42
  toOpenAIResponseInputItem(): ResponseInputItem | ResponseInputItem[];
43
43
  toGoogleMessage(): Content;
44
44
  toOllamaMessage(): Message;
45
+ toAnthropicMessage(): {
46
+ role: "assistant";
47
+ content: string | Array<{
48
+ type: "text";
49
+ text: string;
50
+ } | {
51
+ type: "tool_use";
52
+ id: string;
53
+ name: string;
54
+ input: Record<string, any>;
55
+ }>;
56
+ };
45
57
  }
@@ -112,4 +112,28 @@ export class AssistantMessage extends BaseMessage {
112
112
  tool_calls: this.toolCalls?.map((tc) => tc.toOpenAI()),
113
113
  };
114
114
  }
115
+ toAnthropicMessage() {
116
+ const hasText = this._content !== null &&
117
+ this._content !== undefined &&
118
+ (typeof this._content === "string"
119
+ ? this._content.length > 0
120
+ : this._content.length > 0);
121
+ const hasToolCalls = this._toolCalls && this._toolCalls.length > 0;
122
+ if (!hasToolCalls) {
123
+ return { role: "assistant", content: this.content };
124
+ }
125
+ const blocks = [];
126
+ if (hasText) {
127
+ const text = typeof this._content === "string"
128
+ ? this._content
129
+ : this._content.map((p) => p.text).join("");
130
+ if (text) {
131
+ blocks.push({ type: "text", text });
132
+ }
133
+ }
134
+ for (const tc of this._toolCalls) {
135
+ blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input: tc.arguments });
136
+ }
137
+ return { role: "assistant", content: blocks };
138
+ }
115
139
  }
@@ -29,4 +29,5 @@ export declare class DeveloperMessage extends BaseMessage implements MessageClas
29
29
  toOpenAIResponseInputItem(): ResponseInputItem;
30
30
  toGoogleMessage(): Content;
31
31
  toOllamaMessage(): Message;
32
+ toAnthropicMessage(): null;
32
33
  }
@@ -56,4 +56,9 @@ export class DeveloperMessage extends BaseMessage {
56
56
  toOllamaMessage() {
57
57
  return { role: this.role, content: this.content };
58
58
  }
59
+ // Developer messages are treated like system messages in Anthropic's API.
60
+ // Returns null to signal they should be collected into the `system` param.
61
+ toAnthropicMessage() {
62
+ return null;
63
+ }
59
64
  }
@@ -29,4 +29,5 @@ export declare class SystemMessage extends BaseMessage implements MessageClass {
29
29
  toOpenAIResponseInputItem(): ResponseInputItem;
30
30
  toGoogleMessage(): Content;
31
31
  toOllamaMessage(): Message;
32
+ toAnthropicMessage(): null;
32
33
  }
@@ -56,4 +56,9 @@ export class SystemMessage extends BaseMessage {
56
56
  toOllamaMessage() {
57
57
  return { role: this.role, content: this.content };
58
58
  }
59
+ // System messages are passed as the top-level `system` param in Anthropic's API,
60
+ // not as entries in the messages array. Returns null to signal this.
61
+ toAnthropicMessage() {
62
+ return null;
63
+ }
59
64
  }
@@ -33,4 +33,12 @@ export declare class ToolMessage extends BaseMessage implements MessageClass {
33
33
  toOpenAIResponseInputItem(): ResponseInputItem;
34
34
  toGoogleMessage(): Content;
35
35
  toOllamaMessage(): Message;
36
+ toAnthropicMessage(): {
37
+ role: "user";
38
+ content: Array<{
39
+ type: "tool_result";
40
+ tool_use_id: string;
41
+ content: string;
42
+ }>;
43
+ };
36
44
  }
@@ -83,4 +83,11 @@ export class ToolMessage extends BaseMessage {
83
83
  content: this.content,
84
84
  };
85
85
  }
86
+ // In Anthropic's API, tool results are user-role messages containing tool_result blocks.
87
+ toAnthropicMessage() {
88
+ return {
89
+ role: "user",
90
+ content: [{ type: "tool_result", tool_use_id: this.tool_call_id, content: this.content }],
91
+ };
92
+ }
86
93
  }
@@ -28,4 +28,8 @@ export declare class UserMessage extends BaseMessage implements MessageClass {
28
28
  toOpenAIResponseInputItem(): ResponseInputItem;
29
29
  toGoogleMessage(): Content;
30
30
  toOllamaMessage(): Message;
31
+ toAnthropicMessage(): {
32
+ role: "user";
33
+ content: string;
34
+ };
31
35
  }
@@ -61,4 +61,7 @@ export class UserMessage extends BaseMessage {
61
61
  content: this.content,
62
62
  };
63
63
  }
64
+ toAnthropicMessage() {
65
+ return { role: "user", content: this.content };
66
+ }
64
67
  }
package/dist/client.d.ts CHANGED
@@ -1,9 +1,11 @@
1
+ export * from "./clients/anthropic.js";
1
2
  export * from "./clients/google.js";
2
3
  export * from "./clients/openai.js";
3
4
  export * from "./clients/openaiResponses.js";
5
+ import { SmolAnthropic } from "./clients/anthropic.js";
4
6
  import { SmolGoogle } from "./clients/google.js";
5
7
  import { SmolOpenAi } from "./clients/openai.js";
6
8
  import { SmolOpenAiResponses } from "./clients/openaiResponses.js";
7
9
  import { SmolConfig } from "./types.js";
8
10
  import { SmolOllama } from "./clients/ollama.js";
9
- export declare function getClient(config: SmolConfig): SmolGoogle | SmolOpenAi | SmolOpenAiResponses | SmolOllama;
11
+ export declare function getClient(config: SmolConfig): SmolAnthropic | SmolGoogle | SmolOpenAi | SmolOpenAiResponses | SmolOllama;
package/dist/client.js CHANGED
@@ -1,10 +1,12 @@
1
+ export * from "./clients/anthropic.js";
1
2
  export * from "./clients/google.js";
2
3
  export * from "./clients/openai.js";
3
4
  export * from "./clients/openaiResponses.js";
5
+ import { SmolAnthropic } from "./clients/anthropic.js";
4
6
  import { SmolGoogle } from "./clients/google.js";
5
7
  import { SmolOpenAi } from "./clients/openai.js";
6
8
  import { SmolOpenAiResponses } from "./clients/openaiResponses.js";
7
- import { getModel, isModelConfig, isTextModel, pickModel } from "./models.js";
9
+ import { getModel, isModelConfig, isTextModel, pickModel, } from "./models.js";
8
10
  import { SmolError } from "./smolError.js";
9
11
  import { getLogger } from "./logger.js";
10
12
  import { SmolOllama } from "./clients/ollama.js";
@@ -28,6 +30,14 @@ export function getClient(config) {
28
30
  }
29
31
  const clientConfig = { ...config, model: modelName };
30
32
  switch (provider) {
33
+ case "anthropic":
34
+ if (!config.anthropicApiKey) {
35
+ throw new SmolError("No Anthropic API key provided. Please provide an Anthropic API key in the config using anthropicApiKey.");
36
+ }
37
+ return new SmolAnthropic({
38
+ ...clientConfig,
39
+ anthropicApiKey: config.anthropicApiKey,
40
+ });
31
41
  case "openai":
32
42
  if (!config.openAiApiKey) {
33
43
  throw new SmolError("No OpenAI API key provided. Please provide an OpenAI API key in the config using openAiApiKey.");
@@ -0,0 +1,16 @@
1
+ import { BaseClientConfig, PromptConfig, PromptResult, Result, SmolClient, StreamChunk } from "../types.js";
2
+ import { BaseClient } from "./baseClient.js";
3
+ export type SmolAnthropicConfig = BaseClientConfig & {
4
+ anthropicApiKey: string;
5
+ };
6
+ export declare class SmolAnthropic extends BaseClient implements SmolClient {
7
+ private client;
8
+ private logger;
9
+ private model;
10
+ constructor(config: SmolAnthropicConfig);
11
+ getModel(): string;
12
+ private calculateUsageAndCost;
13
+ private buildRequest;
14
+ _textSync(config: PromptConfig): Promise<Result<PromptResult>>;
15
+ _textStream(config: PromptConfig): AsyncGenerator<StreamChunk>;
16
+ }
@@ -0,0 +1,188 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
2
+ import { ToolCall } from "../classes/ToolCall.js";
3
+ import { SystemMessage, DeveloperMessage, } from "../classes/message/index.js";
4
+ import { getLogger } from "../logger.js";
5
+ import { success, } from "../types.js";
6
+ import { zodToAnthropicTool } from "../util/tool.js";
7
+ import { BaseClient } from "./baseClient.js";
8
+ import { calculateCost } from "../models.js";
9
+ const DEFAULT_MAX_TOKENS = 4096;
10
+ export class SmolAnthropic extends BaseClient {
11
+ client;
12
+ logger;
13
+ model;
14
+ constructor(config) {
15
+ super(config);
16
+ this.client = new Anthropic({ apiKey: config.anthropicApiKey });
17
+ this.logger = getLogger();
18
+ this.model = config.model;
19
+ }
20
+ getModel() {
21
+ return this.model;
22
+ }
23
+ calculateUsageAndCost(usageData) {
24
+ const usage = {
25
+ inputTokens: usageData.input_tokens,
26
+ outputTokens: usageData.output_tokens,
27
+ totalTokens: usageData.input_tokens + usageData.output_tokens,
28
+ };
29
+ const cost = calculateCost(this.model, usage) ?? undefined;
30
+ return { usage, cost };
31
+ }
32
+ buildRequest(config) {
33
+ // Split system/developer messages out into the top-level `system` param
34
+ const systemParts = config.messages
35
+ .filter((m) => m instanceof SystemMessage || m instanceof DeveloperMessage)
36
+ .map((m) => m.content);
37
+ const system = systemParts.length > 0 ? systemParts.join("\n") : undefined;
38
+ // Convert remaining messages, merging consecutive tool_result user messages
39
+ const anthropicMessages = [];
40
+ for (const msg of config.messages) {
41
+ if (msg instanceof SystemMessage || msg instanceof DeveloperMessage) {
42
+ continue;
43
+ }
44
+ const converted = msg.toAnthropicMessage();
45
+ if (converted === null)
46
+ continue;
47
+ // Merge consecutive tool_result user messages into one (required by Anthropic)
48
+ if (converted.role === "user" &&
49
+ Array.isArray(converted.content) &&
50
+ converted.content.every((c) => c.type === "tool_result")) {
51
+ const last = anthropicMessages[anthropicMessages.length - 1];
52
+ if (last &&
53
+ last.role === "user" &&
54
+ Array.isArray(last.content) &&
55
+ last.content.every((c) => c.type === "tool_result")) {
56
+ last.content.push(...converted.content);
57
+ continue;
58
+ }
59
+ }
60
+ anthropicMessages.push(converted);
61
+ }
62
+ const tools = config.tools && config.tools.length > 0
63
+ ? config.tools.map((tool) => zodToAnthropicTool(tool.name, tool.schema, {
64
+ description: tool.description,
65
+ }))
66
+ : undefined;
67
+ return { system, messages: anthropicMessages, tools };
68
+ }
69
+ async _textSync(config) {
70
+ const { system, messages, tools } = this.buildRequest(config);
71
+ this.logger.debug("Sending request to Anthropic:", {
72
+ model: this.model,
73
+ max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
74
+ messages,
75
+ system,
76
+ tools,
77
+ });
78
+ const response = await this.client.messages.create({
79
+ model: this.model,
80
+ max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
81
+ messages,
82
+ ...(system && { system }),
83
+ ...(tools && { tools }),
84
+ ...(config.temperature !== undefined && {
85
+ temperature: config.temperature,
86
+ }),
87
+ ...(config.rawAttributes || {}),
88
+ stream: false,
89
+ });
90
+ this.logger.debug("Response from Anthropic:", response);
91
+ let output = null;
92
+ const toolCalls = [];
93
+ for (const block of response.content) {
94
+ if (block.type === "text") {
95
+ output = (output ?? "") + block.text;
96
+ }
97
+ else if (block.type === "tool_use") {
98
+ toolCalls.push(new ToolCall(block.id, block.name, block.input));
99
+ }
100
+ }
101
+ const { usage, cost } = this.calculateUsageAndCost(response.usage);
102
+ return success({
103
+ output,
104
+ toolCalls,
105
+ usage,
106
+ cost,
107
+ model: this.model,
108
+ });
109
+ }
110
+ async *_textStream(config) {
111
+ const { system, messages, tools } = this.buildRequest(config);
112
+ this.logger.debug("Sending streaming request to Anthropic:", {
113
+ model: this.model,
114
+ max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
115
+ messages,
116
+ system,
117
+ tools,
118
+ });
119
+ const stream = await this.client.messages.create({
120
+ model: this.model,
121
+ max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
122
+ messages,
123
+ ...(system && { system }),
124
+ ...(tools && { tools }),
125
+ ...(config.temperature !== undefined && {
126
+ temperature: config.temperature,
127
+ }),
128
+ ...(config.rawAttributes || {}),
129
+ stream: true,
130
+ });
131
+ let content = "";
132
+ // Track tool blocks by index: index -> { id, name, arguments (partial JSON) }
133
+ const toolBlocks = new Map();
134
+ let inputTokens = 0;
135
+ let outputTokens = 0;
136
+ for await (const event of stream) {
137
+ if (event.type === "message_start") {
138
+ inputTokens = event.message.usage.input_tokens;
139
+ }
140
+ else if (event.type === "content_block_start" &&
141
+ event.content_block.type === "tool_use") {
142
+ toolBlocks.set(event.index, {
143
+ id: event.content_block.id,
144
+ name: event.content_block.name,
145
+ arguments: "",
146
+ });
147
+ }
148
+ else if (event.type === "content_block_delta") {
149
+ if (event.delta.type === "text_delta") {
150
+ content += event.delta.text;
151
+ yield { type: "text", text: event.delta.text };
152
+ }
153
+ else if (event.delta.type === "input_json_delta") {
154
+ const block = toolBlocks.get(event.index);
155
+ if (block) {
156
+ block.arguments += event.delta.partial_json;
157
+ }
158
+ }
159
+ }
160
+ else if (event.type === "message_delta") {
161
+ outputTokens = event.usage.output_tokens;
162
+ }
163
+ }
164
+ this.logger.debug("Streaming response completed from Anthropic");
165
+ const toolCalls = [];
166
+ for (const block of toolBlocks.values()) {
167
+ const toolCall = new ToolCall(block.id, block.name, block.arguments);
168
+ toolCalls.push(toolCall);
169
+ yield { type: "tool_call", toolCall };
170
+ }
171
+ const usage = {
172
+ inputTokens,
173
+ outputTokens,
174
+ totalTokens: inputTokens + outputTokens,
175
+ };
176
+ const cost = calculateCost(this.model, usage) ?? undefined;
177
+ yield {
178
+ type: "done",
179
+ result: {
180
+ output: content || null,
181
+ toolCalls,
182
+ usage,
183
+ cost,
184
+ model: this.model,
185
+ },
186
+ };
187
+ }
188
+ }
@@ -131,6 +131,12 @@ export class BaseClient {
131
131
  if (inner.success)
132
132
  return inner.data;
133
133
  }
134
+ // 7. Wrap object with "response" and see if that matches the schema
135
+ const wrapped = { response: rawValue };
136
+ const wrappedParse = schema.safeParse(wrapped);
137
+ if (wrappedParse.success) {
138
+ return wrappedParse.data;
139
+ }
134
140
  // 8. Nothing worked — throw error
135
141
  throw direct.error;
136
142
  }
package/dist/functions.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { getClient } from "./client.js";
2
2
  import { isModelConfig, pickModel } from "./models.js";
3
3
  function splitConfig(config) {
4
- const { openAiApiKey, googleApiKey, ollamaApiKey, ollamaHost, model: rawModel, provider, logLevel, toolLoopDetection, ...promptConfig } = config;
4
+ const { openAiApiKey, googleApiKey, ollamaApiKey, anthropicApiKey, ollamaHost, model: rawModel, provider, logLevel, toolLoopDetection, ...promptConfig } = config;
5
5
  const model = isModelConfig(rawModel) ? pickModel(rawModel) : rawModel;
6
6
  return {
7
7
  smolConfig: {
8
8
  openAiApiKey,
9
9
  googleApiKey,
10
10
  ollamaApiKey,
11
+ anthropicApiKey,
11
12
  ollamaHost,
12
13
  model,
13
14
  provider,
package/dist/index.d.ts CHANGED
@@ -5,3 +5,4 @@ export * from "./smolError.js";
5
5
  export * from "./util.js";
6
6
  export * from "./classes/message/index.js";
7
7
  export * from "./functions.js";
8
+ export * from "./classes/ToolCall.js";
package/dist/index.js CHANGED
@@ -5,3 +5,4 @@ export * from "./smolError.js";
5
5
  export * from "./util.js";
6
6
  export * from "./classes/message/index.js";
7
7
  export * from "./functions.js";
8
+ export * from "./classes/ToolCall.js";
package/dist/models.d.ts CHANGED
@@ -303,25 +303,54 @@ export declare const textModels: readonly [{
303
303
  readonly costUnit: "characters";
304
304
  readonly disabled: true;
305
305
  readonly provider: "google";
306
+ }, {
307
+ readonly type: "text";
308
+ readonly modelName: "claude-opus-4-6";
309
+ readonly description: "The most intelligent Claude model for building agents and coding. 200K context window, 128K max output.";
310
+ readonly maxInputTokens: 200000;
311
+ readonly maxOutputTokens: 131072;
312
+ readonly inputTokenCost: 5;
313
+ readonly outputTokenCost: 25;
314
+ readonly provider: "anthropic";
315
+ }, {
316
+ readonly type: "text";
317
+ readonly modelName: "claude-sonnet-4-6";
318
+ readonly description: "The best combination of speed and intelligence. 200K context window, 64K max output.";
319
+ readonly maxInputTokens: 200000;
320
+ readonly maxOutputTokens: 64000;
321
+ readonly inputTokenCost: 3;
322
+ readonly outputTokenCost: 15;
323
+ readonly provider: "anthropic";
324
+ }, {
325
+ readonly type: "text";
326
+ readonly modelName: "claude-haiku-4-5-20251001";
327
+ readonly description: "The fastest Claude model with near-frontier intelligence. 200K context window, 64K max output.";
328
+ readonly maxInputTokens: 200000;
329
+ readonly maxOutputTokens: 64000;
330
+ readonly inputTokenCost: 1;
331
+ readonly outputTokenCost: 5;
332
+ readonly provider: "anthropic";
306
333
  }, {
307
334
  readonly type: "text";
308
335
  readonly modelName: "claude-3-7-sonnet-latest";
309
- readonly description: "Our most intelligent model to date and the first hybrid reasoning model on the market. Claude 3.7 Sonnet shows particularly strong improvements in coding and front-end web development.";
336
+ readonly description: "Claude 3.7 Sonnet legacy model. Use claude-sonnet-4-6 instead.";
310
337
  readonly maxInputTokens: 200000;
311
338
  readonly maxOutputTokens: 8192;
312
339
  readonly inputTokenCost: 3;
313
340
  readonly outputTokenCost: 15;
314
341
  readonly outputTokensPerSecond: 78;
342
+ readonly disabled: true;
315
343
  readonly provider: "anthropic";
316
344
  }, {
317
345
  readonly type: "text";
318
346
  readonly modelName: "claude-3-5-haiku-latest";
319
- readonly description: "Our fastest model";
347
+ readonly description: "Claude 3.5 Haiku — legacy model. Use claude-haiku-4-5-20251001 instead.";
320
348
  readonly maxInputTokens: 200000;
321
349
  readonly maxOutputTokens: 8192;
322
350
  readonly inputTokenCost: 0.8;
323
351
  readonly outputTokenCost: 4;
324
352
  readonly outputTokensPerSecond: 66;
353
+ readonly disabled: true;
325
354
  readonly provider: "anthropic";
326
355
  }, {
327
356
  readonly type: "text";
@@ -666,25 +695,54 @@ export declare function getModel(modelName: ModelName): {
666
695
  readonly costUnit: "characters";
667
696
  readonly disabled: true;
668
697
  readonly provider: "google";
698
+ } | {
699
+ readonly type: "text";
700
+ readonly modelName: "claude-opus-4-6";
701
+ readonly description: "The most intelligent Claude model for building agents and coding. 200K context window, 128K max output.";
702
+ readonly maxInputTokens: 200000;
703
+ readonly maxOutputTokens: 131072;
704
+ readonly inputTokenCost: 5;
705
+ readonly outputTokenCost: 25;
706
+ readonly provider: "anthropic";
707
+ } | {
708
+ readonly type: "text";
709
+ readonly modelName: "claude-sonnet-4-6";
710
+ readonly description: "The best combination of speed and intelligence. 200K context window, 64K max output.";
711
+ readonly maxInputTokens: 200000;
712
+ readonly maxOutputTokens: 64000;
713
+ readonly inputTokenCost: 3;
714
+ readonly outputTokenCost: 15;
715
+ readonly provider: "anthropic";
716
+ } | {
717
+ readonly type: "text";
718
+ readonly modelName: "claude-haiku-4-5-20251001";
719
+ readonly description: "The fastest Claude model with near-frontier intelligence. 200K context window, 64K max output.";
720
+ readonly maxInputTokens: 200000;
721
+ readonly maxOutputTokens: 64000;
722
+ readonly inputTokenCost: 1;
723
+ readonly outputTokenCost: 5;
724
+ readonly provider: "anthropic";
669
725
  } | {
670
726
  readonly type: "text";
671
727
  readonly modelName: "claude-3-7-sonnet-latest";
672
- readonly description: "Our most intelligent model to date and the first hybrid reasoning model on the market. Claude 3.7 Sonnet shows particularly strong improvements in coding and front-end web development.";
728
+ readonly description: "Claude 3.7 Sonnet legacy model. Use claude-sonnet-4-6 instead.";
673
729
  readonly maxInputTokens: 200000;
674
730
  readonly maxOutputTokens: 8192;
675
731
  readonly inputTokenCost: 3;
676
732
  readonly outputTokenCost: 15;
677
733
  readonly outputTokensPerSecond: 78;
734
+ readonly disabled: true;
678
735
  readonly provider: "anthropic";
679
736
  } | {
680
737
  readonly type: "text";
681
738
  readonly modelName: "claude-3-5-haiku-latest";
682
- readonly description: "Our fastest model";
739
+ readonly description: "Claude 3.5 Haiku — legacy model. Use claude-haiku-4-5-20251001 instead.";
683
740
  readonly maxInputTokens: 200000;
684
741
  readonly maxOutputTokens: 8192;
685
742
  readonly inputTokenCost: 0.8;
686
743
  readonly outputTokenCost: 4;
687
744
  readonly outputTokensPerSecond: 66;
745
+ readonly disabled: true;
688
746
  readonly provider: "anthropic";
689
747
  } | {
690
748
  readonly type: "text";
package/dist/models.js CHANGED
@@ -307,26 +307,58 @@ export const textModels = [
307
307
  disabled: true,
308
308
  provider: "google",
309
309
  },
310
+ {
311
+ type: "text",
312
+ modelName: "claude-opus-4-6",
313
+ description: "The most intelligent Claude model for building agents and coding. 200K context window, 128K max output.",
314
+ maxInputTokens: 200_000,
315
+ maxOutputTokens: 131_072,
316
+ inputTokenCost: 5,
317
+ outputTokenCost: 25,
318
+ provider: "anthropic",
319
+ },
320
+ {
321
+ type: "text",
322
+ modelName: "claude-sonnet-4-6",
323
+ description: "The best combination of speed and intelligence. 200K context window, 64K max output.",
324
+ maxInputTokens: 200_000,
325
+ maxOutputTokens: 64_000,
326
+ inputTokenCost: 3,
327
+ outputTokenCost: 15,
328
+ provider: "anthropic",
329
+ },
330
+ {
331
+ type: "text",
332
+ modelName: "claude-haiku-4-5-20251001",
333
+ description: "The fastest Claude model with near-frontier intelligence. 200K context window, 64K max output.",
334
+ maxInputTokens: 200_000,
335
+ maxOutputTokens: 64_000,
336
+ inputTokenCost: 1,
337
+ outputTokenCost: 5,
338
+ provider: "anthropic",
339
+ },
310
340
  {
311
341
  type: "text",
312
342
  modelName: "claude-3-7-sonnet-latest",
313
- description: "Our most intelligent model to date and the first hybrid reasoning model on the market. Claude 3.7 Sonnet shows particularly strong improvements in coding and front-end web development.",
343
+ description: "Claude 3.7 Sonnet legacy model. Use claude-sonnet-4-6 instead.",
314
344
  maxInputTokens: 200_000,
315
345
  maxOutputTokens: 8192,
316
346
  inputTokenCost: 3,
317
347
  outputTokenCost: 15,
318
348
  outputTokensPerSecond: 78,
349
+ disabled: true,
319
350
  provider: "anthropic",
320
351
  },
321
352
  {
322
353
  type: "text",
323
354
  modelName: "claude-3-5-haiku-latest",
324
- description: "Our fastest model",
355
+ description: "Claude 3.5 Haiku — legacy model. Use claude-haiku-4-5-20251001 instead.",
325
356
  maxInputTokens: 200_000,
326
357
  maxOutputTokens: 8192,
327
358
  inputTokenCost: 0.8,
328
359
  outputTokenCost: 4,
329
360
  outputTokensPerSecond: 66,
361
+ disabled: true,
330
362
  provider: "anthropic",
331
363
  },
332
364
  /* {
package/dist/types.d.ts CHANGED
@@ -31,6 +31,7 @@ export type PromptConfig = {
31
31
  export type SmolConfig = {
32
32
  openAiApiKey?: string;
33
33
  googleApiKey?: string;
34
+ anthropicApiKey?: string;
34
35
  ollamaApiKey?: string;
35
36
  ollamaHost?: string;
36
37
  model: ModelName | ModelConfig;
@@ -29,6 +29,18 @@ export declare function zodToOpenAIResponsesTool(name: string, schema: z.ZodType
29
29
  description?: string;
30
30
  strict?: boolean;
31
31
  }>): OpenAIResponsesFunctionTool;
32
+ export type AnthropicTool = {
33
+ name: string;
34
+ description?: string;
35
+ input_schema: {
36
+ type: "object";
37
+ properties: Record<string, any>;
38
+ required?: string[];
39
+ };
40
+ };
41
+ export declare function zodToAnthropicTool(name: string, schema: z.ZodType, options?: Partial<{
42
+ description?: string;
43
+ }>): AnthropicTool;
32
44
  /**
33
45
  * Converts an OpenAI tool definition to a Google FunctionDeclaration format
34
46
  *
package/dist/util/tool.js CHANGED
@@ -62,6 +62,30 @@ export function zodToOpenAIResponsesTool(name, schema, options = {}) {
62
62
  }
63
63
  return tool;
64
64
  }
65
+ export function zodToAnthropicTool(name, schema, options = {}) {
66
+ const jsonSchema = schema.toJSONSchema();
67
+ let description;
68
+ if (options?.description) {
69
+ description = options.description;
70
+ }
71
+ else if (typeof jsonSchema === "object" &&
72
+ "description" in jsonSchema &&
73
+ typeof jsonSchema.description === "string") {
74
+ description = jsonSchema.description;
75
+ }
76
+ const tool = {
77
+ name,
78
+ input_schema: {
79
+ type: "object",
80
+ properties: jsonSchema.properties || {},
81
+ required: jsonSchema.required || [],
82
+ },
83
+ };
84
+ if (description) {
85
+ tool.description = description;
86
+ }
87
+ return tool;
88
+ }
65
89
  /**
66
90
  * Removes properties that Google's API doesn't support from JSON schemas
67
91
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoltalk",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "A common interface for LLM APIs",
5
5
  "homepage": "https://github.com/egonSchiele/smoltalk",
6
6
  "scripts": {
@@ -41,6 +41,7 @@
41
41
  "zod": "^4.3.5"
42
42
  },
43
43
  "dependencies": {
44
+ "@anthropic-ai/sdk": "^0.78.0",
44
45
  "@google/genai": "^1.34.0",
45
46
  "egonlog": "^0.0.2",
46
47
  "ollama": "^0.6.3",