smoltalk 0.7.1 → 0.8.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.
@@ -5,23 +5,29 @@ export declare const ToolCallJSONSchema: z.ZodObject<{
5
5
  id: z.ZodDefault<z.ZodString>;
6
6
  name: z.ZodString;
7
7
  arguments: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
8
+ thoughtSignature: z.ZodOptional<z.ZodString>;
8
9
  }, z.core.$strip>;
9
10
  export type ToolCallJSON = z.infer<typeof ToolCallJSONSchema>;
10
- export type ToolCallOptions = {};
11
+ export type ToolCallOptions = {
12
+ thoughtSignature?: string;
13
+ };
11
14
  export declare class ToolCall {
12
15
  private _id;
13
16
  private _name;
14
17
  private _arguments;
18
+ private _thoughtSignature?;
15
19
  private logger;
16
20
  constructor(id: string, name: string, args: Record<string, any> | string, options?: ToolCallOptions);
17
21
  get id(): string;
18
22
  get name(): string;
19
23
  get arguments(): Record<string, any>;
24
+ get thoughtSignature(): string | undefined;
20
25
  toJSON(): ToolCallJSON;
21
26
  static fromJSON(json: unknown): ToolCall;
22
27
  toOpenAI(): any;
23
28
  toGoogle(): {
24
29
  functionCall: FunctionCall;
30
+ thoughtSignature?: string;
25
31
  };
26
32
  toOpenAIResponseInputItem(): ResponseInputItem;
27
33
  }
@@ -4,15 +4,20 @@ export const ToolCallJSONSchema = z.object({
4
4
  id: z.string().default(""),
5
5
  name: z.string(),
6
6
  arguments: z.record(z.string(), z.any()).default({}),
7
+ // Google Gemini 3 attaches an encrypted thought signature to the same part as
8
+ // the function call; it must be echoed back verbatim during tool use.
9
+ thoughtSignature: z.string().optional(),
7
10
  });
