agentic-api 2.0.491 → 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.
Files changed (50) hide show
  1. package/README.md +37 -34
  2. package/dist/src/agents/reducer.core.js +2 -2
  3. package/dist/src/agents/simulator.d.ts +26 -1
  4. package/dist/src/agents/simulator.dashboard.d.ts +140 -0
  5. package/dist/src/agents/simulator.dashboard.js +344 -0
  6. package/dist/src/agents/simulator.js +56 -0
  7. package/dist/src/agents/simulator.types.d.ts +38 -6
  8. package/dist/src/agents/simulator.utils.d.ts +22 -1
  9. package/dist/src/agents/simulator.utils.js +27 -0
  10. package/dist/src/execute/helpers.js +2 -2
  11. package/dist/src/execute/modelconfig.d.ts +21 -11
  12. package/dist/src/execute/modelconfig.js +29 -13
  13. package/dist/src/execute/responses.js +8 -7
  14. package/dist/src/index.d.ts +5 -1
  15. package/dist/src/index.js +20 -1
  16. package/dist/src/llm/config.d.ts +25 -0
  17. package/dist/src/llm/config.js +38 -0
  18. package/dist/src/llm/index.d.ts +48 -0
  19. package/dist/src/llm/index.js +115 -0
  20. package/dist/src/llm/openai.d.ts +6 -0
  21. package/dist/src/llm/openai.js +154 -0
  22. package/dist/src/llm/pricing.d.ts +26 -0
  23. package/dist/src/llm/pricing.js +129 -0
  24. package/dist/src/llm/xai.d.ts +17 -0
  25. package/dist/src/llm/xai.js +90 -0
  26. package/dist/src/pricing.llm.d.ts +3 -15
  27. package/dist/src/pricing.llm.js +10 -251
  28. package/dist/src/prompts.d.ts +0 -1
  29. package/dist/src/prompts.js +51 -118
  30. package/dist/src/rag/embeddings.d.ts +5 -1
  31. package/dist/src/rag/embeddings.js +15 -5
  32. package/dist/src/rag/parser.js +1 -1
  33. package/dist/src/rag/rag.manager.d.ts +33 -2
  34. package/dist/src/rag/rag.manager.js +132 -46
  35. package/dist/src/rag/types.d.ts +2 -0
  36. package/dist/src/rag/usecase.js +8 -11
  37. package/dist/src/rules/git/git.health.js +59 -4
  38. package/dist/src/rules/git/repo.d.ts +11 -4
  39. package/dist/src/rules/git/repo.js +64 -18
  40. package/dist/src/rules/git/repo.pr.d.ts +8 -0
  41. package/dist/src/rules/git/repo.pr.js +45 -1
  42. package/dist/src/rules/git/repo.tools.d.ts +5 -1
  43. package/dist/src/rules/git/repo.tools.js +54 -7
  44. package/dist/src/rules/types.d.ts +14 -0
  45. package/dist/src/rules/utils.matter.d.ts +0 -20
  46. package/dist/src/rules/utils.matter.js +42 -74
  47. package/dist/src/scrapper.js +2 -2
  48. package/dist/src/utils.d.ts +0 -8
  49. package/dist/src/utils.js +1 -28
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -66,15 +66,24 @@ npm install @agentic-api
66
66
 
67
67
  ## 💡 Quick Start
68
68
 
