saico 2.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/LICENSE +21 -0
- package/README.md +398 -0
- package/context.js +1056 -0
- package/index.js +130 -0
- package/itask.js +678 -0
- package/openai.js +72 -0
- package/package.json +49 -0
- package/redis.js +123 -0
- package/sid.js +207 -0
- package/util.js +110 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 wanderli-ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# Saico - Simple AI-agent Conversation Orchestrator
|
|
2
|
+
|
|
3
|
+
`Saico` is a minimal yet powerful JavaScript/Node.js library for managing AI conversations with hierarchical context, token-aware summarization, and **enterprise-grade tool calling capabilities**. It's designed to support complex nested conversations while maintaining clean summaries and parent context, making it ideal for AI agents, assistants, and customer support bots.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## โจ Features
|
|
8
|
+
|
|
9
|
+
- ๐ **Hierarchical Conversations** โ Track parent-child chat contexts with summary propagation.
|
|
10
|
+
- ๐งต **Scoped Memory** โ Manage sub-conversations independently while maintaining parent relevance.
|
|
11
|
+
- ๐ **Token-Aware Summarization** โ Automatically summarize message history based on token thresholds.
|
|
12
|
+
- ๐ฌ **Message-Level Metadata** โ Track reply state, summaries, and custom flags.
|
|
13
|
+
- ๐ ๏ธ **OpenAI-Compatible Format** โ Built for seamless interaction with OpenAI-compatible APIs.
|
|
14
|
+
- ๐งฐ **Proxy-Based Interface** โ Interact with message history like an array, with extra powers.
|
|
15
|
+
- **๐ NEW: Tool Calls** โ Complete tool calling system with depth control, deferred execution, and safety features.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## ๐ง Tool Calls System
|
|
20
|
+
|
|
21
|
+
Saico now includes a sophisticated tool calling system with enterprise-grade safety and control features:
|
|
22
|
+
|
|
23
|
+
### Key Features:
|
|
24
|
+
- **๐๏ธ Depth Control** โ Prevent infinite recursion with configurable depth limits
|
|
25
|
+
- **๐ Deferred Execution** โ Tool calls automatically defer and resume when depth limits reached
|
|
26
|
+
- **๐ซ Duplicate Protection** โ Identical tool calls blocked while active to prevent resource waste
|
|
27
|
+
- **โฑ๏ธ Timeout Handling** โ Configurable timeouts (default: 5s) with graceful failure
|
|
28
|
+
- **๐ Repetition Prevention** โ Block excessive repeated tool calls (default: 20 max)
|
|
29
|
+
- **๐ฅ Message Queuing** โ Messages automatically queue when tool calls are pending
|
|
30
|
+
- **๐จโ๐ฉโ๐งโ๐ฆ Parent-Child Inheritance** โ Unresponded tool calls move from parent to child contexts
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## ๐ฆ Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install saico-ai-thread --save
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or clone manually:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/wanderli-ai/saico
|
|
44
|
+
cd saico
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## ๐งโ๐ป Usage
|
|
50
|
+
|
|
51
|
+
### Basic Setup with Tool Handler
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const { createQ } = require('saico');
|
|
55
|
+
|
|
56
|
+
// Define your tool handler
|
|
57
|
+
async function toolHandler(toolName, argumentsString) {
|
|
58
|
+
const args = JSON.parse(argumentsString);
|
|
59
|
+
|
|
60
|
+
switch (toolName) {
|
|
61
|
+
case 'get_weather':
|
|
62
|
+
return `Weather in ${args.location}: 72ยฐF, sunny`;
|
|
63
|
+
case 'book_hotel':
|
|
64
|
+
return `Booked ${args.hotel} for ${args.nights} nights`;
|
|
65
|
+
default:
|
|
66
|
+
return 'Tool not found';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create conversation with tool support
|
|
71
|
+
const q = createQ(
|
|
72
|
+
"You are a helpful assistant.", // prompt
|
|
73
|
+
null, // parent (null for root)
|
|
74
|
+
"main", // tag
|
|
75
|
+
4000, // token limit
|
|
76
|
+
null, // initial messages
|
|
77
|
+
toolHandler, // tool handler function
|
|
78
|
+
{ max_depth: 5, max_tool_repetition: 20 } // config
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Send a message that might trigger tool calls
|
|
82
|
+
await q.sendMessage('user', 'What\'s the weather in New York?');
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Create a Sub-Conversation with Tool Inheritance
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const subQ = q.spawnChild(
|
|
89
|
+
"Now focus only on hotel bookings.", // prompt
|
|
90
|
+
"hotels", // tag
|
|
91
|
+
null, // token limit (inherits from parent)
|
|
92
|
+
null, // initial messages
|
|
93
|
+
null, // tool handler (inherits from parent)
|
|
94
|
+
{ max_depth: 3 } // custom config
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
await subQ.sendMessage('user', 'Book me something in Rome.');
|
|
98
|
+
await subQ.close(); // Automatically summarizes and passes back to parent
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Advanced Tool Configuration
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const q = createQ(
|
|
105
|
+
"You are a travel assistant.",
|
|
106
|
+
null,
|
|
107
|
+
"travel",
|
|
108
|
+
8000,
|
|
109
|
+
null,
|
|
110
|
+
toolHandler,
|
|
111
|
+
{
|
|
112
|
+
max_depth: 8, // Allow deeper tool call chains
|
|
113
|
+
max_tool_repetition: 10 // Be more strict about repetitions
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Send message with custom tool options
|
|
118
|
+
await q.sendMessage('user', 'Plan my trip', null, {
|
|
119
|
+
handler: customToolHandler, // Override default tool handler
|
|
120
|
+
timeout: 10000, // 10 second timeout for this message's tools
|
|
121
|
+
nofunc: false // Ensure tool calls are enabled
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Hierarchy Example with Tool Calls
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
[Main] (toolHandler: generalTools)
|
|
129
|
+
โโโ [hotels] (inherits generalTools) โ tool calls + summary returned to [Main]
|
|
130
|
+
โโโ [flights] (inherits generalTools) โ tool calls + summary returned to [Main]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## ๐ง Enhanced Message API
|
|
136
|
+
|
|
137
|
+
Each message is stored with enhanced tool call support:
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
{
|
|
141
|
+
msg: {
|
|
142
|
+
role, // 'user', 'assistant', 'tool', 'system'
|
|
143
|
+
content, // Message content
|
|
144
|
+
name?, // Optional name for user/tool messages
|
|
145
|
+
tool_calls?, // Array of tool calls from assistant
|
|
146
|
+
tool_call_id? // ID linking tool responses to calls
|
|
147
|
+
},
|
|
148
|
+
opts: {
|
|
149
|
+
summary?, // Is this a summary message?
|
|
150
|
+
noreply?, // Skip AI reply for this message
|
|
151
|
+
nofunc?, // Disable tool calls for this message
|
|
152
|
+
handler?, // Custom tool handler override
|
|
153
|
+
timeout? // Custom timeout for tool calls
|
|
154
|
+
},
|
|
155
|
+
msgid: String, // Unique message identifier
|
|
156
|
+
replied: 0 | 1 | 3 // 0=pending, 1=user sent, 3=AI replied
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Enhanced API Methods
|
|
161
|
+
|
|
162
|
+
* `q[0]` โ Access nth message
|
|
163
|
+
* `q.length` โ Total messages
|
|
164
|
+
* `q.pushSummary(summary)` โ Manually inject a summary
|
|
165
|
+
* `q.getMsgContext()` โ Get summarized parent chain
|
|
166
|
+
* `q.serialize()` โ Export current state
|
|
167
|
+
* **NEW**: `q._hasPendingToolCalls()` โ Check for pending tool executions
|
|
168
|
+
* **NEW**: `q._processWaitingQueue()` โ Manually process queued messages
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## ๐ก๏ธ Tool Call Safety Features
|
|
173
|
+
|
|
174
|
+
### Depth Control & Deferred Execution
|
|
175
|
+
```js
|
|
176
|
+
const q = createQ("Assistant", null, "main", 4000, null, toolHandler, {
|
|
177
|
+
max_depth: 3 // Tool calls defer at depth 4+
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// When max depth reached:
|
|
181
|
+
// 1. Tool calls are deferred (not executed immediately)
|
|
182
|
+
// 2. Conversation continues normally
|
|
183
|
+
// 3. Deferred tools execute when depth reduces
|
|
184
|
+
// 4. Results are seamlessly integrated back
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Repetition Prevention
|
|
188
|
+
```js
|
|
189
|
+
const q = createQ("Assistant", null, "main", 4000, null, toolHandler, {
|
|
190
|
+
max_tool_repetition: 5 // Block tools called >5 times consecutively
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Automatically filters excessive repeated tool calls
|
|
194
|
+
// Logs: "Dropping excessive tool call: get_weather (hit max_tool_repetition=5)"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Duplicate Detection
|
|
198
|
+
```js
|
|
199
|
+
// If two identical tool calls (same name + arguments) are active:
|
|
200
|
+
// Second call returns: "Duplicate call detected. Please wait for previous call to complete."
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Timeout Handling
|
|
204
|
+
```js
|
|
205
|
+
// Tool calls automatically timeout (default: 5s)
|
|
206
|
+
// Returns: "Tool call 'slow_function' timed out after 5 seconds"
|
|
207
|
+
|
|
208
|
+
// Custom timeout per message:
|
|
209
|
+
await q.sendMessage('user', 'Run slow analysis', null, { timeout: 30000 });
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## ๐งช Summary Behavior
|
|
215
|
+
|
|
216
|
+
Summaries trigger when total token count exceeds 85% of the limit and are always triggered when `close()` is called.
|
|
217
|
+
Summaries are:
|
|
218
|
+
|
|
219
|
+
* Injected as special `[SUMMARY]: ...` messages
|
|
220
|
+
* Bubbled up into the parent context
|
|
221
|
+
* Excluded from re-summarization unless explicitly kept
|
|
222
|
+
* **NEW**: Include tool call results in summarization context
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## ๐ Redis Integration (Persistent Observable State)
|
|
227
|
+
|
|
228
|
+
This library includes an optional Redis-based persistence layer to automatically store and update conversation objects (or any JS object) using a **proxy-based observable**.
|
|
229
|
+
|
|
230
|
+
It supports:
|
|
231
|
+
|
|
232
|
+
* ๐ **Auto-saving on change** (with debounce)
|
|
233
|
+
* ๐ง **Selective serialization** (skips internal/private `_` properties)
|
|
234
|
+
* ๐๏ธ **Support for serializing `Messages` class**
|
|
235
|
+
* ๐ **Efficient diff-checking** (saves only when changed)
|
|
236
|
+
* **NEW**: **Tool call state persistence** (active calls, deferred calls, waiting queues)
|
|
237
|
+
|
|
238
|
+
### ๐ง Setup
|
|
239
|
+
|
|
240
|
+
1. Install `redis`:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
npm install redis
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
2. Initialize Redis:
|
|
247
|
+
|
|
248
|
+
```js
|
|
249
|
+
const { init, createObservableForRedis } = require('./redis-store');
|
|
250
|
+
await init(); // connects to redis://localhost:6379
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
3. Wrap a tool-enabled conversation:
|
|
254
|
+
|
|
255
|
+
```js
|
|
256
|
+
const { createQ } = require('./saico');
|
|
257
|
+
const q = createQ("Travel assistant", null, "flights", 3000, null, toolHandler);
|
|
258
|
+
|
|
259
|
+
// Wrap with Redis observable - tool states auto-persist
|
|
260
|
+
const obsQ = createObservableForRedis("q:session:12345", q);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Now, any changes to `obsQ` including tool call states, deferred calls, and message queues are **automatically saved** to Redis.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## ๐งผ Auto-Sanitization Rules
|
|
268
|
+
|
|
269
|
+
When saving to Redis:
|
|
270
|
+
|
|
271
|
+
* All keys starting with `_` are ignored.
|
|
272
|
+
* Custom `.serialize()` methods (like on `Messages`) are respected.
|
|
273
|
+
* Object updates are **debounced (1s)** and only saved if actual changes are detected.
|
|
274
|
+
* **NEW**: Tool call tracking data is sanitized automatically
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## ๐ OpenAI Integration
|
|
279
|
+
|
|
280
|
+
This library supports the modern OpenAI Tools API:
|
|
281
|
+
|
|
282
|
+
* **NEW**: Native `tool_calls` support (OpenAI's current standard)
|
|
283
|
+
* Backward compatibility with legacy `functions` format
|
|
284
|
+
* Automatic format conversion in openai.js
|
|
285
|
+
* Built-in retry logic with exponential backoff for rate limits
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
// OpenAI will return tool_calls in responses:
|
|
289
|
+
{
|
|
290
|
+
role: 'assistant',
|
|
291
|
+
content: 'I need to check the weather',
|
|
292
|
+
tool_calls: [{
|
|
293
|
+
id: 'call_abc123',
|
|
294
|
+
type: 'function',
|
|
295
|
+
function: {
|
|
296
|
+
name: 'get_weather',
|
|
297
|
+
arguments: '{"location": "New York"}'
|
|
298
|
+
}
|
|
299
|
+
}]
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Saico handles the complete tool execution cycle automatically
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## ๐งช Testing
|
|
308
|
+
|
|
309
|
+
Comprehensive test suite with **37 tests** covering:
|
|
310
|
+
|
|
311
|
+
* Core conversation management (25 tests)
|
|
312
|
+
* **NEW**: Tool calls functionality (12 tests):
|
|
313
|
+
- Basic tool execution
|
|
314
|
+
- Depth limits and deferred execution
|
|
315
|
+
- Repetition prevention and filtering
|
|
316
|
+
- Duplicate detection
|
|
317
|
+
- Message queuing systems
|
|
318
|
+
- Timeout handling
|
|
319
|
+
- Parent-child tool inheritance
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
npm test # Run full test suite
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## ๐ Project Structure
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
.
|
|
331
|
+
โโโ saico.js # Core implementation with tool calls
|
|
332
|
+
โโโ openai.js # OpenAI API wrapper with tools support
|
|
333
|
+
โโโ redis.js # Saico compatible redis wrapper
|
|
334
|
+
โโโ util.js # Utilities: token counting, etc.
|
|
335
|
+
โโโ test.js # Comprehensive test suite
|
|
336
|
+
โโโ msgs.js # Original enhanced version (reference)
|
|
337
|
+
โโโ README.md # This file
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## ๐ Migration Guide
|
|
343
|
+
|
|
344
|
+
If upgrading from older versions:
|
|
345
|
+
|
|
346
|
+
### Old API:
|
|
347
|
+
```js
|
|
348
|
+
const q = createQ(prompt, opts, msgs, parent);
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### New API:
|
|
352
|
+
```js
|
|
353
|
+
const q = createQ(prompt, parent, tag, token_limit, msgs, tool_handler, config);
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### Breaking Changes:
|
|
357
|
+
- Constructor parameter order changed
|
|
358
|
+
- `opts.tag` โ `tag` parameter
|
|
359
|
+
- `opts.token_limit` โ `token_limit` parameter
|
|
360
|
+
- Added `tool_handler` and `config` parameters
|
|
361
|
+
- `function_call` โ `tool_calls` in OpenAI responses
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## ๐ License
|
|
366
|
+
|
|
367
|
+
MIT License ยฉ [Wanderli.ai]
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## ๐ Contributing
|
|
372
|
+
|
|
373
|
+
Pull requests, issues, and suggestions welcome! Please fork the repo and open a PR, or submit issues directly.
|
|
374
|
+
|
|
375
|
+
Areas where contributions are especially welcome:
|
|
376
|
+
- Additional tool call safety features
|
|
377
|
+
- Performance optimizations for large conversations
|
|
378
|
+
- Extended test coverage
|
|
379
|
+
- Documentation improvements
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## ๐ฃ Acknowledgements
|
|
384
|
+
|
|
385
|
+
This project was inspired by the need for a lightweight, non-opinionated alternative to LangChain's memory modules, with full support for real-world LLM conversation flows and enterprise-grade tool calling capabilities.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## ๐ฎ Roadmap
|
|
390
|
+
|
|
391
|
+
- [ ] **Multi-model support** (Anthropic, Google, etc.)
|
|
392
|
+
- [ ] **Advanced tool call analytics** and monitoring
|
|
393
|
+
- [ ] **Custom summarization strategies**
|
|
394
|
+
- [ ] **Tool call result caching**
|
|
395
|
+
- [ ] **Streaming tool call responses**
|
|
396
|
+
- [ ] **Tool call permission systems**
|
|
397
|
+
|
|
398
|
+
Let me know if you'd like to see any of these features prioritized!
|