@sisu-ai/mw-tool-calling 2.0.0 → 3.0.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.
package/README.md CHANGED
@@ -11,16 +11,76 @@ npm i @sisu-ai/mw-tool-calling
11
11
  Discover what you can do through examples or documentation. Check it out at https://github.com/finger-gun/sisu
12
12
 
13
13
  ## Behavior
14
- - First turn: calls `ctx.model.generate(messages, { tools, toolChoice:'auto' })`.
15
- - If assistant returns `tool_calls`, appends the assistant message and executes each tool.
16
- - Executes each unique `(name, args)` once and responds to every `tool_call_id`.
17
- - Handles provider quirks (e.g., missing args on duplicates) by reusing last args.
18
- - Second turn: asks for a pure completion (`toolChoice:'none'`).
14
+ - `toolCalling`: single-round tool calling.
15
+ - First turn: calls `ctx.model.generate(messages, { tools, toolChoice:'auto' })`.
16
+ - If assistant returns `tool_calls`, appends the assistant message and executes each tool.
17
+ - Executes each unique `(name, args)` once and responds to every `tool_call_id`.
18
+ - Handles provider quirks by reusing last args for identical tool names with missing args.
19
+ - Second turn: asks for a pure completion (`toolChoice:'none'`).
20
+ ```mermaid
21
+ sequenceDiagram
22
+ autonumber
23
+ participant A as Agent toolCalling
24
+ participant M as Model Adapter
25
+ participant R as Tools Registry
26
+ participant H as Tool Handler
27
+
28
+ A->>R: list
29
+ R-->>A: tools
30
+ A->>M: generate with tools auto
31
+ alt tool calls
32
+ M-->>A: assistant with tool calls
33
+ loop each unique name args
34
+ A->>R: resolve and validate
35
+ R-->>A: handler
36
+ A->>H: execute
37
+ H-->>A: append tool message
38
+ end
39
+ A->>M: generate finalize none
40
+ M-->>A: assistant completion
41
+ else no tool calls
42
+ M-->>A: assistant completion
43
+ end
44
+
45
+ ```
46
+ - `iterativeToolCalling`: multi-round tool calling.
47
+ - Repeats calls with `toolChoice:'auto'` until the model returns a message with no `tool_calls` (max 12 iters).
48
+
49
+ ```mermaid
50
+ sequenceDiagram
51
+ autonumber
52
+ participant A as Agent iterativeToolCalling
53
+ participant M as Model Adapter
54
+ participant R as Tools Registry
55
+ participant H as Tool Handler
56
+
57
+ A->>R: list
58
+ R-->>A: tools
59
+ loop max twelve iterations until no tool calls
60
+ A->>M: generate with tools auto
61
+ alt tool calls present
62
+ M-->>A: assistant with tool calls
63
+ loop each unique name args
64
+ A->>R: resolve and validate
65
+ R-->>A: handler
66
+ A->>H: execute
67
+ H-->>A: append tool message
68
+ end
69
+ else no tool calls
70
+ M-->>A: assistant no tools
71
+ end
72
+ end
73
+ A->>M: generate finalize none
74
+ M-->>A: assistant completion
75
+ ```
19
76
 
20
77
  ## Usage
21
78
  ```ts
22
- import { toolCalling } from '@sisu-ai/mw-tool-calling';
79
+ import { toolCalling, iterativeToolCalling } from '@sisu-ai/mw-tool-calling';
80
+
81
+ // Single-round
82
+ agent.use(toolCalling);
23
83
 
24
- const app = new Agent()
25
- .use(toolCalling);
84
+ // OR multi-round
85
+ agent.use(iterativeToolCalling);
26
86
  ```
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  import type { Middleware } from '@sisu-ai/core';
2
2
  export declare const toolCalling: Middleware;
3
+ export declare const iterativeToolCalling: Middleware;
package/dist/index.js CHANGED
@@ -61,6 +61,61 @@ export const toolCalling = async (ctx, next) => {
61
61
  }
62
62
  }
63
63
  };
64
+ export const iterativeToolCalling = async (ctx, next) => {
65
+ await next();
66
+ const maxIters = 12;
67
+ for (let i = 0; i < maxIters; i++) {
68
+ ctx.log.debug?.('[iterative-tool-calling] iteration start', { i, messages: ctx.messages.length });
69
+ const toolList = ctx.tools.list();
70
+ const genOpts = { toolChoice: 'auto', tools: toolList, parallelToolCalls: false, signal: ctx.signal };
71
+ const out = await ctx.model.generate(ctx.messages, genOpts);
72
+ const msg = out.message;
73
+ const toolCalls = msg.tool_calls;
74
+ if (toolCalls && toolCalls.length > 0) {
75
+ // include the assistant message that requested tools
76
+ ctx.messages.push(msg);
77
+ ctx.log.info?.('[iterative-tool-calling] model requested tools', toolCalls.map(tc => ({ id: tc.id, name: tc.name, hasArgs: typeof tc.arguments !== 'undefined' })));
78
+ const cache = new Map();
79
+ const keyOf = (tc) => `${tc.name}:${safeStableStringify(tc.arguments)}`;
80
+ const lastArgsByName = new Map();
81
+ const resolvedCalls = toolCalls.map((tc) => {
82
+ if (typeof tc.arguments === 'undefined' && lastArgsByName.has(tc.name)) {
83
+ return { ...tc, arguments: lastArgsByName.get(tc.name) };
84
+ }
85
+ return tc;
86
+ });
87
+ for (const call of resolvedCalls) {
88
+ const tool = ctx.tools.get(call.name);
89
+ if (!tool)
90
+ throw new Error('Unknown tool: ' + call.name);
91
+ const key = keyOf(call);
92
+ let result = cache.get(key);
93
+ if (result === undefined) {
94
+ const args = tool.schema?.parse ? tool.schema.parse(call.arguments) : call.arguments;
95
+ ctx.log.debug?.('[iterative-tool-calling] invoking tool', { name: call.name, id: call.id, args });
96
+ result = await tool.handler(args, ctx);
97
+ cache.set(key, result);
98
+ lastArgsByName.set(call.name, args);
99
+ }
100
+ else {
101
+ ctx.log.debug?.('[iterative-tool-calling] reusing cached result', { name: call.name, id: call.id });
102
+ }
103
+ const toolMsg = { role: 'tool', content: JSON.stringify(result) };
104
+ if (call.id)
105
+ toolMsg.tool_call_id = call.id;
106
+ else
107
+ toolMsg.name = call.name;
108
+ ctx.messages.push(toolMsg);
109
+ }
110
+ continue; // next round may call more tools
111
+ }
112
+ else {
113
+ ctx.log.info?.('[iterative-tool-calling] no tool calls; appending assistant message');
114
+ ctx.messages.push(msg);
115
+ break;
116
+ }
117
+ }
118
+ };
64
119
  function safeStableStringify(v) {
65
120
  try {
66
121
  if (v && typeof v === 'object' && !Array.isArray(v)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisu-ai/mw-tool-calling",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "build": "tsc -b"
15
15
  },
16
16
  "peerDependencies": {
17
- "@sisu-ai/core": "0.3.0"
17
+ "@sisu-ai/core": "1.0.0"
18
18
  },
19
19
  "repository": {
20
20
  "type": "git",