69
+ ### Configuration `.env`
70
+
71
+ ```bash
72
+ # Provider LLM (openai | xai)
73
+ LLM_PROVIDER=openai
74
+
75
+ # Clés API
76
+ OPENAI_API_KEY=sk-... # Requis pour OpenAI + embeddings + whisper
77
+ XAI_API_KEY=xai-... # Requis si LLM_PROVIDER=xai
78
+ ```
79
+
80
+ ### Usage
81
+
69
82
  ```typescript
70
- import OpenAI from "openai";
71
- import { executeAgentSet } from '@agentic-api';
72
- import { AgenticContext } from '@agentic-api';
73
- import { AgentStateGraph } from '@agentic-api';
83
+ import { llmInstance, executeAgentSet, AgenticContext, AgentStateGraph } from '@agentic-api';
74
84
 
75
- const openai = new OpenAI({
76
- apiKey: process.env.OPENAI_API_KEY,
77
- });
85
+ // Initialiser le LLM (utilise LLM_PROVIDER depuis .env)
86
+ llmInstance();
78
87
 
79
88
  // Create context with user information
80
89
  const context: AgenticContext = {
@@ -357,41 +366,35 @@ const structuredResult = await mapper.reduce(config, structuredCallback, {
357
366
 
358
367
  Advanced testing framework for agent behavior validation with scenario-based simulations.
359
368
 
360
- - **Scenario-Based Testing**: Define complex test scenarios with goals, personas, and expected outcomes
361
- - **Conversational Simulation**: Simulate realistic user interactions with agents
362
- - **Automatic Validation**: Built-in success/failure detection and error reporting
369
+ - **Clean API**: Separated `scenario` (context) and `testCase` (test parameters)
370
+ - **Oneshot by Default**: `maxExchanges=1` for simple single-response tests
371
+ - **Automatic Tool Validation**: Built-in validation with `expectedTools`
363
372
  - **Exchange Limiting**: Control simulation length with configurable exchange limits
364
373
 
365
374
  📖 **[Complete Agent Simulator Documentation →](./docs/README-AGENT-SIMULATOR.md)**
366
375
 
367
376
  ```typescript
368
- import { AgentSimulator, SimulationScenario } from '@agentic-api';
369
-
370
- // Define test scenario
371
- const scenario: SimulationScenario = {
372
- testGoals: "Verify that the agent can help with haiku creation",
373
- testEnd: "Agent provides a complete haiku poem",
374
- testPersona: "A poetry enthusiast seeking creative assistance",
375
- testQuery: "I want to write a haiku about nature. Can you help me?",
376
- testResult: "Agent successfully guides haiku creation process",
377
- testError: "Agent refuses to help or provides incorrect format"
378
- };
377
+ import { AgentSimulator, PERSONA_PATIENT } from '@agentic-api';
379
378
 
380
379
  // Configure simulator
381
380
  const simulator = new AgentSimulator({
382
381
  agents: [haikuAgent, welcomeAgent],
383
382
  start: "welcome",
384
- verbose: true,
385
- instructionEx: "Focus on creative writing assistance"
383
+ verbose: true
386
384
  });
387
385
 
