goblin-agents 1.0.4 → 2.1.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/config.js +0 -24
- package/lib/llm/aiAgent.js +128 -35
- package/lib/llm/providers.js +22 -2
- package/lib/llm/utils.js +54 -34
- package/package.json +1 -1
package/config.js
CHANGED
|
@@ -7,28 +7,4 @@ module.exports = [
|
|
|
7
7
|
message: 'Agents version',
|
|
8
8
|
default: 2,
|
|
9
9
|
},
|
|
10
|
-
{
|
|
11
|
-
type: 'input',
|
|
12
|
-
name: 'defaultProfile',
|
|
13
|
-
message: 'Agents default profile',
|
|
14
|
-
default: null,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
type: 'input',
|
|
18
|
-
name: 'defaultSettings',
|
|
19
|
-
message: 'Agents default setting',
|
|
20
|
-
default: null,
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
type: 'input',
|
|
24
|
-
name: 'profiles',
|
|
25
|
-
message: 'Agents profiles for overriding settings',
|
|
26
|
-
default: {},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
type: 'input',
|
|
30
|
-
name: 'settings',
|
|
31
|
-
message: 'Settings by name',
|
|
32
|
-
default: {},
|
|
33
|
-
},
|
|
34
10
|
];
|
package/lib/llm/aiAgent.js
CHANGED
|
@@ -13,7 +13,7 @@ const {
|
|
|
13
13
|
} = require('xcraft-core-stones');
|
|
14
14
|
const {getMutex} = require('xcraft-core-utils/lib/locks.js');
|
|
15
15
|
const LLMProvider = require('./providers.js');
|
|
16
|
-
const {OCR_SYSTEM_PROMPT, getPDFImages} = require('./utils.js');
|
|
16
|
+
const {OCR_SYSTEM_PROMPT, getPDFImages, splitThinkTags} = require('./utils.js');
|
|
17
17
|
|
|
18
18
|
class MessageShape {
|
|
19
19
|
role = enumeration('assistant', 'user', 'tool');
|
|
@@ -98,6 +98,37 @@ function vectorToSqlLiteral(vec) {
|
|
|
98
98
|
return hex;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Convertit un vecteur float32 normalisé [-1,1]
|
|
103
|
+
* en int8 [-128,127] et retourne une chaîne hex
|
|
104
|
+
* pour stockage en SQL.
|
|
105
|
+
*
|
|
106
|
+
* @param {Array|Float32Array} vec - vecteur normalisé
|
|
107
|
+
* @returns {string} - représentation hex
|
|
108
|
+
*/
|
|
109
|
+
function vectorToSqlLiteralInt8(vec) {
|
|
110
|
+
const dimensions = vec.length;
|
|
111
|
+
const qvec = new Int8Array(dimensions);
|
|
112
|
+
|
|
113
|
+
// Quantization uniforme
|
|
114
|
+
const step = 2.0 / 255.0;
|
|
115
|
+
for (let i = 0; i < dimensions; i++) {
|
|
116
|
+
qvec[i] = Math.round((vec[i] + 1.0) / step - 128);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Vue mémoire en octets non signés
|
|
120
|
+
const view = new Uint8Array(qvec.buffer, qvec.byteOffset, qvec.byteLength);
|
|
121
|
+
|
|
122
|
+
// Conversion en hex
|
|
123
|
+
let hex = '';
|
|
124
|
+
for (let i = 0; i < view.length; i++) {
|
|
125
|
+
const byte = view[i];
|
|
126
|
+
hex += byte < 16 ? '0' + byte.toString(16) : byte.toString(16);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return hex;
|
|
130
|
+
}
|
|
131
|
+
|
|
101
132
|
class AiAgentShape {
|
|
102
133
|
id = id('aiAgent');
|
|
103
134
|
version = number;
|
|
@@ -114,6 +145,8 @@ class AiAgentShape {
|
|
|
114
145
|
tools = option(array(object));
|
|
115
146
|
toolServiceId = option(string);
|
|
116
147
|
usability = enumeration('disabled', 'experimental', 'stable', 'deprecated');
|
|
148
|
+
reasoning = option(boolean);
|
|
149
|
+
vectorQuality = option(enumeration('f32', 'int8'));
|
|
117
150
|
meta = MetaShape;
|
|
118
151
|
}
|
|
119
152
|
|
|
@@ -123,15 +156,17 @@ class AiAgentState extends Elf.Sculpt(AiAgentShape) {}
|
|
|
123
156
|
const defaultValues = {
|
|
124
157
|
name: 'agent',
|
|
125
158
|
role: 'assistant',
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
model: 'mistral-small',
|
|
159
|
+
provider: 'open-ai',
|
|
160
|
+
model: '',
|
|
129
161
|
options: {},
|
|
130
162
|
host: 'http://127.0.0.1:11434',
|
|
131
163
|
headers: {},
|
|
132
164
|
format: null,
|
|
165
|
+
toolServiceId: null,
|
|
133
166
|
tools: null,
|
|
167
|
+
reasoning: false,
|
|
134
168
|
usability: 'disabled',
|
|
169
|
+
vectorQuality: 'f32',
|
|
135
170
|
};
|
|
136
171
|
|
|
137
172
|
class AiAgentLogic extends Elf.Archetype {
|
|
@@ -149,8 +184,10 @@ class AiAgentLogic extends Elf.Archetype {
|
|
|
149
184
|
host: defaultValues.host,
|
|
150
185
|
headers: defaultValues.headers,
|
|
151
186
|
format: defaultValues.format,
|
|
187
|
+
toolServiceId: defaultValues.toolServiceId,
|
|
152
188
|
tools: defaultValues.tools,
|
|
153
189
|
usability: defaultValues.usability,
|
|
190
|
+
vectorQuality: 'f32',
|
|
154
191
|
meta: {
|
|
155
192
|
status: 'published',
|
|
156
193
|
},
|
|
@@ -240,6 +277,11 @@ class AiAgentLogic extends Elf.Archetype {
|
|
|
240
277
|
state._state.set(path, newValue);
|
|
241
278
|
}
|
|
242
279
|
|
|
280
|
+
publish() {
|
|
281
|
+
const {state} = this;
|
|
282
|
+
state.meta.status = 'published';
|
|
283
|
+
}
|
|
284
|
+
|
|
243
285
|
trash() {
|
|
244
286
|
const {state} = this;
|
|
245
287
|
state.meta.status = 'trashed';
|
|
@@ -300,7 +342,7 @@ class AiAgent extends Elf {
|
|
|
300
342
|
}
|
|
301
343
|
|
|
302
344
|
const Instance = LLMProvider.get(this.state.provider);
|
|
303
|
-
this._provider = new Instance(this.state.host, this.state.headers);
|
|
345
|
+
this._provider = new Instance(this.state.host, this.state.headers.toJS());
|
|
304
346
|
this._conf = {provider, host, headersStr};
|
|
305
347
|
}
|
|
306
348
|
|
|
@@ -332,14 +374,20 @@ class AiAgent extends Elf {
|
|
|
332
374
|
if (this.state.options) {
|
|
333
375
|
options = this.state.options.toJS();
|
|
334
376
|
}
|
|
335
|
-
|
|
377
|
+
const res = await this._provider.gen({
|
|
336
378
|
model: this.state.model,
|
|
379
|
+
reasoning: this.state.reasoning,
|
|
337
380
|
prompt,
|
|
338
381
|
system: this.state.prompt,
|
|
339
382
|
format: this.state.format?.toJS(),
|
|
340
383
|
options,
|
|
341
384
|
stream: false,
|
|
342
385
|
});
|
|
386
|
+
const {thinks, cleanText} = splitThinkTags(res);
|
|
387
|
+
for (const think of thinks) {
|
|
388
|
+
this.log.dbg(`🧐 ${think}`);
|
|
389
|
+
}
|
|
390
|
+
return cleanText;
|
|
343
391
|
}
|
|
344
392
|
|
|
345
393
|
async reset(contextId, save = false) {
|
|
@@ -380,6 +428,7 @@ class AiAgent extends Elf {
|
|
|
380
428
|
];
|
|
381
429
|
const result = await this._provider.chat({
|
|
382
430
|
model: this.state.model,
|
|
431
|
+
reasoning: this.state.reasoning,
|
|
383
432
|
messages,
|
|
384
433
|
});
|
|
385
434
|
descriptions.push(result.message.content);
|
|
@@ -396,7 +445,11 @@ class AiAgent extends Elf {
|
|
|
396
445
|
model: this.state.model,
|
|
397
446
|
input: rawText,
|
|
398
447
|
});
|
|
399
|
-
|
|
448
|
+
let vectorToSql = vectorToSqlLiteral;
|
|
449
|
+
if (this.state.vectorQuality === 'int8') {
|
|
450
|
+
vectorToSql = vectorToSqlLiteralInt8;
|
|
451
|
+
}
|
|
452
|
+
return vectorToSql(vectors);
|
|
400
453
|
}
|
|
401
454
|
|
|
402
455
|
async embedInBatch(rawTexts = []) {
|
|
@@ -412,22 +465,24 @@ class AiAgent extends Elf {
|
|
|
412
465
|
model: this.state.model,
|
|
413
466
|
input: rawTexts,
|
|
414
467
|
});
|
|
415
|
-
|
|
468
|
+
let vectorToSql = vectorToSqlLiteral;
|
|
469
|
+
if (this.state.vectorQuality === 'int8') {
|
|
470
|
+
vectorToSql = vectorToSqlLiteralInt8;
|
|
471
|
+
}
|
|
472
|
+
return res.map((v) => vectorToSql(v));
|
|
416
473
|
}
|
|
417
474
|
|
|
418
475
|
async set(contextId, messages) {
|
|
419
476
|
this.logic.set(contextId, messages);
|
|
420
477
|
}
|
|
421
478
|
|
|
422
|
-
async chat(contextId, question, userDesktopId
|
|
479
|
+
async chat(contextId, question, userDesktopId) {
|
|
423
480
|
await this._loadProvider();
|
|
424
481
|
const contextMessages = this.state.messages[contextId] || [];
|
|
425
482
|
const messages = [
|
|
426
483
|
{role: 'system', content: this.state.prompt},
|
|
427
484
|
...Array.from(contextMessages).map((m) => m._state.toJS()), //history
|
|
428
485
|
];
|
|
429
|
-
//add new question
|
|
430
|
-
messages.push({role: 'user', content: question});
|
|
431
486
|
|
|
432
487
|
let options;
|
|
433
488
|
if (this.state.options) {
|
|
@@ -435,67 +490,88 @@ class AiAgent extends Elf {
|
|
|
435
490
|
options = pState?.options;
|
|
436
491
|
}
|
|
437
492
|
|
|
438
|
-
if (
|
|
493
|
+
if (question) {
|
|
439
494
|
this.logic.chat(contextId, 'user', question);
|
|
440
|
-
|
|
441
|
-
|
|
495
|
+
//add new question
|
|
496
|
+
messages.push({role: 'user', content: question});
|
|
442
497
|
}
|
|
443
498
|
|
|
444
|
-
|
|
499
|
+
this.log.dbg(`🐈 Chat session started : ${contextId}`);
|
|
500
|
+
|
|
501
|
+
const chatCompletionRequest = {
|
|
445
502
|
model: this.state.model,
|
|
446
503
|
messages,
|
|
447
504
|
options,
|
|
448
505
|
format: this.state.format?.toJS(),
|
|
449
506
|
tools: this.state.tools?.toJS(),
|
|
450
|
-
|
|
507
|
+
reasoning: this.state.reasoning,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const result = await this._provider.chat(chatCompletionRequest);
|
|
451
511
|
|
|
452
512
|
if (this.state.toolServiceId && this.state.tools) {
|
|
453
513
|
const used = await this.callTools(
|
|
454
514
|
contextId,
|
|
455
515
|
result.message,
|
|
456
|
-
userDesktopId
|
|
457
|
-
sessionId
|
|
516
|
+
userDesktopId
|
|
458
517
|
);
|
|
459
518
|
if (used) {
|
|
460
|
-
|
|
519
|
+
//leave for a second turn
|
|
520
|
+
this.log.dbg(`🐈 Chat session ended : ${contextId}`);
|
|
521
|
+
return true;
|
|
461
522
|
}
|
|
462
523
|
}
|
|
463
524
|
|
|
464
525
|
//keep agent response
|
|
465
|
-
|
|
526
|
+
const {thinks, cleanText} = splitThinkTags(result.message.content);
|
|
527
|
+
this.logic.chat(contextId, 'assistant', cleanText);
|
|
528
|
+
for (const think of thinks) {
|
|
529
|
+
this.log.dbg(`🧐 ${think}`);
|
|
530
|
+
}
|
|
531
|
+
this.log.dbg(`🐈 Chat session ended : ${contextId}`);
|
|
466
532
|
|
|
467
|
-
return
|
|
533
|
+
return cleanText;
|
|
468
534
|
}
|
|
469
535
|
|
|
470
|
-
async callTools(contextId, message, userDesktopId
|
|
471
|
-
const {tool_calls} = message;
|
|
536
|
+
async callTools(contextId, message, userDesktopId) {
|
|
537
|
+
const {tool_calls, content} = message;
|
|
472
538
|
if (!tool_calls || !userDesktopId) {
|
|
473
539
|
return false;
|
|
474
540
|
}
|
|
475
|
-
this.logic.callTools(contextId, tool_calls);
|
|
541
|
+
this.logic.callTools(contextId, content, tool_calls);
|
|
476
542
|
for (const tool of tool_calls) {
|
|
477
543
|
const {
|
|
478
544
|
id,
|
|
479
545
|
function: {name},
|
|
480
546
|
} = tool;
|
|
547
|
+
|
|
481
548
|
let result = '';
|
|
549
|
+
let toolArguments = {};
|
|
550
|
+
if (
|
|
551
|
+
tool.function.arguments &&
|
|
552
|
+
typeof tool.function.arguments === 'string'
|
|
553
|
+
) {
|
|
554
|
+
toolArguments = JSON.parse(tool.function.arguments);
|
|
555
|
+
} else if (
|
|
556
|
+
tool.function.arguments &&
|
|
557
|
+
typeof tool.function.arguments === 'object'
|
|
558
|
+
) {
|
|
559
|
+
toolArguments = tool.function.arguments;
|
|
560
|
+
}
|
|
482
561
|
try {
|
|
483
562
|
const payload = {
|
|
484
563
|
id: this.state.toolServiceId,
|
|
485
564
|
desktopId: userDesktopId,
|
|
486
|
-
|
|
565
|
+
contextId,
|
|
566
|
+
...toolArguments,
|
|
487
567
|
};
|
|
488
568
|
const toolNamespace = this.state.toolServiceId?.split('@', 1)[0];
|
|
489
569
|
const toolCmd = `${toolNamespace}.${name}`;
|
|
490
|
-
this.log.dbg(
|
|
491
|
-
`🔧 Tool call: ${toolCmd}(${JSON.stringify(
|
|
492
|
-
payload
|
|
493
|
-
)}) sid: ${sessionId}`
|
|
494
|
-
);
|
|
570
|
+
this.log.dbg(`🔧 Tool call: ${toolCmd}(${JSON.stringify(payload)})`);
|
|
495
571
|
result = await this.quest.cmd(toolCmd, payload);
|
|
496
572
|
await this.addToolMessage(contextId, id, name, result);
|
|
497
573
|
} catch (err) {
|
|
498
|
-
this.log.dbg(`🔧 Tool error : ${err}
|
|
574
|
+
this.log.dbg(`🔧 Tool error : ${err}`);
|
|
499
575
|
}
|
|
500
576
|
}
|
|
501
577
|
return true;
|
|
@@ -527,6 +603,7 @@ class AiAgent extends Elf {
|
|
|
527
603
|
const response = await this._provider.chat({
|
|
528
604
|
model: this.state.model,
|
|
529
605
|
messages,
|
|
606
|
+
reasoning: this.state.reasoning,
|
|
530
607
|
options,
|
|
531
608
|
stream: true,
|
|
532
609
|
});
|
|
@@ -559,6 +636,7 @@ class AiAgent extends Elf {
|
|
|
559
636
|
model: this.state.model,
|
|
560
637
|
prompt,
|
|
561
638
|
system: resumePrompt,
|
|
639
|
+
reasoning: this.state.reasoning,
|
|
562
640
|
options,
|
|
563
641
|
stream: false,
|
|
564
642
|
});
|
|
@@ -584,6 +662,7 @@ class AiAgent extends Elf {
|
|
|
584
662
|
model: this.state.model,
|
|
585
663
|
messages,
|
|
586
664
|
options,
|
|
665
|
+
reasoning: this.state.reasoning,
|
|
587
666
|
format: {
|
|
588
667
|
type: 'object',
|
|
589
668
|
properties: {
|
|
@@ -683,20 +762,34 @@ class AiAgent extends Elf {
|
|
|
683
762
|
];
|
|
684
763
|
}
|
|
685
764
|
|
|
765
|
+
async getToolChatContextHistory(contextId) {
|
|
766
|
+
const contextMessages = this.state.messages[contextId] || [];
|
|
767
|
+
return [
|
|
768
|
+
...Array.from(contextMessages)
|
|
769
|
+
.map((m) => m._state.toJS())
|
|
770
|
+
.filter((m) => {
|
|
771
|
+
if (m.role === 'tool' && !m.tool_calls) {
|
|
772
|
+
return m;
|
|
773
|
+
}
|
|
774
|
+
}),
|
|
775
|
+
];
|
|
776
|
+
}
|
|
777
|
+
|
|
686
778
|
async getBaseSettings() {
|
|
687
779
|
const {provider, host, headers, model} = this.state.toJS();
|
|
688
780
|
return {provider, host, headers, model};
|
|
689
781
|
}
|
|
690
782
|
|
|
691
|
-
async getUsability() {
|
|
692
|
-
return this.state.usability;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
783
|
async trash() {
|
|
696
784
|
this.logic.trash();
|
|
697
785
|
await this.persist();
|
|
698
786
|
}
|
|
699
787
|
|
|
788
|
+
async publish() {
|
|
789
|
+
this.logic.publish();
|
|
790
|
+
await this.persist();
|
|
791
|
+
}
|
|
792
|
+
|
|
700
793
|
delete() {}
|
|
701
794
|
}
|
|
702
795
|
|
package/lib/llm/providers.js
CHANGED
|
@@ -49,7 +49,15 @@ class OllamaProvider extends LLMProvider {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async chat(chatRequest) {
|
|
52
|
-
const {
|
|
52
|
+
const {
|
|
53
|
+
model,
|
|
54
|
+
messages,
|
|
55
|
+
options,
|
|
56
|
+
format,
|
|
57
|
+
tools,
|
|
58
|
+
stream,
|
|
59
|
+
reasoning,
|
|
60
|
+
} = chatRequest;
|
|
53
61
|
const useFormat = !!format;
|
|
54
62
|
const chat = await this.#ollama.chat({
|
|
55
63
|
model,
|
|
@@ -58,6 +66,7 @@ class OllamaProvider extends LLMProvider {
|
|
|
58
66
|
format,
|
|
59
67
|
tools,
|
|
60
68
|
stream,
|
|
69
|
+
think: reasoning,
|
|
61
70
|
});
|
|
62
71
|
if (useFormat) {
|
|
63
72
|
chat.message.content = JSON.parse(chat.message.content);
|
|
@@ -68,7 +77,15 @@ class OllamaProvider extends LLMProvider {
|
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
async gen(genRequest) {
|
|
71
|
-
const {
|
|
80
|
+
const {
|
|
81
|
+
model,
|
|
82
|
+
system,
|
|
83
|
+
prompt,
|
|
84
|
+
format,
|
|
85
|
+
options,
|
|
86
|
+
stream,
|
|
87
|
+
reasoning,
|
|
88
|
+
} = genRequest;
|
|
72
89
|
const useFormat = !!format;
|
|
73
90
|
const gen = await this.#ollama.generate({
|
|
74
91
|
model,
|
|
@@ -77,6 +94,7 @@ class OllamaProvider extends LLMProvider {
|
|
|
77
94
|
format,
|
|
78
95
|
options,
|
|
79
96
|
stream,
|
|
97
|
+
think: reasoning,
|
|
80
98
|
});
|
|
81
99
|
if (useFormat) {
|
|
82
100
|
return JSON.parse(gen.response);
|
|
@@ -120,6 +138,7 @@ class OpenAIProvider extends LLMProvider {
|
|
|
120
138
|
}
|
|
121
139
|
|
|
122
140
|
async chat(chatRequest) {
|
|
141
|
+
delete chatRequest.reasoning; //not yet supported
|
|
123
142
|
if (chatRequest.options) {
|
|
124
143
|
chatRequest = {...chatRequest, ...chatRequest.options};
|
|
125
144
|
delete chatRequest.options;
|
|
@@ -145,6 +164,7 @@ class OpenAIProvider extends LLMProvider {
|
|
|
145
164
|
},
|
|
146
165
|
},
|
|
147
166
|
};
|
|
167
|
+
|
|
148
168
|
delete chatRequest.format;
|
|
149
169
|
}
|
|
150
170
|
const query = `${this.#baseUrl}/chat/completions`;
|
package/lib/llm/utils.js
CHANGED
|
@@ -90,7 +90,7 @@ const splitLongText = (text, maxLength, baseId) => {
|
|
|
90
90
|
return segments;
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
const html2chunks = async (html, chunkSize = 300, chunkOverlap =
|
|
93
|
+
const html2chunks = async (html, chunkSize = 300, chunkOverlap = 50) => {
|
|
94
94
|
const textSplitter = RecursiveCharacterTextSplitter.fromLanguage('html', {
|
|
95
95
|
chunkSize,
|
|
96
96
|
chunkOverlap,
|
|
@@ -237,44 +237,38 @@ function groupSentences(text, maxSize) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
function buildPromptFromArticlesMulti(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
articles,
|
|
241
|
+
maxTotalTokens,
|
|
242
|
+
dbg = () => null
|
|
243
243
|
) {
|
|
244
|
-
let prompt = '[CONTEXT]\n\n';
|
|
245
|
-
let currentTokenCount = estimateTokenCount(prompt);
|
|
246
|
-
|
|
247
244
|
const buffer = 16000; // Réserve de tokens pour l'instruction + réponse
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
for (const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
section += articleBlock;
|
|
265
|
-
sectionTokenCount += tokensForArticle;
|
|
266
|
-
}
|
|
245
|
+
const maxTokens = Math.floor(maxTotalTokens - buffer);
|
|
246
|
+
|
|
247
|
+
let sections = [];
|
|
248
|
+
let sectionTokenCount = 0;
|
|
249
|
+
for (const article of articles) {
|
|
250
|
+
const {kind, title, sourceId, distance, combinedScore, chunk} = article;
|
|
251
|
+
const articleBlock = JSON.stringify({
|
|
252
|
+
kind,
|
|
253
|
+
title,
|
|
254
|
+
url: sourceId.startsWith('http') ? sourceId : null,
|
|
255
|
+
distance,
|
|
256
|
+
combinedScore,
|
|
257
|
+
chunk,
|
|
258
|
+
});
|
|
259
|
+
dbg(articleBlock);
|
|
260
|
+
const tokensForArticle = estimateTokenCount(articleBlock);
|
|
267
261
|
|
|
268
|
-
if (
|
|
262
|
+
if (sectionTokenCount + tokensForArticle > maxTokens) {
|
|
263
|
+
dbg('MAX TOKEN REACHED');
|
|
269
264
|
break;
|
|
270
265
|
}
|
|
271
266
|
|
|
272
|
-
|
|
273
|
-
|
|
267
|
+
sections.push(articleBlock);
|
|
268
|
+
sectionTokenCount += tokensForArticle;
|
|
274
269
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
return prompt.trim();
|
|
270
|
+
|
|
271
|
+
return `[${sections.join(',')}]`;
|
|
278
272
|
}
|
|
279
273
|
|
|
280
274
|
/**
|
|
@@ -302,12 +296,17 @@ function chunkArray(array, size) {
|
|
|
302
296
|
* @returns {Promise<object>} A map of embeddings structured as:
|
|
303
297
|
* { [contentId]: { [key]: { chunk, embedding } } }
|
|
304
298
|
*/
|
|
305
|
-
async function embedChunksInBatch(
|
|
299
|
+
async function embedChunksInBatch(
|
|
300
|
+
agent,
|
|
301
|
+
allChunks,
|
|
302
|
+
batchSize = 50,
|
|
303
|
+
prefix = 'passage: '
|
|
304
|
+
) {
|
|
306
305
|
const embeddingsByContent = {};
|
|
307
306
|
|
|
308
307
|
for (let start = 0; start < allChunks.length; start += batchSize) {
|
|
309
308
|
const currentBatch = allChunks.slice(start, start + batchSize);
|
|
310
|
-
const inputTexts = currentBatch.map(({chunk}) => chunk);
|
|
309
|
+
const inputTexts = currentBatch.map(({chunk}) => `${prefix}${chunk}`);
|
|
311
310
|
const embeddedVectors = await agent.embedInBatch(inputTexts);
|
|
312
311
|
|
|
313
312
|
for (let i = 0; i < embeddedVectors.length; i++) {
|
|
@@ -328,6 +327,26 @@ async function embedChunksInBatch(agent, allChunks, batchSize = 50) {
|
|
|
328
327
|
return embeddingsByContent;
|
|
329
328
|
}
|
|
330
329
|
|
|
330
|
+
function splitThinkTags(input) {
|
|
331
|
+
const thinkRegex = /<think>([\s\S]*?)<\/think>/g;
|
|
332
|
+
let match;
|
|
333
|
+
const thinks = [];
|
|
334
|
+
let cleanText = input;
|
|
335
|
+
|
|
336
|
+
// Extraire toutes les sections <think>...</think>
|
|
337
|
+
while ((match = thinkRegex.exec(input)) !== null) {
|
|
338
|
+
thinks.push(match[1].trim());
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Supprimer les balises et leur contenu du texte
|
|
342
|
+
cleanText = cleanText.replace(thinkRegex, '').trim();
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
thinks, // Tableau avec le contenu des balises <think>
|
|
346
|
+
cleanText, // Texte sans balises <think>
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
|
|
331
350
|
module.exports = {
|
|
332
351
|
chunkArray,
|
|
333
352
|
embedChunksInBatch,
|
|
@@ -342,4 +361,5 @@ module.exports = {
|
|
|
342
361
|
sliceText,
|
|
343
362
|
groupSentences,
|
|
344
363
|
buildPromptFromArticlesMulti,
|
|
364
|
+
splitThinkTags,
|
|
345
365
|
};
|