goblin-agents 2.1.0 → 2.1.1
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/lib/llm/aiAgent.js +53 -19
- package/lib/llm/utils.js +36 -10
- package/package.json +1 -1
package/lib/llm/aiAgent.js
CHANGED
|
@@ -129,6 +129,17 @@ function vectorToSqlLiteralInt8(vec) {
|
|
|
129
129
|
return hex;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
function cleanText(text) {
|
|
133
|
+
if (typeof text !== 'string') {
|
|
134
|
+
return text;
|
|
135
|
+
}
|
|
136
|
+
const {thinks, cleanText} = splitThinkTags(text);
|
|
137
|
+
for (const think of thinks) {
|
|
138
|
+
this.log.dbg(`🧐 ${think}`);
|
|
139
|
+
}
|
|
140
|
+
return cleanText;
|
|
141
|
+
}
|
|
142
|
+
|
|
132
143
|
class AiAgentShape {
|
|
133
144
|
id = id('aiAgent');
|
|
134
145
|
version = number;
|
|
@@ -294,7 +305,7 @@ class AiAgent extends Elf {
|
|
|
294
305
|
|
|
295
306
|
_provider;
|
|
296
307
|
_conf;
|
|
297
|
-
|
|
308
|
+
_mutex = getMutex;
|
|
298
309
|
|
|
299
310
|
/**
|
|
300
311
|
* @param {string} id
|
|
@@ -368,26 +379,30 @@ class AiAgent extends Elf {
|
|
|
368
379
|
}
|
|
369
380
|
|
|
370
381
|
async gen(prompt) {
|
|
382
|
+
await this._mutex.lock(this.id);
|
|
383
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
384
|
+
|
|
371
385
|
await this._loadProvider();
|
|
372
386
|
|
|
373
387
|
let options;
|
|
374
388
|
if (this.state.options) {
|
|
375
389
|
options = this.state.options.toJS();
|
|
376
390
|
}
|
|
377
|
-
const
|
|
391
|
+
const format = this.state.format
|
|
392
|
+
? typeof this.state.format === 'string'
|
|
393
|
+
? this.state.format
|
|
394
|
+
: this.state.format.toJS()
|
|
395
|
+
: undefined;
|
|
396
|
+
let res = await this._provider.gen({
|
|
378
397
|
model: this.state.model,
|
|
379
398
|
reasoning: this.state.reasoning,
|
|
380
399
|
prompt,
|
|
381
400
|
system: this.state.prompt,
|
|
382
|
-
format
|
|
401
|
+
format,
|
|
383
402
|
options,
|
|
384
403
|
stream: false,
|
|
385
404
|
});
|
|
386
|
-
|
|
387
|
-
for (const think of thinks) {
|
|
388
|
-
this.log.dbg(`🧐 ${think}`);
|
|
389
|
-
}
|
|
390
|
-
return cleanText;
|
|
405
|
+
return cleanText(res);
|
|
391
406
|
}
|
|
392
407
|
|
|
393
408
|
async reset(contextId, save = false) {
|
|
@@ -404,6 +419,9 @@ class AiAgent extends Elf {
|
|
|
404
419
|
}
|
|
405
420
|
|
|
406
421
|
async readPDFpages(pdfPath) {
|
|
422
|
+
await this._mutex.lock(this.id);
|
|
423
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
424
|
+
|
|
407
425
|
await this._loadProvider();
|
|
408
426
|
|
|
409
427
|
const images = await getPDFImages(pdfPath);
|
|
@@ -440,6 +458,10 @@ class AiAgent extends Elf {
|
|
|
440
458
|
if (!rawText) {
|
|
441
459
|
return null;
|
|
442
460
|
}
|
|
461
|
+
|
|
462
|
+
await this._mutex.lock(this.id);
|
|
463
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
464
|
+
|
|
443
465
|
await this._loadProvider();
|
|
444
466
|
const vectors = await this._provider.embed({
|
|
445
467
|
model: this.state.model,
|
|
@@ -457,8 +479,8 @@ class AiAgent extends Elf {
|
|
|
457
479
|
return [];
|
|
458
480
|
}
|
|
459
481
|
|
|
460
|
-
await this.
|
|
461
|
-
this.quest.defer(() => this.
|
|
482
|
+
await this._mutex.lock(this.id);
|
|
483
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
462
484
|
|
|
463
485
|
await this._loadProvider();
|
|
464
486
|
const res = await this._provider.embedInBatch({
|
|
@@ -477,6 +499,9 @@ class AiAgent extends Elf {
|
|
|
477
499
|
}
|
|
478
500
|
|
|
479
501
|
async chat(contextId, question, userDesktopId) {
|
|
502
|
+
await this._mutex.lock(this.id);
|
|
503
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
504
|
+
|
|
480
505
|
await this._loadProvider();
|
|
481
506
|
const contextMessages = this.state.messages[contextId] || [];
|
|
482
507
|
const messages = [
|
|
@@ -522,23 +547,22 @@ class AiAgent extends Elf {
|
|
|
522
547
|
}
|
|
523
548
|
}
|
|
524
549
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
this.log.dbg(`🧐 ${think}`);
|
|
550
|
+
const res = cleanText(result.message.content);
|
|
551
|
+
if (typeof res === 'string') {
|
|
552
|
+
//keep agent response
|
|
553
|
+
this.logic.chat(contextId, 'assistant', cleanText);
|
|
530
554
|
}
|
|
531
|
-
this.log.dbg(`🐈 Chat session ended : ${contextId}`);
|
|
532
555
|
|
|
533
|
-
|
|
556
|
+
this.log.dbg(`🐈 Chat session ended : ${contextId}`);
|
|
557
|
+
return res;
|
|
534
558
|
}
|
|
535
559
|
|
|
536
560
|
async callTools(contextId, message, userDesktopId) {
|
|
537
|
-
const {tool_calls
|
|
561
|
+
const {tool_calls} = message;
|
|
538
562
|
if (!tool_calls || !userDesktopId) {
|
|
539
563
|
return false;
|
|
540
564
|
}
|
|
541
|
-
this.logic.callTools(contextId,
|
|
565
|
+
this.logic.callTools(contextId, tool_calls);
|
|
542
566
|
for (const tool of tool_calls) {
|
|
543
567
|
const {
|
|
544
568
|
id,
|
|
@@ -585,6 +609,10 @@ class AiAgent extends Elf {
|
|
|
585
609
|
if (this.state.provider !== 'ollama') {
|
|
586
610
|
throw new Error('Streaming API not impl.');
|
|
587
611
|
}
|
|
612
|
+
|
|
613
|
+
await this._mutex.lock(this.id);
|
|
614
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
615
|
+
|
|
588
616
|
await this._loadProvider();
|
|
589
617
|
|
|
590
618
|
const contextMessages = this.state.messages[contextId] || [];
|
|
@@ -619,6 +647,9 @@ class AiAgent extends Elf {
|
|
|
619
647
|
}
|
|
620
648
|
|
|
621
649
|
async resumeExchange(resumePrompt, contextId) {
|
|
650
|
+
await this._mutex.lock(this.id);
|
|
651
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
652
|
+
|
|
622
653
|
await this._loadProvider();
|
|
623
654
|
const contextMessages = this.state.messages[contextId] || [];
|
|
624
655
|
|
|
@@ -643,6 +674,9 @@ class AiAgent extends Elf {
|
|
|
643
674
|
}
|
|
644
675
|
|
|
645
676
|
async react(reactPrompt, contextId, question) {
|
|
677
|
+
await this._mutex.lock(this.id);
|
|
678
|
+
this.quest.defer(() => this._mutex.unlock(this.id));
|
|
679
|
+
|
|
646
680
|
await this._loadProvider();
|
|
647
681
|
const feedId = await this.newQuestFeed();
|
|
648
682
|
const contextMessages = this.state.messages[contextId] || [];
|
package/lib/llm/utils.js
CHANGED
|
@@ -236,8 +236,23 @@ function groupSentences(text, maxSize) {
|
|
|
236
236
|
return chunks;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Construit un prompt à partir d'une liste d'articles, avec gestion du format et de la limite de tokens.
|
|
241
|
+
*
|
|
242
|
+
* @param {ArticleShape[]} articles - Liste des articles sources utilisés pour générer le prompt.
|
|
243
|
+
* @param {"text"|"json"} [format="text"] - Format de sortie attendu :
|
|
244
|
+
* - `"text"` → prompt en texte brut
|
|
245
|
+
* - `"json"` → prompt structuré en JSON
|
|
246
|
+
* @param {number} [maxTotalTokens] - Nombre maximal de tokens autorisés dans le prompt.
|
|
247
|
+
* Si non défini, aucun contrôle n'est appliqué.
|
|
248
|
+
* @param {function} [dbg=() => null] - Fonction de débogage optionnelle appelée
|
|
249
|
+
* avec des informations intermédiaires (signature `(info: any) => void`).
|
|
250
|
+
* @returns {string} - Le prompt généré, soit en texte brut (string),
|
|
251
|
+
* soit en objet JSON stringifié selon le format choisi.
|
|
252
|
+
*/
|
|
239
253
|
function buildPromptFromArticlesMulti(
|
|
240
254
|
articles,
|
|
255
|
+
format = 'text',
|
|
241
256
|
maxTotalTokens,
|
|
242
257
|
dbg = () => null
|
|
243
258
|
) {
|
|
@@ -247,15 +262,23 @@ function buildPromptFromArticlesMulti(
|
|
|
247
262
|
let sections = [];
|
|
248
263
|
let sectionTokenCount = 0;
|
|
249
264
|
for (const article of articles) {
|
|
250
|
-
const {kind, title, sourceId,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
265
|
+
const {kind, title, sourceId, description, finalScore} = article;
|
|
266
|
+
|
|
267
|
+
let articleBlock = '';
|
|
268
|
+
if (format === 'json') {
|
|
269
|
+
articleBlock = JSON.stringify({
|
|
270
|
+
kind,
|
|
271
|
+
title,
|
|
272
|
+
url: sourceId.startsWith('http') ? sourceId : null,
|
|
273
|
+
finalScore,
|
|
274
|
+
description,
|
|
275
|
+
});
|
|
276
|
+
} else {
|
|
277
|
+
articleBlock += `# ${title}\n\n`;
|
|
278
|
+
articleBlock += `**score :** ${finalScore}\n\n`;
|
|
279
|
+
articleBlock += `\`\`\`html\n${description}\n\`\`\`\n\n`;
|
|
280
|
+
}
|
|
281
|
+
|
|
259
282
|
dbg(articleBlock);
|
|
260
283
|
const tokensForArticle = estimateTokenCount(articleBlock);
|
|
261
284
|
|
|
@@ -268,7 +291,10 @@ function buildPromptFromArticlesMulti(
|
|
|
268
291
|
sectionTokenCount += tokensForArticle;
|
|
269
292
|
}
|
|
270
293
|
|
|
271
|
-
|
|
294
|
+
if (format === 'json') {
|
|
295
|
+
return `[${sections.join(',')}]`;
|
|
296
|
+
}
|
|
297
|
+
return sections.join('---\n\n');
|
|
272
298
|
}
|
|
273
299
|
|
|
274
300
|
/**
|