saico 2.3.0 → 2.5.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 +287 -297
- package/index.js +2 -9
- package/itask.js +16 -4
- package/msgs.js +106 -99
- package/package.json +1 -2
- package/saico.js +305 -41
- package/sid.js +0 -248
package/saico.js
CHANGED
|
@@ -17,6 +17,9 @@ const { Store } = require('./store.js');
|
|
|
17
17
|
* message Q context (when opts.createQ is true).
|
|
18
18
|
* - DB access works before and after activation.
|
|
19
19
|
*
|
|
20
|
+
* Saico orchestrates the full message payload sent to the LLM by walking its
|
|
21
|
+
* parent chain to aggregate prompts, tools, digests, and state summaries.
|
|
22
|
+
*
|
|
20
23
|
* `new Saico(opt)` returns a Redis observable proxy of the instance when
|
|
21
24
|
* Redis is available, enabling automatic persistence of public properties.
|
|
22
25
|
*/
|
|
@@ -26,14 +29,17 @@ class Saico {
|
|
|
26
29
|
* @param {string} [opt.id] - Instance ID (auto-generated if omitted)
|
|
27
30
|
* @param {string} [opt.name] - Instance name (defaults to class name)
|
|
28
31
|
* @param {string} [opt.prompt] - Class-level system prompt
|
|
29
|
-
* @param {Function} [opt.tool_handler] - Tool handler function
|
|
30
32
|
* @param {Array} [opt.functions] - Available AI functions
|
|
31
33
|
* @param {string} [opt.key] - Redis key override (default: 'saico:<id>')
|
|
32
34
|
* @param {boolean} [opt.redis=true] - Set false to skip Redis proxy
|
|
35
|
+
* @param {boolean} [opt.isolate] - Isolate: don't aggregate from ancestors
|
|
33
36
|
* @param {string} [opt.dynamodb_table] - DynamoDB table name (enables db accessor)
|
|
34
37
|
* @param {string} [opt.dynamodb_region] - AWS region for DynamoDB
|
|
35
38
|
* @param {Object} [opt.dynamodb_client] - Injectable DynamoDB client (for testing)
|
|
39
|
+
* @param {Object} [opt.db] - Pluggable DB backend
|
|
36
40
|
* @param {Object} [opt.store] - Store instance override
|
|
41
|
+
* @param {Object} [opt.userData] - Initial user data
|
|
42
|
+
* @param {Object} [opt.sessionConfig] - Session config overrides
|
|
37
43
|
*/
|
|
38
44
|
constructor(opt = {}) {
|
|
39
45
|
// Internal properties (underscore-prefixed, not persisted to Redis)
|
|
@@ -41,17 +47,26 @@ class Saico {
|
|
|
41
47
|
this._task = null;
|
|
42
48
|
this._store = opt.store || Store.instance || null;
|
|
43
49
|
this._opt = opt;
|
|
50
|
+
this._isolate = opt.isolate || false;
|
|
44
51
|
|
|
45
52
|
// Public configuration
|
|
46
53
|
this.name = opt.name || this.constructor.name || 'saico';
|
|
47
54
|
this.prompt = opt.prompt || '';
|
|
48
|
-
this.tool_handler = opt.tool_handler || null;
|
|
49
55
|
this.functions = opt.functions || null;
|
|
50
56
|
|
|
57
|
+
// Absorbed from Sid
|
|
58
|
+
this.userData = opt.userData || {};
|
|
59
|
+
this.tm_create = Date.now();
|
|
60
|
+
this.sessionConfig = {
|
|
61
|
+
token_limit: opt.token_limit,
|
|
62
|
+
max_depth: opt.max_depth,
|
|
63
|
+
max_tool_repetition: opt.max_tool_repetition,
|
|
64
|
+
queue_limit: opt.queue_limit,
|
|
65
|
+
min_chat_messages: opt.min_chat_messages,
|
|
66
|
+
...opt.sessionConfig,
|
|
67
|
+
};
|
|
68
|
+
|
|
51
69
|
// DB backend — pluggable storage adapter.
|
|
52
|
-
// Any adapter that implements the same interface (put/get/delete/query/
|
|
53
|
-
// getAll/update/updatePath/listAppend/listAppendPath/nextCounterId/
|
|
54
|
-
// getCounterValue/setCounterValue/countItems) can be used.
|
|
55
70
|
this._db = opt.db || null;
|
|
56
71
|
if (!this._db && opt.dynamodb_table) {
|
|
57
72
|
const { DynamoDBAdapter } = require('./dynamo.js');
|
|
@@ -79,7 +94,6 @@ class Saico {
|
|
|
79
94
|
* @param {Object} opts
|
|
80
95
|
* @param {boolean} [opts.createQ] - If true, attach a message Q (Context)
|
|
81
96
|
* @param {string} [opts.prompt] - Additional prompt (appended to class-level)
|
|
82
|
-
* @param {Function} [opts.tool_handler] - Override tool handler
|
|
83
97
|
* @param {Array} [opts.functions] - Override functions
|
|
84
98
|
* @param {Array} [opts.states] - Task state functions
|
|
85
99
|
* @param {Itask} [opts.parent] - Parent task to spawn under
|
|
@@ -109,7 +123,6 @@ class Saico {
|
|
|
109
123
|
id: opts.taskId,
|
|
110
124
|
async: true,
|
|
111
125
|
store: this._store,
|
|
112
|
-
tool_handler: opts.tool_handler || this.tool_handler,
|
|
113
126
|
functions: opts.functions || this.functions,
|
|
114
127
|
bind: this, // State functions run with Saico instance as `this`
|
|
115
128
|
};
|
|
@@ -119,22 +132,18 @@ class Saico {
|
|
|
119
132
|
|
|
120
133
|
this._task = new Itask(taskOpt, states);
|
|
121
134
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
this._task.getStateSummary = function () {
|
|
125
|
-
return saicoInstance.getStateSummary();
|
|
126
|
-
};
|
|
135
|
+
// Store Saico reference on task for parent chain traversal
|
|
136
|
+
this._task._saico = this;
|
|
127
137
|
|
|
128
138
|
// Create message Q context if requested (only via createQ flag, NOT prompt)
|
|
129
139
|
if (opts.createQ) {
|
|
130
140
|
const contextConfig = {
|
|
131
141
|
tag: opts.tag || this._task.id,
|
|
132
|
-
token_limit: opts.token_limit,
|
|
133
|
-
max_depth: opts.max_depth,
|
|
134
|
-
max_tool_repetition: opts.max_tool_repetition,
|
|
135
|
-
queue_limit: opts.queue_limit,
|
|
136
|
-
min_chat_messages: opts.min_chat_messages,
|
|
137
|
-
tool_handler: taskOpt.tool_handler,
|
|
142
|
+
token_limit: opts.token_limit ?? this.sessionConfig.token_limit,
|
|
143
|
+
max_depth: opts.max_depth ?? this.sessionConfig.max_depth,
|
|
144
|
+
max_tool_repetition: opts.max_tool_repetition ?? this.sessionConfig.max_tool_repetition,
|
|
145
|
+
queue_limit: opts.queue_limit ?? this.sessionConfig.queue_limit,
|
|
146
|
+
min_chat_messages: opts.min_chat_messages ?? this.sessionConfig.min_chat_messages,
|
|
138
147
|
functions: taskOpt.functions,
|
|
139
148
|
sequential_mode: opts.sequential_mode,
|
|
140
149
|
msgs: opts.msgs,
|
|
@@ -153,28 +162,143 @@ class Saico {
|
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
/**
|
|
156
|
-
* Deactivate — close context, cancel task
|
|
165
|
+
* Deactivate — bubble cleaned messages to parent, close context, cancel task.
|
|
166
|
+
* Pushes cleaned messages (no tool calls, no BACKEND) into the parent's Q,
|
|
167
|
+
* then closes the context without the default summary bubbling.
|
|
157
168
|
*/
|
|
158
169
|
async deactivate() {
|
|
159
170
|
if (!this._task) return;
|
|
160
|
-
if (this._task.context)
|
|
161
|
-
|
|
171
|
+
if (this._task.context) {
|
|
172
|
+
// Find parent context to bubble cleaned messages
|
|
173
|
+
let parentTask = this._task.parent;
|
|
174
|
+
let parentCtx = null;
|
|
175
|
+
while (parentTask) {
|
|
176
|
+
if (parentTask.context) { parentCtx = parentTask.context; break; }
|
|
177
|
+
parentTask = parentTask.parent;
|
|
178
|
+
}
|
|
179
|
+
if (parentCtx) {
|
|
180
|
+
const cleaned = this.getRecentMessages(Infinity);
|
|
181
|
+
for (const msg of cleaned)
|
|
182
|
+
parentCtx.push(msg);
|
|
183
|
+
}
|
|
184
|
+
// Clean tool calls and close context without additional summary bubbling.
|
|
185
|
+
// We already pushed cleaned messages above — closeContext's own
|
|
186
|
+
// summarization would double-bubble.
|
|
187
|
+
if (this._task.context_id && typeof this._task.context.cleanToolCallsByTag === 'function')
|
|
188
|
+
this._task.context.cleanToolCallsByTag(this._task.context_id);
|
|
189
|
+
this._task.context = null;
|
|
190
|
+
}
|
|
162
191
|
this._task._ecancel();
|
|
163
192
|
this._task = null;
|
|
164
193
|
}
|
|
165
194
|
|
|
166
|
-
// ----
|
|
195
|
+
// ---- Saico parent chain traversal ----
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Walk up the Saico parent chain (stop at isolate boundary or root).
|
|
199
|
+
* Returns array ordered root -> ... -> this.
|
|
200
|
+
*/
|
|
201
|
+
_getSaicoAncestors() {
|
|
202
|
+
const chain = [this];
|
|
203
|
+
if (this._isolate) return chain;
|
|
204
|
+
let task = this._task?.parent;
|
|
205
|
+
while (task) {
|
|
206
|
+
if (task._saico) {
|
|
207
|
+
chain.unshift(task._saico);
|
|
208
|
+
if (task._saico._isolate) break;
|
|
209
|
+
}
|
|
210
|
+
task = task.parent;
|
|
211
|
+
}
|
|
212
|
+
return chain; // root -> ... -> this
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Build preamble and aggregated functions by walking the Saico chain.
|
|
217
|
+
* @param {Context} activeCtx - The deepest active context (for state summary logic)
|
|
218
|
+
* @returns {{ preamble: Array, allFunctions: Array }}
|
|
219
|
+
*/
|
|
220
|
+
_buildPreamble(activeCtx) {
|
|
221
|
+
const chain = this._getSaicoAncestors();
|
|
222
|
+
const preamble = [];
|
|
223
|
+
const allFunctions = [];
|
|
224
|
+
|
|
225
|
+
for (const saico of chain) {
|
|
226
|
+
// Prompt
|
|
227
|
+
if (saico.prompt)
|
|
228
|
+
preamble.push({ role: 'system', content: saico.prompt });
|
|
229
|
+
|
|
230
|
+
// State summary (can return array)
|
|
231
|
+
const summary = saico._getStateSummary(activeCtx);
|
|
232
|
+
if (Array.isArray(summary)) {
|
|
233
|
+
for (const item of summary) {
|
|
234
|
+
if (typeof item === 'string')
|
|
235
|
+
preamble.push({ role: 'system', content: '[State Summary]\n' + item });
|
|
236
|
+
else
|
|
237
|
+
preamble.push(item); // {role, content} message object
|
|
238
|
+
}
|
|
239
|
+
} else if (summary) {
|
|
240
|
+
preamble.push({ role: 'system', content: '[State Summary]\n' + summary });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Tools digest
|
|
244
|
+
if (saico.context?.tool_digest?.length > 0) {
|
|
245
|
+
const digestText = saico.context.tool_digest.map(entry =>
|
|
246
|
+
`[${new Date(entry.tm).toISOString()}] ${entry.tool}: ${entry.result}`
|
|
247
|
+
).join('\n');
|
|
248
|
+
preamble.push({ role: 'system', content: '[Tool Activity Log]\n' + digestText });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Collect functions
|
|
252
|
+
if (saico.functions) allFunctions.push(...saico.functions);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { preamble, allFunctions };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---- Message orchestration ----
|
|
167
259
|
|
|
168
260
|
async sendMessage(content, functions, opts) {
|
|
169
261
|
if (!this._task)
|
|
170
262
|
throw new Error('Not activated. Call activate() first.');
|
|
171
|
-
|
|
263
|
+
|
|
264
|
+
// Find the active context (own or walk up)
|
|
265
|
+
let ctx = this._task.getContext() || this._task.findContext();
|
|
266
|
+
if (!ctx)
|
|
267
|
+
throw new Error('No context available');
|
|
268
|
+
|
|
269
|
+
// Build preamble by walking Saico chain
|
|
270
|
+
const activeCtx = this._task.findDeepestContext() || ctx;
|
|
271
|
+
const { preamble, allFunctions } = this._buildPreamble(activeCtx);
|
|
272
|
+
|
|
273
|
+
// Merge with call-specific functions
|
|
274
|
+
if (functions) allFunctions.push(...(Array.isArray(functions) ? functions : [functions]));
|
|
275
|
+
|
|
276
|
+
opts = Object.assign({}, opts, {
|
|
277
|
+
tag: this._task.context_id,
|
|
278
|
+
_preamble: preamble,
|
|
279
|
+
_aggregatedFunctions: allFunctions.length > 0 ? allFunctions : null,
|
|
280
|
+
});
|
|
281
|
+
return ctx.sendMessage('user', '[BACKEND] ' + content, null, opts);
|
|
172
282
|
}
|
|
173
283
|
|
|
174
284
|
async recvChatMessage(content, opts) {
|
|
175
285
|
if (!this._task)
|
|
176
286
|
throw new Error('Not activated. Call activate() first.');
|
|
177
|
-
|
|
287
|
+
|
|
288
|
+
// Route DOWN to deepest descendant with a msg Q
|
|
289
|
+
const ctx = this._task.findDeepestContext();
|
|
290
|
+
if (!ctx)
|
|
291
|
+
throw new Error('No context available');
|
|
292
|
+
|
|
293
|
+
// Build preamble by walking Saico chain
|
|
294
|
+
const { preamble, allFunctions } = this._buildPreamble(ctx);
|
|
295
|
+
|
|
296
|
+
opts = Object.assign({}, opts, {
|
|
297
|
+
tag: ctx.tag,
|
|
298
|
+
_preamble: preamble,
|
|
299
|
+
_aggregatedFunctions: allFunctions.length > 0 ? allFunctions : null,
|
|
300
|
+
});
|
|
301
|
+
return ctx.sendMessage('user', content, null, opts);
|
|
178
302
|
}
|
|
179
303
|
|
|
180
304
|
// ---- Task delegation ----
|
|
@@ -200,12 +324,11 @@ class Saico {
|
|
|
200
324
|
if (opt.prompt) {
|
|
201
325
|
const childContext = new Context(opt.prompt, childTask, {
|
|
202
326
|
tag: opt.tag || childTask.id,
|
|
203
|
-
token_limit: opt.token_limit,
|
|
204
|
-
max_depth: opt.max_depth,
|
|
205
|
-
max_tool_repetition: opt.max_tool_repetition,
|
|
206
|
-
queue_limit: opt.queue_limit,
|
|
207
|
-
min_chat_messages: opt.min_chat_messages,
|
|
208
|
-
tool_handler: opt.tool_handler || this.tool_handler,
|
|
327
|
+
token_limit: opt.token_limit ?? this.sessionConfig.token_limit,
|
|
328
|
+
max_depth: opt.max_depth ?? this.sessionConfig.max_depth,
|
|
329
|
+
max_tool_repetition: opt.max_tool_repetition ?? this.sessionConfig.max_tool_repetition,
|
|
330
|
+
queue_limit: opt.queue_limit ?? this.sessionConfig.queue_limit,
|
|
331
|
+
min_chat_messages: opt.min_chat_messages ?? this.sessionConfig.min_chat_messages,
|
|
209
332
|
functions: opt.functions || this.functions,
|
|
210
333
|
});
|
|
211
334
|
childTask.setContext(childContext);
|
|
@@ -238,10 +361,90 @@ class Saico {
|
|
|
238
361
|
return childTask;
|
|
239
362
|
}
|
|
240
363
|
|
|
364
|
+
// ---- State Summary ----
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Override in subclasses to provide a state summary.
|
|
368
|
+
* @returns {string}
|
|
369
|
+
*/
|
|
370
|
+
getStateSummary() { return ''; }
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get recent user/assistant messages (filtering out tool calls and BACKEND msgs).
|
|
374
|
+
* @param {number} n - Max number of messages to return
|
|
375
|
+
* @returns {Array<{role: string, content: string}>}
|
|
376
|
+
*/
|
|
377
|
+
getRecentMessages(n = 5) {
|
|
378
|
+
if (!this.context) return [];
|
|
379
|
+
return this.context._msgs
|
|
380
|
+
.filter(m => {
|
|
381
|
+
if (m.msg.role === 'tool' || m.msg.tool_calls) return false;
|
|
382
|
+
if (typeof m.msg.content === 'string' && m.msg.content.startsWith('[BACKEND]')) return false;
|
|
383
|
+
return m.msg.role === 'user' || m.msg.role === 'assistant';
|
|
384
|
+
})
|
|
385
|
+
.slice(-n)
|
|
386
|
+
.map(m => ({ role: m.msg.role, content: m.msg.content }));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Internal state summary builder. Includes own getStateSummary() and,
|
|
391
|
+
* if this context is NOT the active (deepest) Q, includes recent messages.
|
|
392
|
+
* @param {Context} activeCtx - The deepest active context
|
|
393
|
+
* @returns {Array|string|null}
|
|
394
|
+
*/
|
|
395
|
+
_getStateSummary(activeCtx) {
|
|
396
|
+
const parts = [];
|
|
397
|
+
const own = this.getStateSummary();
|
|
398
|
+
if (own) parts.push(own);
|
|
399
|
+
|
|
400
|
+
// If this context is NOT the active (deepest) Q, include recent messages
|
|
401
|
+
if (this.context && activeCtx && this.context !== activeCtx) {
|
|
402
|
+
const recent = this.getRecentMessages(5);
|
|
403
|
+
if (recent.length > 0) parts.push(...recent);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return parts.length > 0 ? parts : null;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// ---- User Data (absorbed from Sid) ----
|
|
410
|
+
|
|
411
|
+
setUserData(key, value) {
|
|
412
|
+
this.userData[key] = value;
|
|
413
|
+
return this;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
getUserData(key) {
|
|
417
|
+
return key ? this.userData[key] : this.userData;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
clearUserData() {
|
|
421
|
+
this.userData = {};
|
|
422
|
+
return this;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ---- Session Info ----
|
|
426
|
+
|
|
427
|
+
getSessionInfo() {
|
|
428
|
+
return {
|
|
429
|
+
id: this._id,
|
|
430
|
+
name: this.name,
|
|
431
|
+
running: this._task?.running || false,
|
|
432
|
+
completed: this._task?._completed || false,
|
|
433
|
+
messageCount: this.context?.length || 0,
|
|
434
|
+
childCount: this._task?.child?.size || 0,
|
|
435
|
+
userData: this.userData,
|
|
436
|
+
uptime: Date.now() - this.tm_create,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async closeSession() {
|
|
441
|
+
if (!this._task) return;
|
|
442
|
+
if (this._task.context)
|
|
443
|
+
await this._task.context.close();
|
|
444
|
+
this._task._ecancel();
|
|
445
|
+
}
|
|
446
|
+
|
|
241
447
|
// ---- Generic DB access ----
|
|
242
|
-
// These delegate to whatever _db backend was configured (DynamoDB, MongoDB,
|
|
243
|
-
// etc). Upper layers call these and don't care about the storage impl.
|
|
244
|
-
// All are no-ops (return undefined) when no backend is configured.
|
|
245
448
|
|
|
246
449
|
async dbPutItem(item, table) {
|
|
247
450
|
if (!this._db) return;
|
|
@@ -250,7 +453,8 @@ class Saico {
|
|
|
250
453
|
|
|
251
454
|
async dbGetItem(key, value, table) {
|
|
252
455
|
if (!this._db) return;
|
|
253
|
-
|
|
456
|
+
const result = await this._db.get(key, value, table);
|
|
457
|
+
return result ? this._deserializeRecord(result) : result;
|
|
254
458
|
}
|
|
255
459
|
|
|
256
460
|
async dbDeleteItem(key, value, table) {
|
|
@@ -260,12 +464,18 @@ class Saico {
|
|
|
260
464
|
|
|
261
465
|
async dbQuery(index, key, value, table) {
|
|
262
466
|
if (!this._db) return;
|
|
263
|
-
|
|
467
|
+
const results = await this._db.query(index, key, value, table);
|
|
468
|
+
return Array.isArray(results)
|
|
469
|
+
? results.map(r => this._deserializeRecord(r))
|
|
470
|
+
: results;
|
|
264
471
|
}
|
|
265
472
|
|
|
266
473
|
async dbGetAll(table) {
|
|
267
474
|
if (!this._db) return;
|
|
268
|
-
|
|
475
|
+
const results = await this._db.getAll(table);
|
|
476
|
+
return Array.isArray(results)
|
|
477
|
+
? results.map(r => this._deserializeRecord(r))
|
|
478
|
+
: results;
|
|
269
479
|
}
|
|
270
480
|
|
|
271
481
|
async dbUpdate(key, keyValue, setKey, item, table) {
|
|
@@ -308,14 +518,15 @@ class Saico {
|
|
|
308
518
|
return this._db.countItems(table);
|
|
309
519
|
}
|
|
310
520
|
|
|
311
|
-
// ----
|
|
521
|
+
// ---- DB deserialization hook ----
|
|
312
522
|
|
|
313
523
|
/**
|
|
314
|
-
* Override in subclasses to
|
|
315
|
-
*
|
|
316
|
-
* @
|
|
524
|
+
* Override in subclasses to transform raw DB records (e.g. restore class instances).
|
|
525
|
+
* Called by dbGetItem, dbQuery, dbGetAll.
|
|
526
|
+
* @param {Object} raw - Raw record from DB
|
|
527
|
+
* @returns {Object} Transformed record
|
|
317
528
|
*/
|
|
318
|
-
|
|
529
|
+
_deserializeRecord(raw) { return raw; }
|
|
319
530
|
|
|
320
531
|
// ---- Serialization ----
|
|
321
532
|
|
|
@@ -324,6 +535,10 @@ class Saico {
|
|
|
324
535
|
id: this._id,
|
|
325
536
|
name: this.name,
|
|
326
537
|
prompt: this.prompt,
|
|
538
|
+
userData: this.userData,
|
|
539
|
+
sessionConfig: this.sessionConfig,
|
|
540
|
+
tm_create: this.tm_create,
|
|
541
|
+
isolate: this._isolate,
|
|
327
542
|
};
|
|
328
543
|
if (this._task) {
|
|
329
544
|
data.task = {
|
|
@@ -340,6 +555,55 @@ class Saico {
|
|
|
340
555
|
}
|
|
341
556
|
return JSON.stringify(data);
|
|
342
557
|
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Restore a Saico instance from serialized data.
|
|
561
|
+
* @param {string|Object} data - Serialized data (JSON string or object)
|
|
562
|
+
* @param {Object} opt - Options (functions, store, states, etc.)
|
|
563
|
+
* @returns {Saico}
|
|
564
|
+
*/
|
|
565
|
+
static deserialize(data, opt = {}) {
|
|
566
|
+
const parsed = typeof data === 'string' ? JSON.parse(data) : data;
|
|
567
|
+
|
|
568
|
+
const instance = new Saico({
|
|
569
|
+
id: parsed.id,
|
|
570
|
+
name: parsed.name,
|
|
571
|
+
prompt: parsed.prompt,
|
|
572
|
+
userData: parsed.userData,
|
|
573
|
+
sessionConfig: parsed.sessionConfig,
|
|
574
|
+
isolate: parsed.isolate,
|
|
575
|
+
functions: opt.functions || parsed.task?.context?.functions,
|
|
576
|
+
store: opt.store,
|
|
577
|
+
redis: false, // No Redis proxy during deserialization
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
instance.tm_create = parsed.tm_create || instance.tm_create;
|
|
581
|
+
|
|
582
|
+
// Activate with restored context if task data exists
|
|
583
|
+
if (parsed.task) {
|
|
584
|
+
instance.activate({
|
|
585
|
+
createQ: !!parsed.task.context,
|
|
586
|
+
taskId: parsed.task.id,
|
|
587
|
+
tag: parsed.task.context?.tag,
|
|
588
|
+
chat_history: parsed.task.context?.chat_history,
|
|
589
|
+
functions: opt.functions || parsed.task.context?.functions,
|
|
590
|
+
states: opt.states || [],
|
|
591
|
+
...opt,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// Restore messages to context
|
|
595
|
+
if (parsed.task.context?.msgs && instance._task.context) {
|
|
596
|
+
instance._task.context._msgs = parsed.task.context.msgs;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Restore tool_digest
|
|
600
|
+
if (Array.isArray(parsed.task.context?.tool_digest) && instance._task.context) {
|
|
601
|
+
instance._task.context.tool_digest = parsed.task.context.tool_digest;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
return instance;
|
|
606
|
+
}
|
|
343
607
|
}
|
|
344
608
|
|
|
345
609
|
module.exports = { Saico };
|