agentic-api 2.0.314 → 2.0.585
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 +37 -34
- package/dist/src/agents/prompts.d.ts +1 -1
- package/dist/src/agents/prompts.js +9 -7
- package/dist/src/agents/reducer.core.js +2 -2
- package/dist/src/agents/simulator.d.ts +33 -4
- package/dist/src/agents/simulator.dashboard.d.ts +140 -0
- package/dist/src/agents/simulator.dashboard.js +344 -0
- package/dist/src/agents/simulator.executor.d.ts +9 -3
- package/dist/src/agents/simulator.executor.js +43 -17
- package/dist/src/agents/simulator.js +103 -19
- package/dist/src/agents/simulator.prompts.d.ts +9 -8
- package/dist/src/agents/simulator.prompts.js +68 -62
- package/dist/src/agents/simulator.types.d.ts +39 -4
- package/dist/src/agents/simulator.utils.d.ts +22 -1
- package/dist/src/agents/simulator.utils.js +27 -2
- package/dist/src/execute/helpers.d.ts +75 -0
- package/dist/src/execute/helpers.js +139 -0
- package/dist/src/execute/index.d.ts +11 -0
- package/dist/src/execute/index.js +44 -0
- package/dist/src/execute/legacy.d.ts +46 -0
- package/dist/src/{execute.js → execute/legacy.js} +130 -232
- package/dist/src/execute/modelconfig.d.ts +29 -0
- package/dist/src/execute/modelconfig.js +72 -0
- package/dist/src/execute/responses.d.ts +55 -0
- package/dist/src/execute/responses.js +595 -0
- package/dist/src/execute/shared.d.ts +83 -0
- package/dist/src/execute/shared.js +188 -0
- package/dist/src/index.d.ts +5 -1
- package/dist/src/index.js +21 -2
- package/dist/src/llm/config.d.ts +25 -0
- package/dist/src/llm/config.js +38 -0
- package/dist/src/llm/index.d.ts +48 -0
- package/dist/src/llm/index.js +115 -0
- package/dist/src/llm/openai.d.ts +6 -0
- package/dist/src/llm/openai.js +154 -0
- package/dist/src/llm/pricing.d.ts +26 -0
- package/dist/src/llm/pricing.js +129 -0
- package/dist/src/llm/xai.d.ts +17 -0
- package/dist/src/llm/xai.js +90 -0
- package/dist/src/pricing.llm.d.ts +3 -15
- package/dist/src/pricing.llm.js +10 -230
- package/dist/src/prompts.d.ts +0 -1
- package/dist/src/prompts.js +51 -118
- package/dist/src/rag/embeddings.d.ts +5 -1
- package/dist/src/rag/embeddings.js +23 -7
- package/dist/src/rag/parser.js +1 -1
- package/dist/src/rag/rag.manager.d.ts +33 -2
- package/dist/src/rag/rag.manager.js +159 -61
- package/dist/src/rag/types.d.ts +2 -0
- package/dist/src/rag/usecase.js +8 -11
- package/dist/src/rules/git/git.e2e.helper.js +21 -2
- package/dist/src/rules/git/git.health.d.ts +4 -2
- package/dist/src/rules/git/git.health.js +113 -16
- package/dist/src/rules/git/index.d.ts +1 -1
- package/dist/src/rules/git/index.js +3 -2
- package/dist/src/rules/git/repo.d.ts +57 -7
- package/dist/src/rules/git/repo.js +326 -39
- package/dist/src/rules/git/repo.pr.d.ts +8 -0
- package/dist/src/rules/git/repo.pr.js +161 -13
- package/dist/src/rules/git/repo.tools.d.ts +5 -1
- package/dist/src/rules/git/repo.tools.js +54 -7
- package/dist/src/rules/types.d.ts +25 -0
- package/dist/src/rules/utils.matter.d.ts +0 -20
- package/dist/src/rules/utils.matter.js +58 -81
- package/dist/src/scrapper.js +3 -2
- package/dist/src/stategraph/stategraph.d.ts +26 -1
- package/dist/src/stategraph/stategraph.js +43 -2
- package/dist/src/stategraph/stategraph.storage.js +4 -0
- package/dist/src/stategraph/types.d.ts +5 -0
- package/dist/src/types.d.ts +42 -7
- package/dist/src/types.js +8 -7
- package/dist/src/usecase.js +1 -1
- package/dist/src/utils.d.ts +0 -8
- package/dist/src/utils.js +26 -29
- package/package.json +9 -7
- package/dist/src/execute.d.ts +0 -63
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SimulatorDashboard = void 0;
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const promises_1 = require("fs/promises");
|
|
9
|
+
const readline_1 = require("readline");
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const simulator_1 = require("./simulator");
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// CLASS: SimulatorDashboard
|
|
14
|
+
// ============================================================================
|
|
15
|
+
class SimulatorDashboard {
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.simulator = null;
|
|
19
|
+
this.abortController = null;
|
|
20
|
+
this._currentOutputPath = null;
|
|
21
|
+
this._currentInputPath = null;
|
|
22
|
+
this.status = this.createInitialStatus();
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Chemin du fichier output actuel (ou du dernier run)
|
|
26
|
+
*/
|
|
27
|
+
get currentOutputPath() {
|
|
28
|
+
return this._currentOutputPath;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Chemin du fichier input actuel (ou du dernier run)
|
|
32
|
+
*/
|
|
33
|
+
get currentInputPath() {
|
|
34
|
+
return this._currentInputPath;
|
|
35
|
+
}
|
|
36
|
+
createInitialStatus() {
|
|
37
|
+
return {
|
|
38
|
+
isRunning: false,
|
|
39
|
+
sessionId: null,
|
|
40
|
+
currentTest: 0,
|
|
41
|
+
totalTests: 0,
|
|
42
|
+
passed: 0,
|
|
43
|
+
failed: 0,
|
|
44
|
+
errors: 0,
|
|
45
|
+
startTime: null,
|
|
46
|
+
lastUpdate: null
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Obtenir le status actuel (pour le contrôleur backend)
|
|
51
|
+
*/
|
|
52
|
+
getStatus() {
|
|
53
|
+
return { ...this.status };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Vérifier si une exécution est en cours
|
|
57
|
+
*/
|
|
58
|
+
isRunning() {
|
|
59
|
+
return this.status.isRunning;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Annuler l'exécution en cours
|
|
63
|
+
*/
|
|
64
|
+
abort() {
|
|
65
|
+
if (this.abortController) {
|
|
66
|
+
this.abortController.abort();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Charger un fichier JSON d'entrée
|
|
71
|
+
*/
|
|
72
|
+
async loadInputFile(filePath) {
|
|
73
|
+
const content = await (0, promises_1.readFile)(filePath, 'utf-8');
|
|
74
|
+
return JSON.parse(content);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Générer le chemin du fichier output basé sur le fichier input
|
|
78
|
+
* Exemple: tests/my-tests.json → tests/results.my-tests.jsonl
|
|
79
|
+
*/
|
|
80
|
+
createOutputPath(inputPath) {
|
|
81
|
+
const dir = path_1.default.dirname(inputPath);
|
|
82
|
+
const basename = path_1.default.basename(inputPath, path_1.default.extname(inputPath));
|
|
83
|
+
return path_1.default.join(dir, `results.${basename}.jsonl`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Charger les résultats JSONL existants (en cours ou terminés)
|
|
87
|
+
* Supporte le streaming partiel (fichier en cours d'écriture)
|
|
88
|
+
*
|
|
89
|
+
* @param inputPath - Chemin du fichier JSON d'entrée (génère automatiquement le output path)
|
|
90
|
+
* @returns Les lignes parsées ou null si le fichier n'existe pas
|
|
91
|
+
*/
|
|
92
|
+
async loadResults(inputPath) {
|
|
93
|
+
// Déterminer le chemin du fichier output
|
|
94
|
+
let outputPath = null;
|
|
95
|
+
if (inputPath) {
|
|
96
|
+
outputPath = this.createOutputPath(inputPath);
|
|
97
|
+
}
|
|
98
|
+
else if (this._currentInputPath) {
|
|
99
|
+
outputPath = this.createOutputPath(this._currentInputPath);
|
|
100
|
+
}
|
|
101
|
+
else if (this._currentOutputPath) {
|
|
102
|
+
outputPath = this._currentOutputPath;
|
|
103
|
+
}
|
|
104
|
+
if (!outputPath || !(0, fs_1.existsSync)(outputPath)) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
const lines = [];
|
|
109
|
+
const stream = (0, fs_1.createReadStream)(outputPath, { encoding: 'utf-8' });
|
|
110
|
+
const rl = (0, readline_1.createInterface)({ input: stream, crlfDelay: Infinity });
|
|
111
|
+
rl.on('line', (line) => {
|
|
112
|
+
if (line.trim()) {
|
|
113
|
+
try {
|
|
114
|
+
lines.push(JSON.parse(line));
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
// Ignorer les lignes mal formées (fichier en cours d'écriture)
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
rl.on('close', () => resolve(lines));
|
|
122
|
+
rl.on('error', reject);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Obtenir le résumé des résultats (dernière ligne type='end')
|
|
127
|
+
* @param inputPath - Chemin du fichier JSON d'entrée
|
|
128
|
+
*/
|
|
129
|
+
async getResultsSummary(inputPath) {
|
|
130
|
+
const results = await this.loadResults(inputPath);
|
|
131
|
+
if (!results)
|
|
132
|
+
return null;
|
|
133
|
+
const endLine = results.find(r => r.type === 'end');
|
|
134
|
+
return endLine?.summary || null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Vérifier si les résultats sont complets (contient une ligne 'end')
|
|
138
|
+
* @param inputPath - Chemin du fichier JSON d'entrée
|
|
139
|
+
*/
|
|
140
|
+
async isResultsComplete(inputPath) {
|
|
141
|
+
const results = await this.loadResults(inputPath);
|
|
142
|
+
if (!results)
|
|
143
|
+
return false;
|
|
144
|
+
return results.some(r => r.type === 'end');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Exécuter les tests et écrire les résultats en JSONL
|
|
148
|
+
*
|
|
149
|
+
* @param input - Données d'entrée (ou chemin vers fichier JSON)
|
|
150
|
+
* @param outputPath - Chemin du fichier JSONL de sortie
|
|
151
|
+
* @param onLine - Callback optionnel pour chaque ligne JSONL (streaming)
|
|
152
|
+
*/
|
|
153
|
+
async run(input, outputPath, onLine) {
|
|
154
|
+
// Stocker et charger l'input si c'est un chemin
|
|
155
|
+
const isInputFile = typeof input === 'string';
|
|
156
|
+
this._currentInputPath = isInputFile ? input : null;
|
|
157
|
+
const data = isInputFile
|
|
158
|
+
? await this.loadInputFile(input)
|
|
159
|
+
: input;
|
|
160
|
+
// Générer automatiquement le outputPath si input est un fichier
|
|
161
|
+
const resolvedOutputPath = outputPath ?? (isInputFile ? this.createOutputPath(input) : undefined);
|
|
162
|
+
this._currentOutputPath = resolvedOutputPath || null;
|
|
163
|
+
// Initialiser le status
|
|
164
|
+
const sessionId = `session-${Date.now()}`;
|
|
165
|
+
this.status = {
|
|
166
|
+
isRunning: true,
|
|
167
|
+
sessionId,
|
|
168
|
+
currentTest: 0,
|
|
169
|
+
totalTests: data.tests.length,
|
|
170
|
+
passed: 0,
|
|
171
|
+
failed: 0,
|
|
172
|
+
errors: 0,
|
|
173
|
+
startTime: new Date(),
|
|
174
|
+
lastUpdate: new Date()
|
|
175
|
+
};
|
|
176
|
+
// Créer le simulateur avec config mergée
|
|
177
|
+
const mergedConfig = {
|
|
178
|
+
...this.config,
|
|
179
|
+
...data.config
|
|
180
|
+
};
|
|
181
|
+
this.simulator = new simulator_1.AgentSimulator(mergedConfig);
|
|
182
|
+
// Créer l'AbortController
|
|
183
|
+
this.abortController = new AbortController();
|
|
184
|
+
// Ouvrir le fichier de sortie si spécifié
|
|
185
|
+
let writeStream = null;
|
|
186
|
+
if (resolvedOutputPath) {
|
|
187
|
+
writeStream = (0, fs_1.createWriteStream)(resolvedOutputPath, { encoding: 'utf-8' });
|
|
188
|
+
}
|
|
189
|
+
const results = [];
|
|
190
|
+
const startTime = Date.now();
|
|
191
|
+
// Helper pour écrire une ligne
|
|
192
|
+
const writeLine = (line) => {
|
|
193
|
+
results.push(line);
|
|
194
|
+
const jsonLine = JSON.stringify(line) + '\n';
|
|
195
|
+
if (writeStream) {
|
|
196
|
+
writeStream.write(jsonLine);
|
|
197
|
+
}
|
|
198
|
+
if (onLine) {
|
|
199
|
+
onLine(line);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
try {
|
|
203
|
+
// Écrire la ligne de début
|
|
204
|
+
writeLine({
|
|
205
|
+
type: 'start',
|
|
206
|
+
timestamp: new Date().toISOString(),
|
|
207
|
+
sessionId,
|
|
208
|
+
totalTests: data.tests.length
|
|
209
|
+
});
|
|
210
|
+
// Exécuter chaque test
|
|
211
|
+
for (let i = 0; i < data.tests.length; i++) {
|
|
212
|
+
// Vérifier si annulé
|
|
213
|
+
if (this.abortController.signal.aborted) {
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
const testCase = data.tests[i];
|
|
217
|
+
const testId = testCase.id || `test-${i}`;
|
|
218
|
+
const testStartTime = Date.now();
|
|
219
|
+
// Mettre à jour le status
|
|
220
|
+
this.status.currentTest = i + 1;
|
|
221
|
+
this.status.lastUpdate = new Date();
|
|
222
|
+
try {
|
|
223
|
+
// Exécuter le test
|
|
224
|
+
const result = await this.simulator.testCase(testCase.scenario, testCase.case);
|
|
225
|
+
// Déterminer le status
|
|
226
|
+
const status = result.success ? 'completed' : 'failed';
|
|
227
|
+
// Mettre à jour les compteurs
|
|
228
|
+
if (result.success) {
|
|
229
|
+
this.status.passed++;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
this.status.failed++;
|
|
233
|
+
}
|
|
234
|
+
// Écrire le résultat
|
|
235
|
+
writeLine({
|
|
236
|
+
type: 'result',
|
|
237
|
+
timestamp: new Date().toISOString(),
|
|
238
|
+
testId,
|
|
239
|
+
testIndex: i,
|
|
240
|
+
name: testCase.name || testId,
|
|
241
|
+
description: testCase.scenario.description || testCase.scenario.goals,
|
|
242
|
+
query: testCase.case.query,
|
|
243
|
+
status,
|
|
244
|
+
success: result.success,
|
|
245
|
+
message: result.message,
|
|
246
|
+
error: result.error || undefined,
|
|
247
|
+
exchangeCount: result.exchangeCount,
|
|
248
|
+
messages: result.messages,
|
|
249
|
+
duration: Date.now() - testStartTime
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
// Erreur d'exécution
|
|
254
|
+
this.status.errors++;
|
|
255
|
+
writeLine({
|
|
256
|
+
type: 'error',
|
|
257
|
+
timestamp: new Date().toISOString(),
|
|
258
|
+
testId,
|
|
259
|
+
testIndex: i,
|
|
260
|
+
name: testCase.name || testId,
|
|
261
|
+
description: testCase.scenario.description || testCase.scenario.goals,
|
|
262
|
+
query: testCase.case.query,
|
|
263
|
+
status: 'error',
|
|
264
|
+
success: false,
|
|
265
|
+
error: error.message || String(error),
|
|
266
|
+
duration: Date.now() - testStartTime
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Écrire la ligne de fin
|
|
271
|
+
writeLine({
|
|
272
|
+
type: 'end',
|
|
273
|
+
timestamp: new Date().toISOString(),
|
|
274
|
+
sessionId,
|
|
275
|
+
summary: {
|
|
276
|
+
total: data.tests.length,
|
|
277
|
+
passed: this.status.passed,
|
|
278
|
+
failed: this.status.failed,
|
|
279
|
+
errors: this.status.errors,
|
|
280
|
+
totalDuration: Date.now() - startTime
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
// Fermer le stream et attendre la fin de l'écriture
|
|
286
|
+
if (writeStream) {
|
|
287
|
+
await new Promise((resolve, reject) => {
|
|
288
|
+
writeStream.end(() => resolve());
|
|
289
|
+
writeStream.on('error', reject);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
// Reset le status
|
|
293
|
+
this.status.isRunning = false;
|
|
294
|
+
this.status.lastUpdate = new Date();
|
|
295
|
+
this.simulator = null;
|
|
296
|
+
this.abortController = null;
|
|
297
|
+
}
|
|
298
|
+
return results;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Exécuter avec callback de streaming (pour SSE/WebSocket)
|
|
302
|
+
*/
|
|
303
|
+
async runWithStream(input, onLine) {
|
|
304
|
+
return this.run(input, undefined, onLine);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Créer un fichier JSON d'exemple pour les tests
|
|
308
|
+
*/
|
|
309
|
+
static createExampleInput() {
|
|
310
|
+
return {
|
|
311
|
+
name: "Example Test Suite",
|
|
312
|
+
description: "Exemple de suite de tests pour le simulator",
|
|
313
|
+
tests: [
|
|
314
|
+
{
|
|
315
|
+
id: "test-greeting",
|
|
316
|
+
name: "Test de salutation",
|
|
317
|
+
scenario: {
|
|
318
|
+
goals: "L'agent doit répondre poliment à la salutation",
|
|
319
|
+
persona: "Utilisateur poli et patient"
|
|
320
|
+
},
|
|
321
|
+
case: {
|
|
322
|
+
query: "Bonjour, comment allez-vous?"
|
|
323
|
+
// maxExchanges: 1 par défaut
|
|
324
|
+
// expectedTools: {} par défaut
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
id: "test-transfer",
|
|
329
|
+
name: "Test de transfert",
|
|
330
|
+
scenario: {
|
|
331
|
+
goals: "L'agent doit transférer vers l'agent spécialisé",
|
|
332
|
+
persona: "Utilisateur avec une demande spécifique"
|
|
333
|
+
},
|
|
334
|
+
case: {
|
|
335
|
+
query: "J'ai besoin d'aide spécialisée",
|
|
336
|
+
maxExchanges: 3,
|
|
337
|
+
expectedTools: { 'transferAgents': { gte: 1 } }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
]
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
exports.SimulatorDashboard = SimulatorDashboard;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { SimulatorConfig, ExecutionContext } from './simulator.types';
|
|
1
|
+
import { SimulatorConfig, ExecutionContext, SimulationScenario } from './simulator.types';
|
|
2
2
|
export declare class AgentExecutor {
|
|
3
3
|
private config;
|
|
4
|
-
private
|
|
4
|
+
private simulatorAgentBase;
|
|
5
5
|
private mockCacheInitializer?;
|
|
6
6
|
private ragManager?;
|
|
7
7
|
constructor(config: SimulatorConfig & {
|
|
@@ -9,8 +9,9 @@ export declare class AgentExecutor {
|
|
|
9
9
|
});
|
|
10
10
|
/**
|
|
11
11
|
* Initialiser les contextes agent et simulateur
|
|
12
|
+
* @param scenario Le scenario de simulation complet avec persona, goals, result
|
|
12
13
|
*/
|
|
13
|
-
initializeContexts(): Promise<ExecutionContext>;
|
|
14
|
+
initializeContexts(scenario: SimulationScenario): Promise<ExecutionContext>;
|
|
14
15
|
/**
|
|
15
16
|
* ✅ Exécuter l'agent testé et retourner sa réponse (extraction de agent-vs-agent.ts)
|
|
16
17
|
*/
|
|
@@ -19,6 +20,11 @@ export declare class AgentExecutor {
|
|
|
19
20
|
* ✅ Exécuter le simulateur et retourner sa réponse (extraction de client-simulator.ts)
|
|
20
21
|
*/
|
|
21
22
|
executeSimulator(context: ExecutionContext, query: string): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* ✅ Construire l'injection de contexte technique pour le simulateur
|
|
25
|
+
* Permet au simulateur de connaître les tools utilisés par l'agent testé
|
|
26
|
+
*/
|
|
27
|
+
private buildAgentContextInjection;
|
|
22
28
|
/**
|
|
23
29
|
* ✅ Récupérer le dernier message de l'agent testé (extraction de agent-vs-agent.ts ligne 168-191)
|
|
24
30
|
*/
|
|
@@ -8,18 +8,13 @@ const simulator_prompts_1 = require("./simulator.prompts");
|
|
|
8
8
|
class AgentExecutor {
|
|
9
9
|
constructor(config) {
|
|
10
10
|
this.config = config;
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
(config.instructionEx ? `\n\n${config.instructionEx}` : '') +
|
|
14
|
-
'\n\n**CRITICAL**: Your response after [DONE] must be valid JSON format only.';
|
|
15
|
-
// console.log('---- DBG simulator fullInstructions',fullInstructions);
|
|
16
|
-
// Agent simulateur simple pour éviter les dépendances complexes du ClientSimulator
|
|
17
|
-
this.simulatorAgent = {
|
|
11
|
+
// Configuration de base du simulateur (instructions seront construites dynamiquement avec le scenario)
|
|
12
|
+
this.simulatorAgentBase = {
|
|
18
13
|
name: "simple-simulator",
|
|
19
|
-
model: ("
|
|
14
|
+
model: ("HIGH-fast"),
|
|
20
15
|
publicDescription: "Simulateur simple pour tests - TOUJOURS retourner JSON valide",
|
|
21
16
|
cancelMemory: true,
|
|
22
|
-
instructions:
|
|
17
|
+
instructions: '', // Sera construit dans initializeContexts avec GENERIC_SIMULATOR_PROMPT
|
|
23
18
|
tools: [],
|
|
24
19
|
downstreamAgents: []
|
|
25
20
|
};
|
|
@@ -42,8 +37,9 @@ class AgentExecutor {
|
|
|
42
37
|
}
|
|
43
38
|
/**
|
|
44
39
|
* Initialiser les contextes agent et simulateur
|
|
40
|
+
* @param scenario Le scenario de simulation complet avec persona, goals, result
|
|
45
41
|
*/
|
|
46
|
-
async initializeContexts() {
|
|
42
|
+
async initializeContexts(scenario) {
|
|
47
43
|
const sessionId = `simulation-${Date.now()}`;
|
|
48
44
|
const agentContext = {
|
|
49
45
|
user: { id: `test-user`, role: 'user', uid: `test-${sessionId}` },
|
|
@@ -63,9 +59,17 @@ class AgentExecutor {
|
|
|
63
59
|
if (this.mockCacheInitializer) {
|
|
64
60
|
await this.mockCacheInitializer(agentContext.session.id);
|
|
65
61
|
}
|
|
62
|
+
// ✅ Construire les instructions complètes avec le scénario intégré via la fonction
|
|
63
|
+
const fullInstructions = (0, simulator_prompts_1.GENERIC_SIMULATOR_PROMPT)(scenario, this.config.instructionEx);
|
|
64
|
+
if (this.config.verbose)
|
|
65
|
+
console.log('---- DBG fullInstructions', fullInstructions);
|
|
66
66
|
return {
|
|
67
67
|
agentContext,
|
|
68
68
|
simulatorContext,
|
|
69
|
+
simulatorAgent: {
|
|
70
|
+
...this.simulatorAgentBase,
|
|
71
|
+
instructions: fullInstructions
|
|
72
|
+
},
|
|
69
73
|
conversationHistory: [],
|
|
70
74
|
exchangeCount: 0,
|
|
71
75
|
lastExecution: {
|
|
@@ -73,8 +77,8 @@ class AgentExecutor {
|
|
|
73
77
|
startQuery: '',
|
|
74
78
|
actions: [],
|
|
75
79
|
lastMessage: '',
|
|
76
|
-
usage: { prompt: 0, completion: 0, total: 0, cost: 0 }
|
|
77
|
-
moreThinkin
|
|
80
|
+
usage: { prompt: 0, completion: 0, total: 0, cost: 0 }
|
|
81
|
+
// moreThinkin removed (obsolete)
|
|
78
82
|
}
|
|
79
83
|
};
|
|
80
84
|
}
|
|
@@ -111,12 +115,15 @@ class AgentExecutor {
|
|
|
111
115
|
* ✅ Exécuter le simulateur et retourner sa réponse (extraction de client-simulator.ts)
|
|
112
116
|
*/
|
|
113
117
|
async executeSimulator(context, query) {
|
|
114
|
-
// Utiliser le simulateur
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
|
|
118
|
+
// Utiliser le simulateur avec les instructions complètes (incluant le scénario)
|
|
119
|
+
// ✅ Injecter le contexte technique si disponible
|
|
120
|
+
const agentContextInjection = this.buildAgentContextInjection(context);
|
|
121
|
+
const enrichedQuery = agentContextInjection ? `${query}\n\n${agentContextInjection}` : query;
|
|
122
|
+
// console.log('DEBUG: Exécution du simulateur avec query:\n', enrichedQuery);
|
|
123
|
+
await (0, execute_1.executeAgentSet)([context.simulatorAgent], context.simulatorContext, {
|
|
124
|
+
query: enrichedQuery,
|
|
118
125
|
home: 'simple-simulator',
|
|
119
|
-
stdout:
|
|
126
|
+
stdout: execute_1.DummyWritable,
|
|
120
127
|
verbose: false,
|
|
121
128
|
debug: false
|
|
122
129
|
});
|
|
@@ -124,6 +131,25 @@ class AgentExecutor {
|
|
|
124
131
|
context.conversationHistory.push(`Simulator: ${response}`);
|
|
125
132
|
return response;
|
|
126
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* ✅ Construire l'injection de contexte technique pour le simulateur
|
|
136
|
+
* Permet au simulateur de connaître les tools utilisés par l'agent testé
|
|
137
|
+
*/
|
|
138
|
+
buildAgentContextInjection(context) {
|
|
139
|
+
if (!context.lastExecution || !context.lastExecution.actions || context.lastExecution.actions.length === 0) {
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
// Extraire les noms des tools appelés (sans les arguments)
|
|
143
|
+
const tools = context.lastExecution.actions.map(action => action.action);
|
|
144
|
+
if (tools.length === 0) {
|
|
145
|
+
return '';
|
|
146
|
+
}
|
|
147
|
+
const contextData = {
|
|
148
|
+
tools: tools,
|
|
149
|
+
exchangeCount: context.exchangeCount
|
|
150
|
+
};
|
|
151
|
+
return `<agent-context>\n${JSON.stringify(contextData, null, 2)}\n</agent-context>`;
|
|
152
|
+
}
|
|
127
153
|
/**
|
|
128
154
|
* ✅ Récupérer le dernier message de l'agent testé (extraction de agent-vs-agent.ts ligne 168-191)
|
|
129
155
|
*/
|
|
@@ -2,35 +2,94 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AgentSimulator = void 0;
|
|
4
4
|
const simulator_executor_1 = require("./simulator.executor");
|
|
5
|
-
const simulator_prompts_1 = require("./simulator.prompts");
|
|
6
5
|
class AgentSimulator {
|
|
7
6
|
constructor(config) {
|
|
8
7
|
this.config = config;
|
|
9
8
|
this.executor = new simulator_executor_1.AgentExecutor(config);
|
|
10
9
|
}
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// NOUVELLE API : testCase(scenario, case)
|
|
12
|
+
// ============================================================================
|
|
11
13
|
/**
|
|
14
|
+
* Exécuter un cas de test avec scénario et paramètres séparés
|
|
15
|
+
*
|
|
16
|
+
* @param scenario - Contexte stable (goals, persona, result)
|
|
17
|
+
* @param testCase - Paramètres du test (query, maxExchanges, model, expectedTools)
|
|
18
|
+
* @returns SimulationResult
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const scenario = {
|
|
23
|
+
* goals: 'Obtenir le nombre secret 1942',
|
|
24
|
+
* persona: PERSONA_PATIENT,
|
|
25
|
+
* result: '{"success": boolean, "error": string}'
|
|
26
|
+
* };
|
|
27
|
+
*
|
|
28
|
+
* const result = await simulator.testCase(scenario, {
|
|
29
|
+
* query: 'À quel nombre penses-tu?',
|
|
30
|
+
* maxExchanges: 3, // défaut: 1 (oneshot)
|
|
31
|
+
* expectedTools: { 'transferAgents': { equal: 1 } } // défaut: {}
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
async testCase(scenario, testCase) {
|
|
36
|
+
// Appliquer les défauts
|
|
37
|
+
const resolvedCase = {
|
|
38
|
+
query: testCase.query,
|
|
39
|
+
maxExchanges: testCase.maxExchanges ?? 1, // Défaut: 1 (oneshot)
|
|
40
|
+
expectedTools: testCase.expectedTools ?? {}, // Défaut: pas de validation
|
|
41
|
+
model: testCase.model,
|
|
42
|
+
onMessage: testCase.onMessage
|
|
43
|
+
};
|
|
44
|
+
// TODO: Support model override (Phase 2)
|
|
45
|
+
// if (resolvedCase.model) {
|
|
46
|
+
// this.overrideModel(resolvedCase.model);
|
|
47
|
+
// }
|
|
48
|
+
// Convertir TestScenario vers SimulationScenario et déléguer
|
|
49
|
+
return this.executeSimulation({
|
|
50
|
+
scenario: {
|
|
51
|
+
goals: scenario.goals,
|
|
52
|
+
persona: scenario.persona,
|
|
53
|
+
result: scenario.result || '{"success": boolean, "explain": string, "error": string}'
|
|
54
|
+
},
|
|
55
|
+
query: resolvedCase.query,
|
|
56
|
+
maxExchanges: resolvedCase.maxExchanges,
|
|
57
|
+
expectedTool: resolvedCase.expectedTools,
|
|
58
|
+
onMessage: resolvedCase.onMessage
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// ============================================================================
|
|
62
|
+
// ANCIENNE API : executeSimulation(options) - deprecated
|
|
63
|
+
// ============================================================================
|
|
64
|
+
/**
|
|
65
|
+
* @deprecated Utiliser testCase(scenario, case) à la place
|
|
66
|
+
*
|
|
12
67
|
* Exécuter la simulation complète
|
|
13
68
|
*
|
|
69
|
+
* Architecture :
|
|
70
|
+
* - Le scénario (Personnalité, Question, Objectifs, Format JSON) est injecté UNE SEULE FOIS
|
|
71
|
+
* dans les instructions du simulateur au moment de l'initialisation (AVANT la boucle).
|
|
72
|
+
*
|
|
14
73
|
* Format de la query passée à l'agent testé :
|
|
15
|
-
* - Message initial :
|
|
74
|
+
* - Message initial : query fournie par l'utilisateur
|
|
16
75
|
* - Messages suivants : réponse conversationnelle du simulateur (sans tags d'évaluation)
|
|
17
76
|
*
|
|
18
77
|
* Format de la query passée au simulateur :
|
|
19
|
-
* -
|
|
20
|
-
* -
|
|
78
|
+
* - Instructions système : scénario complet intégré via GENERIC_SIMULATOR_PROMPT
|
|
79
|
+
* - Tous les messages : réponse directe de l'agent testé (agentResponse)
|
|
21
80
|
*/
|
|
22
81
|
async executeSimulation(options) {
|
|
23
|
-
|
|
82
|
+
// ✅ Initialiser les contextes avec le scenario complet
|
|
83
|
+
const context = await this.executor.initializeContexts(options.scenario);
|
|
24
84
|
const allMessages = [];
|
|
25
85
|
let lastAgentMessage = '';
|
|
26
86
|
let exchangeCounter = 0; // Compteur d'échanges (user+assistant)
|
|
27
87
|
try {
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// Stocker le message initial du simulateur
|
|
88
|
+
// ✅ Pour le premier message, utiliser directement la query fournie
|
|
89
|
+
// Le simulateur ne doit PAS reformuler la question initiale
|
|
90
|
+
const initialQuery = options.query || options.scenario.testQuery || '';
|
|
91
|
+
let currentUserQuery = initialQuery;
|
|
92
|
+
// Stocker le message initial (query brute de l'utilisateur)
|
|
34
93
|
const initialMsg = { content: currentUserQuery, role: 'user' };
|
|
35
94
|
allMessages.push(initialMsg);
|
|
36
95
|
if (options.onMessage) {
|
|
@@ -39,6 +98,7 @@ class AgentSimulator {
|
|
|
39
98
|
// Boucle de conversation - maxExchanges = nombre de paires (user+assistant)
|
|
40
99
|
while (exchangeCounter < options.maxExchanges) {
|
|
41
100
|
// Agent testé répond et retourne sa réponse
|
|
101
|
+
// La première fois la query est options.query, les fois suivantes c'est la réponse conversationnelle du simulateur
|
|
42
102
|
const agentResponse = await this.executor.executeAgent(context, currentUserQuery);
|
|
43
103
|
lastAgentMessage = agentResponse;
|
|
44
104
|
// Stocker la réponse de l'agent
|
|
@@ -49,9 +109,9 @@ class AgentSimulator {
|
|
|
49
109
|
}
|
|
50
110
|
// Incrémenter le compteur après la réponse de l'agent (assistant)
|
|
51
111
|
exchangeCounter++;
|
|
52
|
-
//
|
|
112
|
+
// ✅ Passer directement agentResponse au simulateur (scénario déjà dans les instructions)
|
|
53
113
|
const simulatorResult = await this.executor.executeSimulator(context, agentResponse);
|
|
54
|
-
// console.log(
|
|
114
|
+
// console.log('---- DBG simulatorResult',simulatorResult);
|
|
55
115
|
// Vérifier si terminé
|
|
56
116
|
if (this.isSimulationComplete(simulatorResult)) {
|
|
57
117
|
const expectedFormat = options.scenario.result || options.scenario.testResult || '{"success": boolean, "error": string}';
|
|
@@ -150,8 +210,8 @@ class AgentSimulator {
|
|
|
150
210
|
startQuery: result.execution?.startQuery,
|
|
151
211
|
actions: result.execution?.actions?.map((elem) => elem.action) || [],
|
|
152
212
|
lastMessage: result.execution?.lastMessage,
|
|
153
|
-
usage: result.execution?.usage
|
|
154
|
-
moreThinkin
|
|
213
|
+
usage: result.execution?.usage
|
|
214
|
+
// moreThinkin removed (obsolete)
|
|
155
215
|
};
|
|
156
216
|
const execution = {
|
|
157
217
|
...result.execution,
|
|
@@ -224,9 +284,24 @@ class AgentSimulator {
|
|
|
224
284
|
};
|
|
225
285
|
}
|
|
226
286
|
extractConversationalPart(response) {
|
|
227
|
-
// Extraire la partie conversationnelle avant les tags d'évaluation
|
|
228
|
-
|
|
229
|
-
|
|
287
|
+
// Extraire la partie conversationnelle avant les tags d'évaluation ou d'observation
|
|
288
|
+
// Filtrer tous les tags système : [DONE], [OBSERVATEUR SILENCIEUX], [À NOTER], etc.
|
|
289
|
+
const tagIndex = response.search(/\[(DONE|SIMULATION_COMPLETE|TERMINE|BUG_|OBSERVATEUR|À NOTER|NOTE|ANALYSE)/i);
|
|
290
|
+
if (tagIndex !== -1) {
|
|
291
|
+
return response.substring(0, tagIndex).trim();
|
|
292
|
+
}
|
|
293
|
+
// Filtrer aussi les lignes qui commencent par "Agent :" ou "[...]" (méta-commentaires)
|
|
294
|
+
const lines = response.split('\n');
|
|
295
|
+
const conversationalLines = lines.filter(line => {
|
|
296
|
+
const trimmed = line.trim();
|
|
297
|
+
// Exclure les lignes qui sont des méta-commentaires
|
|
298
|
+
if (trimmed.startsWith('Agent :') ||
|
|
299
|
+
trimmed.startsWith('[') && trimmed.includes(']') && !trimmed.match(/^\[[^\]]{1,3}\]/)) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
return true;
|
|
303
|
+
});
|
|
304
|
+
return conversationalLines.join('\n').trim();
|
|
230
305
|
}
|
|
231
306
|
/**
|
|
232
307
|
* Générer le rapport final en cas de timeout
|
|
@@ -250,8 +325,17 @@ class AgentSimulator {
|
|
|
250
325
|
const errors = [];
|
|
251
326
|
for (const [toolName, constraint] of Object.entries(expected)) {
|
|
252
327
|
const { count } = this.executionActionCount(toolName);
|
|
328
|
+
// Validation equal (égalité exacte)
|
|
253
329
|
if (constraint.equal !== undefined && count !== constraint.equal) {
|
|
254
|
-
errors.push(`Tool '${toolName}': expected ${constraint.equal}, got ${count}`);
|
|
330
|
+
errors.push(`Tool '${toolName}': expected equal to ${constraint.equal}, got ${count}`);
|
|
331
|
+
}
|
|
332
|
+
// Validation gte (greater than or equal - supérieur ou égal)
|
|
333
|
+
if (constraint.gte !== undefined && count < constraint.gte) {
|
|
334
|
+
errors.push(`Tool '${toolName}': expected >= ${constraint.gte}, got ${count}`);
|
|
335
|
+
}
|
|
336
|
+
// Validation lte (less than or equal - inférieur ou égal)
|
|
337
|
+
if (constraint.lte !== undefined && count > constraint.lte) {
|
|
338
|
+
errors.push(`Tool '${toolName}': expected <= ${constraint.lte}, got ${count}`);
|
|
255
339
|
}
|
|
256
340
|
}
|
|
257
341
|
return {
|