agentic-api 2.0.491 → 2.0.592
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/job.runner.d.ts +130 -0
- package/dist/src/agents/job.runner.js +339 -0
- package/dist/src/agents/reducer.core.d.ts +11 -1
- package/dist/src/agents/reducer.core.js +76 -86
- package/dist/src/agents/reducer.d.ts +1 -0
- package/dist/src/agents/reducer.factory.d.ts +46 -0
- package/dist/src/agents/reducer.factory.js +154 -0
- package/dist/src/agents/reducer.js +1 -0
- package/dist/src/agents/simulator.d.ts +26 -1
- package/dist/src/agents/simulator.dashboard.d.ts +140 -0
- package/dist/src/agents/simulator.dashboard.js +344 -0
- package/dist/src/agents/simulator.js +56 -0
- package/dist/src/agents/simulator.types.d.ts +38 -6
- package/dist/src/agents/simulator.utils.d.ts +22 -1
- package/dist/src/agents/simulator.utils.js +27 -0
- package/dist/src/execute/helpers.js +2 -2
- package/dist/src/execute/modelconfig.d.ts +21 -11
- package/dist/src/execute/modelconfig.js +29 -13
- package/dist/src/execute/responses.js +8 -7
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +21 -1
- 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 -251
- 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 +15 -5
- package/dist/src/rag/parser.js +1 -1
- package/dist/src/rag/rag.manager.d.ts +44 -6
- package/dist/src/rag/rag.manager.js +138 -49
- package/dist/src/rag/types.d.ts +2 -0
- package/dist/src/rag/usecase.js +8 -11
- package/dist/src/rules/git/git.health.js +59 -4
- package/dist/src/rules/git/repo.d.ts +11 -4
- package/dist/src/rules/git/repo.js +64 -18
- package/dist/src/rules/git/repo.pr.d.ts +8 -0
- package/dist/src/rules/git/repo.pr.js +45 -1
- 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 +14 -0
- package/dist/src/rules/utils.matter.d.ts +0 -20
- package/dist/src/rules/utils.matter.js +42 -74
- package/dist/src/scrapper.js +2 -2
- package/dist/src/utils.d.ts +0 -8
- package/dist/src/utils.js +1 -28
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -7,7 +7,63 @@ class AgentSimulator {
|
|
|
7
7
|
this.config = config;
|
|
8
8
|
this.executor = new simulator_executor_1.AgentExecutor(config);
|
|
9
9
|
}
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// NOUVELLE API : testCase(scenario, case)
|
|
12
|
+
// ============================================================================
|
|
10
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
|
+
*
|
|
11
67
|
* Exécuter la simulation complète
|
|
12
68
|
*
|
|
13
69
|
* Architecture :
|
|
@@ -9,6 +9,38 @@ export interface SimulatorConfig {
|
|
|
9
9
|
mockCacheInitializer?: (sessionId: string) => Promise<void>;
|
|
10
10
|
ragConfig?: RAGManagerConfig;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Scénario de test - Contexte stable entre les tests
|
|
14
|
+
* Définit la personnalité du simulateur et les objectifs de validation
|
|
15
|
+
*/
|
|
16
|
+
export interface TestScenario {
|
|
17
|
+
description?: string;
|
|
18
|
+
goals: string;
|
|
19
|
+
persona: string;
|
|
20
|
+
result?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Contrainte de validation pour un outil
|
|
24
|
+
*/
|
|
25
|
+
export interface ToolConstraint {
|
|
26
|
+
equal?: number;
|
|
27
|
+
gte?: number;
|
|
28
|
+
lte?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Cas de test spécifique - Varie pour chaque test
|
|
32
|
+
* Définit la query et les attentes de validation
|
|
33
|
+
*/
|
|
34
|
+
export interface TestCaseInput {
|
|
35
|
+
query: string;
|
|
36
|
+
maxExchanges?: number;
|
|
37
|
+
model?: string;
|
|
38
|
+
expectedTools?: Record<string, ToolConstraint>;
|
|
39
|
+
onMessage?: (message: AgentMessage) => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated Utiliser TestScenario à la place
|
|
43
|
+
*/
|
|
12
44
|
export interface SimulationScenario {
|
|
13
45
|
goals?: string;
|
|
14
46
|
persona?: string;
|
|
@@ -20,15 +52,14 @@ export interface SimulationScenario {
|
|
|
20
52
|
testQuery?: string;
|
|
21
53
|
testResult?: string;
|
|
22
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* @deprecated Utiliser testCase(scenario, case) à la place de executeSimulation(options)
|
|
57
|
+
*/
|
|
23
58
|
export interface SimulationOptions {
|
|
24
59
|
scenario: SimulationScenario;
|
|
25
60
|
query?: string;
|
|
26
61
|
maxExchanges: number;
|
|
27
|
-
expectedTool?: Record<string,
|
|
28
|
-
equal?: number;
|
|
29
|
-
gte?: number;
|
|
30
|
-
lte?: number;
|
|
31
|
-
}>;
|
|
62
|
+
expectedTool?: Record<string, ToolConstraint>;
|
|
32
63
|
onMessage?: (message: AgentMessage) => void;
|
|
33
64
|
}
|
|
34
65
|
export interface SimulationResult {
|
|
@@ -51,7 +82,8 @@ export interface ExecutionContext {
|
|
|
51
82
|
exchangeCount: number;
|
|
52
83
|
lastExecution: ExecutionResult;
|
|
53
84
|
}
|
|
54
|
-
|
|
85
|
+
/** @deprecated Utiliser TestScenario avec l'API testCase() */
|
|
86
|
+
export interface PRSolverScenario {
|
|
55
87
|
ticketId: string;
|
|
56
88
|
ticketMarkdown: string;
|
|
57
89
|
clientType: 'locataire' | 'proprietaire';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SimulationScenario } from './simulator.types';
|
|
1
|
+
import { SimulationScenario, TestScenario } from './simulator.types';
|
|
2
2
|
import { PERSONA_PATIENT, PERSONA_PRESSE, PERSONA_ENERVE } from './simulator.prompts';
|
|
3
3
|
/**
|
|
4
4
|
* Charger un ticket depuis le filesystem (comme agent-vs-agent.ts)
|
|
@@ -22,4 +22,25 @@ custom?: Partial<SimulationScenario>): {
|
|
|
22
22
|
* Supporte l'ancien format (testGoals, testEnd, etc.) pour rétrocompatibilité
|
|
23
23
|
*/
|
|
24
24
|
export declare function buildGenericScenario(scenario: SimulationScenario): SimulationScenario;
|
|
25
|
+
/**
|
|
26
|
+
* Créer un TestScenario pour la nouvelle API testCase()
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const scenario = createTestScenario({
|
|
31
|
+
* goals: 'Obtenir le nombre secret 1942',
|
|
32
|
+
* persona: PERSONA_PATIENT
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* const result = await simulator.testCase(scenario, {
|
|
36
|
+
* query: 'À quel nombre penses-tu?'
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function createTestScenario(options: {
|
|
41
|
+
description?: string;
|
|
42
|
+
goals: string;
|
|
43
|
+
persona: string;
|
|
44
|
+
result?: string;
|
|
45
|
+
}): TestScenario;
|
|
25
46
|
export { PERSONA_PATIENT, PERSONA_PRESSE, PERSONA_ENERVE };
|
|
@@ -37,6 +37,7 @@ exports.PERSONA_ENERVE = exports.PERSONA_PRESSE = exports.PERSONA_PATIENT = void
|
|
|
37
37
|
exports.loadScenario = loadScenario;
|
|
38
38
|
exports.buildScenarioFromTicket = buildScenarioFromTicket;
|
|
39
39
|
exports.buildGenericScenario = buildGenericScenario;
|
|
40
|
+
exports.createTestScenario = createTestScenario;
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
42
43
|
const simulator_prompts_1 = require("./simulator.prompts");
|
|
@@ -105,3 +106,29 @@ function buildGenericScenario(scenario) {
|
|
|
105
106
|
result: scenario.result || '{"success": boolean, "error": string, "description": string}'
|
|
106
107
|
};
|
|
107
108
|
}
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// NOUVELLE API : createTestScenario (pour testCase)
|
|
111
|
+
// ============================================================================
|
|
112
|
+
/**
|
|
113
|
+
* Créer un TestScenario pour la nouvelle API testCase()
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const scenario = createTestScenario({
|
|
118
|
+
* goals: 'Obtenir le nombre secret 1942',
|
|
119
|
+
* persona: PERSONA_PATIENT
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* const result = await simulator.testCase(scenario, {
|
|
123
|
+
* query: 'À quel nombre penses-tu?'
|
|
124
|
+
* });
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
function createTestScenario(options) {
|
|
128
|
+
return {
|
|
129
|
+
description: options.description,
|
|
130
|
+
goals: options.goals,
|
|
131
|
+
persona: options.persona,
|
|
132
|
+
result: options.result || '{"success": boolean, "explain": string, "error": string}'
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.accumulateUsageTokens = accumulateUsageTokens;
|
|
4
4
|
exports.stepsToActions = stepsToActions;
|
|
5
5
|
exports.batchProcessToolCalls = batchProcessToolCalls;
|
|
6
|
-
const
|
|
6
|
+
const pricing_1 = require("../llm/pricing");
|
|
7
7
|
const utils_1 = require("../utils");
|
|
8
8
|
/**
|
|
9
9
|
* OPTIM: Accumule l'usage des tokens et met à jour stateGraph + discussion.usage
|
|
@@ -18,7 +18,7 @@ const utils_1 = require("../utils");
|
|
|
18
18
|
* @param usage - Usage retourné par l'API (prompt_tokens, completion_tokens, total_tokens)
|
|
19
19
|
*/
|
|
20
20
|
function accumulateUsageTokens(stateGraph, discussion, agentName, model, usage) {
|
|
21
|
-
(0,
|
|
21
|
+
(0, pricing_1.accumulateCost)(discussion.usage, model, usage);
|
|
22
22
|
stateGraph.updateTokens(agentName, {
|
|
23
23
|
prompt: usage?.prompt_tokens || 0,
|
|
24
24
|
completion: usage?.completion_tokens || 0,
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import { AgentModel } from '../types';
|
|
2
|
+
import { LLMProvider } from '../llm';
|
|
2
3
|
/**
|
|
3
4
|
* Configuration des modèles pour Chat Completions (legacy) et Responses API
|
|
4
5
|
*
|
|
5
6
|
* Gère la configuration des modèles avec migration automatique des paramètres
|
|
6
|
-
*
|
|
7
|
+
* entre les deux APIs.
|
|
7
8
|
*
|
|
8
|
-
* @param model - Alias du modèle (LOW
|
|
9
|
-
* @param custom - Options
|
|
10
|
-
* @param custom.
|
|
11
|
-
* @param
|
|
12
|
-
*
|
|
13
|
-
* - verbosity → text.verbosity
|
|
14
|
-
* @returns Configuration du modèle
|
|
9
|
+
* @param model - Alias du modèle (ex: "LOW", "MEDIUM", "HIGH", "EMBEDDING-small", "VISION")
|
|
10
|
+
* @param custom - Options personnalisées (provider, thinking, temperature, etc.)
|
|
11
|
+
* @param custom.provider - Provider à utiliser ('openai' | 'xai'), default: LLM_PROVIDER env
|
|
12
|
+
* @param custom.thinking - Active le mode raisonnement (reasoning_effort: high)
|
|
13
|
+
* @param forResponses - Si true, adapte pour l'API Responses (sinon Chat Completions)
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Modèle par défaut
|
|
17
|
+
* const config = modelConfig("MEDIUM");
|
|
18
|
+
*
|
|
19
|
+
* // Forcer OpenAI pour embeddings
|
|
20
|
+
* const config = modelConfig("EMBEDDING-small", { provider: 'openai' });
|
|
21
|
+
*
|
|
22
|
+
* // Vision avec xAI
|
|
23
|
+
* const config = modelConfig("VISION", { provider: 'xai' });
|
|
18
24
|
*/
|
|
19
|
-
export declare function modelConfig(model: string, custom?:
|
|
25
|
+
export declare function modelConfig(model: string, custom?: {
|
|
26
|
+
provider?: LLMProvider;
|
|
27
|
+
thinking?: boolean;
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}, forResponses?: boolean): AgentModel;
|
|
@@ -1,27 +1,36 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.modelConfig = modelConfig;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const llm_1 = require("../llm");
|
|
5
|
+
const pricing_1 = require("../llm/pricing");
|
|
6
6
|
/**
|
|
7
7
|
* Configuration des modèles pour Chat Completions (legacy) et Responses API
|
|
8
8
|
*
|
|
9
9
|
* Gère la configuration des modèles avec migration automatique des paramètres
|
|
10
|
-
*
|
|
10
|
+
* entre les deux APIs.
|
|
11
11
|
*
|
|
12
|
-
* @param model - Alias du modèle (LOW
|
|
13
|
-
* @param custom - Options
|
|
14
|
-
* @param custom.
|
|
15
|
-
* @param
|
|
16
|
-
*
|
|
17
|
-
* - verbosity → text.verbosity
|
|
18
|
-
* @returns Configuration du modèle
|
|
12
|
+
* @param model - Alias du modèle (ex: "LOW", "MEDIUM", "HIGH", "EMBEDDING-small", "VISION")
|
|
13
|
+
* @param custom - Options personnalisées (provider, thinking, temperature, etc.)
|
|
14
|
+
* @param custom.provider - Provider à utiliser ('openai' | 'xai'), default: LLM_PROVIDER env
|
|
15
|
+
* @param custom.thinking - Active le mode raisonnement (reasoning_effort: high)
|
|
16
|
+
* @param forResponses - Si true, adapte pour l'API Responses (sinon Chat Completions)
|
|
19
17
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Modèle par défaut
|
|
20
|
+
* const config = modelConfig("MEDIUM");
|
|
21
|
+
*
|
|
22
|
+
* // Forcer OpenAI pour embeddings
|
|
23
|
+
* const config = modelConfig("EMBEDDING-small", { provider: 'openai' });
|
|
24
|
+
*
|
|
25
|
+
* // Vision avec xAI
|
|
26
|
+
* const config = modelConfig("VISION", { provider: 'xai' });
|
|
22
27
|
*/
|
|
23
28
|
function modelConfig(model, custom, forResponses = false) {
|
|
29
|
+
//
|
|
30
|
+
// Extraire et supprimer les paramètres spéciaux
|
|
31
|
+
const provider = custom?.provider || (0, llm_1.getDefaultProvider)();
|
|
24
32
|
const thinking = custom?.thinking || false;
|
|
33
|
+
delete custom?.provider;
|
|
25
34
|
delete custom?.thinking;
|
|
26
35
|
const defaultOptions = Object.assign({
|
|
27
36
|
stream_options: { "include_usage": true },
|
|
@@ -29,7 +38,14 @@ function modelConfig(model, custom, forResponses = false) {
|
|
|
29
38
|
//
|
|
30
39
|
// Get mapping based on provider (OpenAI vs xAI)
|
|
31
40
|
// LLM() applique automatiquement reasoning_effort si thinking=true
|
|
32
|
-
const mapping = (0,
|
|
41
|
+
const mapping = (0, pricing_1.LLM)(provider, thinking);
|
|
42
|
+
//
|
|
43
|
+
// Vérifier que le modèle existe pour ce provider
|
|
44
|
+
if (!mapping[model]) {
|
|
45
|
+
const availableModels = Object.keys(mapping).join(', ');
|
|
46
|
+
throw new Error(`Model "${model}" not available for provider "${provider}". ` +
|
|
47
|
+
`Available models: ${availableModels}`);
|
|
48
|
+
}
|
|
33
49
|
const options = Object.assign({}, mapping[model], defaultOptions);
|
|
34
50
|
//
|
|
35
51
|
// Pour Responses API : mapper vers la nouvelle structure et filtrer les paramètres incompatibles
|