388
- // Run simulation
389
- const result = await simulator.executeSimulation({
390
- scenario,
391
- maxExchanges: 10,
392
- onMessage: (message) => {
393
- console.log(`${message.role}: ${message.content}`);
394
- }
386
+ // Define test scenario (context)
387
+ const scenario = {
388
+ goals: "Verify that the agent can help with haiku creation. Agent provides a complete haiku poem.",
389
+ persona: PERSONA_PATIENT
390
+ // result defaults to '{"success": boolean, "explain": string, "error": string}'
391
+ };
392
+
393
+ // Run test case
394
+ const result = await simulator.testCase(scenario, {
395
+ query: "I want to write a haiku about nature. Can you help me?",
396
+ maxExchanges: 5, // defaults to 1 (oneshot)
397
+ expectedTools: { 'transferAgents': { gte: 1 } } // defaults to {}
395
398
  });
396
399
 
397
400
  // Validate results
@@ -406,10 +409,10 @@ if (!result.success) {
406
409
 
407
410
  ### Simulation Features
408
411
 
409
- - **Structured Scenarios**: Define test goals, end conditions, and expected behaviors
410
- - **Persona Simulation**: Simulator adopts specific user personas for realistic testing
411
- - **Error Detection**: Automatic detection of unwanted content or behaviors
412
- - **Exchange Tracking**: Monitor conversation flow and agent performance
412
+ - **Separated Concerns**: `scenario` for context, `testCase` for test parameters
413
+ - **Sensible Defaults**: `maxExchanges=1`, `expectedTools={}`, default result format
414
+ - **Persona Simulation**: Built-in personas (PERSONA_PATIENT, PERSONA_PRESSE, PERSONA_ENERVE)
415
+ - **Tool Validation**: Automatic validation with `equal`, `gte`, `lte` constraints
413
416
  - **Execution Metadata**: Access to token usage, actions, and performance metrics
414
417
 
415
418
  ## 📋 Rules Management System
@@ -5,7 +5,7 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.MapLLM = void 0;
7
7
  const execute_1 = require("../execute");
8
- const utils_1 = require("../utils");
8
+ const llm_1 = require("../llm");
9
9
  /**
10
10
  * MapLLM - Orchestrateur principal pour le reduce hiérarchique
11
11
  */
@@ -54,7 +54,7 @@ class MapLLM {
54
54
  let totalChunkSize = 0;
55
55
  let totalReduce = 0;
56
56
  const model = (0, execute_1.modelConfig)(result.model);
57
- const openai = (0, utils_1.openaiInstance)();
57
+ const openai = (0, llm_1.llmInstance)();
58
58
  const llm = Object.assign({}, model);
59
59
  llm.stream = false;
60
60
  delete llm.stream_options;
@@ -1,10 +1,35 @@
1
- import { SimulatorConfig, SimulationOptions, SimulationResult } from './simulator.types';
1
+ import { SimulatorConfig, SimulationOptions, SimulationResult, TestScenario, TestCaseInput } from './simulator.types';
2
2
  export declare class AgentSimulator {
3
3
  private config;
4
4
  private executor;
5
5
  private lastExecution?;
6
6
  constructor(config: SimulatorConfig);
7
7
  /**
8
+ * Exécuter un cas de test avec scénario et paramètres séparés
9
+ *
10
+ * @param scenario - Contexte stable (goals, persona, result)
11
+ * @param testCase - Paramètres du test (query, maxExchanges, model, expectedTools)
12
+ * @returns SimulationResult
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const scenario = {
17
+ * goals: 'Obtenir le nombre secret 1942',
18
+ * persona: PERSONA_PATIENT,
19
+ * result: '{"success": boolean, "error": string}'
20
+ * };
21
+ *
22
+ * const result = await simulator.testCase(scenario, {
23
+ * query: 'À quel nombre penses-tu?',
24
+ * maxExchanges: 3, // défaut: 1 (oneshot)
25
+ * expectedTools: { 'transferAgents': { equal: 1 } } // défaut: {}
26
+ * });
27
+ * ```
28
+ */
29
+ testCase(scenario: TestScenario, testCase: TestCaseInput): Promise<SimulationResult>;
30
+ /**
31
+ * @deprecated Utiliser testCase(scenario, case) à la place
32
+ *
8
33
  * Exécuter la simulation complète
9
34
  *
10
35
  * Architecture :
@@ -0,0 +1,140 @@
1
+ import { TestScenario, TestCaseInput, SimulatorConfig } from './simulator.types';
2
+ import { AgentMessage } from '../stategraph';
3
+ /**
4
+ * Test case combining scenario and case input
5
+ * Format du fichier JSON d'entrée
6
+ */
7
+ export interface DashboardTestCase {
8
+ id?: string;
9
+ name?: string;
10
+ scenario: TestScenario;
11
+ case: TestCaseInput;
12
+ }
13
+ /**
14
+ * Format du fichier JSON d'entrée
15
+ */
16
+ export interface DashboardInput {
17
+ name?: string;
18
+ description?: string;
19
+ config?: Partial<SimulatorConfig>;
20
+ tests: DashboardTestCase[];
21
+ }
22
+ /**
23
+ * Status d'exécution d'un test
24
+ */
25
+ export type TestStatus = 'pending' | 'running' | 'completed' | 'failed' | 'error';
26
+ /**
27
+ * Ligne JSONL pour un résultat de test
28
+ */
29
+ export interface DashboardOutputLine {
30
+ type: 'start' | 'result' | 'end' | 'error';
31
+ timestamp: string;
32
+ sessionId?: string;
33
+ totalTests?: number;
34
+ testId?: string;
35
+ testIndex?: number;
36
+ name?: string;
37
+ description?: string;
38
+ query?: string;
39
+ status?: TestStatus;
40
+ success?: boolean;
41
+ message?: string;
42
+ error?: string;
43
+ exchangeCount?: number;
44
+ messages?: AgentMessage[];
45
+ duration?: number;
46
+ summary?: {
47
+ total: number;
48
+ passed: number;
49
+ failed: number;
50
+ errors: number;
51
+ totalDuration: number;
52
+ };
53
+ }
54
+ /**
55
+ * Status de l'exécution pour le contrôleur backend
56
+ */
57
+ export interface DashboardStatus {
58
+ isRunning: boolean;
59
+ sessionId: string | null;
60
+ currentTest: number;
61
+ totalTests: number;
62
+ passed: number;
63
+ failed: number;
64
+ errors: number;
65
+ startTime: Date | null;
66
+ lastUpdate: Date | null;
67
+ }
68
+ export declare class SimulatorDashboard {
69
+ private config;
70
+ private status;
71
+ private simulator;
72
+ private abortController;
73
+ private _currentOutputPath;
74
+ private _currentInputPath;
75
+ constructor(config: SimulatorConfig);
76
+ /**
77
+ * Chemin du fichier output actuel (ou du dernier run)
78
+ */
79
+ get currentOutputPath(): string | null;
80
+ /**
81
+ * Chemin du fichier input actuel (ou du dernier run)
82
+ */
83
+ get currentInputPath(): string | null;
84
+ private createInitialStatus;
85
+ /**
86
+ * Obtenir le status actuel (pour le contrôleur backend)
87
+ */
88
+ getStatus(): DashboardStatus;
89
+ /**
90
+ * Vérifier si une exécution est en cours
91
+ */
92
+ isRunning(): boolean;
93
+ /**
94
+ * Annuler l'exécution en cours
95
+ */
96
+ abort(): void;
97
+ /**
98
+ * Charger un fichier JSON d'entrée
99
+ */
100
+ loadInputFile(filePath: string): Promise<DashboardInput>;
101
+ /**
102
+ * Générer le chemin du fichier output basé sur le fichier input
103
+ * Exemple: tests/my-tests.json → tests/results.my-tests.jsonl
104
+ */
105
+ createOutputPath(inputPath: string): string;
106
+ /**
107
+ * Charger les résultats JSONL existants (en cours ou terminés)
108
+ * Supporte le streaming partiel (fichier en cours d'écriture)
109
+ *
110
+ * @param inputPath - Chemin du fichier JSON d'entrée (génère automatiquement le output path)
111
+ * @returns Les lignes parsées ou null si le fichier n'existe pas
112
+ */
113
+ loadResults(inputPath?: string): Promise<DashboardOutputLine[] | null>;
114
+ /**
115
+ * Obtenir le résumé des résultats (dernière ligne type='end')
116
+ * @param inputPath - Chemin du fichier JSON d'entrée
117
+ */
118
+ getResultsSummary(inputPath?: string): Promise<DashboardOutputLine['summary'] | null>;
119
+ /**
120
+ * Vérifier si les résultats sont complets (contient une ligne 'end')
121
+ * @param inputPath - Chemin du fichier JSON d'entrée
122
+ */
123
+ isResultsComplete(inputPath?: string): Promise<boolean>;
124
+ /**
125
+ * Exécuter les tests et écrire les résultats en JSONL
126
+ *
127
+ * @param input - Données d'entrée (ou chemin vers fichier JSON)
128
+ * @param outputPath - Chemin du fichier JSONL de sortie
129
+ * @param onLine - Callback optionnel pour chaque ligne JSONL (streaming)
130
+ */
131
+ run(input: DashboardInput | string, outputPath?: string, onLine?: (line: DashboardOutputLine) => void): Promise<DashboardOutputLine[]>;
132
+ /**
133
+ * Exécuter avec callback de streaming (pour SSE/WebSocket)
134
+ */
135
+ runWithStream(input: DashboardInput | string, onLine: (line: DashboardOutputLine) => void): Promise<DashboardOutputLine[]>;
136
+ /**
137
+ * Créer un fichier JSON d'exemple pour les tests
138
+ */
139
+ static createExampleInput(): DashboardInput;
140
+ }
@@ -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;