8
11
  export class ToolCall {
9
12
  _id;
10
13
  _name;
11
14
  _arguments;
15
+ _thoughtSignature;
12
16
  logger;
13
17
  constructor(id, name, args, options = {}) {
14
18
  this._id = id;
15
19
  this._name = name;
20
+ this._thoughtSignature = options.thoughtSignature;
16
21
  this.logger = getLogger();
17
22
  if (typeof args === "string") {
18
23
  try {
@@ -37,11 +42,17 @@ export class ToolCall {
37
42
  get arguments() {
38
43
  return this._arguments;
39
44
  }
45
+ get thoughtSignature() {
46
+ return this._thoughtSignature;
47
+ }
40
48
  toJSON() {
41
49
  return {
42
50
  id: this._id,
43
51
  name: this._name,
44
52
  arguments: this._arguments,
53
+ ...(this._thoughtSignature !== undefined && {
54
+ thoughtSignature: this._thoughtSignature,
55
+ }),
45
56
  };
46
57
  }
47
58
  static fromJSON(json) {
@@ -53,7 +64,9 @@ export class ToolCall {
53
64
  logger.debug("ToolCall payload that failed to parse:", JSON.stringify(json, null, 2));
54
65
  throw new Error("Failed to parse ToolCall");
55
66
  }
56
- return new ToolCall(result.data.id, result.data.name, result.data.arguments);
67
+ return new ToolCall(result.data.id, result.data.name, result.data.arguments, {
68
+ thoughtSignature: result.data.thoughtSignature,
69
+ });
57
70
  }
58
71
  toOpenAI() {
59
72
  return {
@@ -71,6 +84,11 @@ export class ToolCall {
71
84
  name: this.name,
72
85
  args: this.arguments,
73
86
  },
87
+ // Gemini 3 requires the original thought signature echoed back on the
88
+ // function-call part; omitting it fails validation during tool use.
89
+ ...(this._thoughtSignature !== undefined && {
90
+ thoughtSignature: this._thoughtSignature,
91
+ }),
74
92
  };
75
93
  }
76
94
  toOpenAIResponseInputItem() {
@@ -19,6 +19,7 @@ export declare const AssistantMessageJSONSchema: z.ZodObject<{
19
19
  id: z.ZodDefault<z.ZodString>;
20
20
  name: z.ZodString;
21
21
  arguments: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
22
+ thoughtSignature: z.ZodOptional<z.ZodString>;
22
23
  }, z.core.$strip>>>;
23
24
  thinkingBlocks: z.ZodOptional<z.ZodArray<z.ZodObject<{
24
25
  text: z.ZodString;
@@ -134,6 +134,14 @@ export class SmolGoogle extends BaseClient {
134
134
  toolGroups.push(entry);
135
135
  }
136
136
  genConfig.tools = toolGroups;
137
+ // Gemini rejects mixing built-in (server-side) tools with function calling
138
+ // unless the caller opts in. Only required when both kinds coexist.
139
+ if (tools.length > 0 && hostedEntries.length > 0) {
140
+ genConfig.toolConfig = {
141
+ ...genConfig.toolConfig,
142
+ includeServerSideToolInvocations: true,
143
+ };
144
+ }
137
145
  }
138
146
  if (config.responseFormat) {
139
147
  genConfig.responseMimeType = "application/json";
@@ -277,7 +285,11 @@ export class SmolGoogle extends BaseClient {
277
285
  candidate.content.parts.forEach((part) => {
278
286
  if (part.functionCall) {
279
287
  const functionCall = part.functionCall;
280
- toolCalls.push(new ToolCall("", functionCall.name, functionCall.args));
288
+ // Gemini 3 rides the thought signature on the same part as the
289
+ // function call; capture it so it can be echoed back during tool use.
290
+ toolCalls.push(new ToolCall("", functionCall.name, functionCall.args, {
291
+ thoughtSignature: part.thoughtSignature,
292
+ }));
281
293
  }
282
294
  else if (part.thoughtSignature) {
283
295
  // Capture thought parts (thought: true indicates a thinking part)
@@ -352,7 +364,38 @@ export class SmolGoogle extends BaseClient {
352
364
  for (const candidate of chunk.candidates || []) {
353
365
  for (const part of candidate?.content?.parts || []) {
354
366
  const p = part;
355
- if (p.thoughtSignature) {
367
+ // Check functionCall first: Gemini 3 attaches the thought signature to
368
+ // the same part as the function call, so a thoughtSignature-first check
369
+ // would misfile the tool call as a thinking block and drop it.
370
+ if (p.functionCall) {
371
+ const id = p.functionCall.id || p.functionCall.name || "";
372
+ const name = p.functionCall.name || "";
373
+ const existing = toolCallsMap.get(id);
374
+ if (!existing) {
375
+ toolCallsMap.set(id, {
376
+ id,
377
+ name,
378
+ arguments: p.functionCall.args,
379
+ thoughtSignature: p.thoughtSignature,
380
+ });
381
+ }
382
+ else {
383
+ // A later chunk can carry the thought signature (or fuller
384
+ // args/name) for a function call first seen without them.
385
+ // Backfill missing fields rather than dropping the update, so the
386
+ // signature isn't lost during tool-use round trips.
387
+ if (p.thoughtSignature && !existing.thoughtSignature) {
388
+ existing.thoughtSignature = p.thoughtSignature;
389
+ }
390
+ if (p.functionCall.args && !existing.arguments) {
391
+ existing.arguments = p.functionCall.args;
392
+ }
393
+ if (name && !existing.name) {
394
+ existing.name = name;
395
+ }
396
+ }
397
+ }
398
+ else if (p.thoughtSignature) {
356
399
  const block = {
357
400
  text: p.text || "",
358
401
  signature: p.thoughtSignature,
@@ -368,17 +411,6 @@ export class SmolGoogle extends BaseClient {
368
411
  content += p.text;
369
412
  yield { type: "text", text: p.text };
370
413
  }
371
- else if (p.functionCall) {
372
- const id = p.functionCall.id || p.functionCall.name || "";
373
- const name = p.functionCall.name || "";
374
- if (!toolCallsMap.has(id)) {
375
- toolCallsMap.set(id, {
376
- id,
377
- name,
378
- arguments: p.functionCall.args,
379
- });
380
- }
381
- }
382
414
  }
383
415
  }
384
416
  }
@@ -387,7 +419,9 @@ export class SmolGoogle extends BaseClient {
387
419
  // Yield tool calls
388
420
  const toolCalls = [];
389
421
  for (const tc of toolCallsMap.values()) {
390
- const toolCall = new ToolCall(tc.id, tc.name, tc.arguments);
422
+ const toolCall = new ToolCall(tc.id, tc.name, tc.arguments, {
423
+ thoughtSignature: tc.thoughtSignature,
424
+ });
391
425
  toolCalls.push(toolCall);
392
426
  yield { type: "tool_call", toolCall };
393
427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoltalk",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "A common interface for LLM APIs",
5
5
  "homepage": "https://github.com/egonSchiele/smoltalk",
6
6
  "files": [
@@ -28,12 +28,12 @@
28
28
  "author": "Aditya Bhargava",
29
29
  "license": "ISC",
30
30
  "dependencies": {
31
- "@anthropic-ai/sdk": "^0.78.0",
32
- "@google/genai": "^1.34.0",
31
+ "@anthropic-ai/sdk": "^0.109.0",
32
+ "@google/genai": "^2.10.0",
33
33
  "nanoid": "^5.1.6",
34
34
  "ollama": "^0.6.3",
35
- "openai": "^6.15.0",
36
- "zod": "^4.3.6"
35
+ "openai": "^6.45.0",
36
+ "zod": "^4.4.3"
37
37
  },
38
38
  "devDependencies": {
39
39
  "tsx": "^4.19.2"