societyai 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/LICENSE +21 -0
  3. package/README.md +879 -0
  4. package/dist/builder.d.ts +181 -0
  5. package/dist/builder.d.ts.map +1 -0
  6. package/dist/builder.js +667 -0
  7. package/dist/builder.js.map +1 -0
  8. package/dist/config.d.ts +43 -0
  9. package/dist/config.d.ts.map +1 -0
  10. package/dist/config.js +11 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/context.d.ts +107 -0
  13. package/dist/context.d.ts.map +1 -0
  14. package/dist/context.js +319 -0
  15. package/dist/context.js.map +1 -0
  16. package/dist/errors.d.ts +31 -0
  17. package/dist/errors.d.ts.map +1 -0
  18. package/dist/errors.js +85 -0
  19. package/dist/errors.js.map +1 -0
  20. package/dist/events.d.ts +219 -0
  21. package/dist/events.d.ts.map +1 -0
  22. package/dist/events.js +395 -0
  23. package/dist/events.js.map +1 -0
  24. package/dist/graph.d.ts +104 -0
  25. package/dist/graph.d.ts.map +1 -0
  26. package/dist/graph.js +366 -0
  27. package/dist/graph.js.map +1 -0
  28. package/dist/index.d.ts +28 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +113 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/logger.d.ts +13 -0
  33. package/dist/logger.d.ts.map +1 -0
  34. package/dist/logger.js +78 -0
  35. package/dist/logger.js.map +1 -0
  36. package/dist/memory.d.ts +146 -0
  37. package/dist/memory.d.ts.map +1 -0
  38. package/dist/memory.js +353 -0
  39. package/dist/memory.js.map +1 -0
  40. package/dist/metrics.d.ts +143 -0
  41. package/dist/metrics.d.ts.map +1 -0
  42. package/dist/metrics.js +271 -0
  43. package/dist/metrics.js.map +1 -0
  44. package/dist/middleware.d.ts +147 -0
  45. package/dist/middleware.d.ts.map +1 -0
  46. package/dist/middleware.js +484 -0
  47. package/dist/middleware.js.map +1 -0
  48. package/dist/models.d.ts +32 -0
  49. package/dist/models.d.ts.map +1 -0
  50. package/dist/models.js +211 -0
  51. package/dist/models.js.map +1 -0
  52. package/dist/patterns.d.ts +6 -0
  53. package/dist/patterns.d.ts.map +1 -0
  54. package/dist/patterns.js +68 -0
  55. package/dist/patterns.js.map +1 -0
  56. package/dist/pipeline.d.ts +84 -0
  57. package/dist/pipeline.d.ts.map +1 -0
  58. package/dist/pipeline.js +569 -0
  59. package/dist/pipeline.js.map +1 -0
  60. package/dist/retry.d.ts +5 -0
  61. package/dist/retry.d.ts.map +1 -0
  62. package/dist/retry.js +70 -0
  63. package/dist/retry.js.map +1 -0
  64. package/dist/society.d.ts +94 -0
  65. package/dist/society.d.ts.map +1 -0
  66. package/dist/society.js +721 -0
  67. package/dist/society.js.map +1 -0
  68. package/dist/strategies.d.ts +55 -0
  69. package/dist/strategies.d.ts.map +1 -0
  70. package/dist/strategies.js +678 -0
  71. package/dist/strategies.js.map +1 -0
  72. package/dist/tools.d.ts +88 -0
  73. package/dist/tools.d.ts.map +1 -0
  74. package/dist/tools.js +366 -0
  75. package/dist/tools.js.map +1 -0
  76. package/dist/types.d.ts +213 -0
  77. package/dist/types.d.ts.map +1 -0
  78. package/dist/types.js +19 -0
  79. package/dist/types.js.map +1 -0
  80. package/dist/validation.d.ts +64 -0
  81. package/dist/validation.d.ts.map +1 -0
  82. package/dist/validation.js +334 -0
  83. package/dist/validation.js.map +1 -0
  84. package/dist/worker-pool.d.ts +17 -0
  85. package/dist/worker-pool.d.ts.map +1 -0
  86. package/dist/worker-pool.js +80 -0
  87. package/dist/worker-pool.js.map +1 -0
  88. package/docs/README.md +468 -0
  89. package/docs/advanced.md +616 -0
  90. package/docs/aggregation-strategies.md +926 -0
  91. package/docs/api-reference.md +771 -0
  92. package/docs/architecture.md +648 -0
  93. package/docs/context-system.md +642 -0
  94. package/docs/event-system.md +1047 -0
  95. package/docs/examples.md +576 -0
  96. package/docs/getting-started.md +564 -0
  97. package/docs/graph-execution.md +389 -0
  98. package/docs/memory-system.md +497 -0
  99. package/docs/metrics-observability.md +560 -0
  100. package/docs/middleware-system.md +1038 -0
  101. package/docs/migration.md +296 -0
  102. package/docs/pipeline-patterns.md +761 -0
  103. package/docs/structured-output.md +612 -0
  104. package/docs/tool-calling.md +491 -0
  105. package/docs/workflows.md +740 -0
  106. package/examples/README.md +234 -0
  107. package/examples/advanced-patterns.ts +115 -0
  108. package/examples/complete-integration.ts +327 -0
  109. package/examples/graph-workflow.ts +161 -0
  110. package/examples/memory-system.ts +155 -0
  111. package/examples/metrics-tracking.ts +243 -0
  112. package/examples/structured-output.ts +231 -0
  113. package/examples/tool-calling.ts +163 -0
  114. package/package.json +94 -0
@@ -0,0 +1,1038 @@
1
+ # Middleware System
2
+
3
+ Le système de middleware de SocietyAI permet d'ajouter des préoccupations transversales (logging, caching, retry, validation) sans modifier la logique de traitement principale.
4
+
5
+ ## Table des Matières
6
+
7
+ - [Vue d'ensemble](#vue-densemble)
8
+ - [Middleware Chain](#middleware-chain)
9
+ - [Built-in Middlewares](#built-in-middlewares)
10
+ - [Custom Middlewares](#custom-middlewares)
11
+ - [Step Middlewares](#step-middlewares)
12
+ - [Middleware Composition](#middleware-composition)
13
+ - [Exemples Complets](#exemples-complets)
14
+
15
+ ## Vue d'ensemble
16
+
17
+ Le système de middleware permet de:
18
+
19
+ - **Intercepter** les requêtes et réponses
20
+ - **Transformer** les entrées/sorties
21
+ - **Ajouter** du logging, caching, validation
22
+ - **Gérer** les erreurs et retries
23
+ - **Monitorer** les performances
24
+ - **Composer** des middlewares en chaîne
25
+
26
+ ### Principes de Design
27
+
28
+ - **Composable**: Les middlewares peuvent être chaînés
29
+ - **Model-agnostic**: Fonctionne avec n'importe quel modèle AI
30
+ - **Non-intrusif**: Ne modifie pas la logique de traitement
31
+ - **Zero runtime deps**: Implémentation pure TypeScript
32
+
33
+ ## Middleware Chain
34
+
35
+ ### Création de Base
36
+
37
+ ```typescript
38
+ import { MiddlewareChain, Middlewares } from 'societyai';
39
+
40
+ const chain = MiddlewareChain.create()
41
+ .use(Middlewares.logging())
42
+ .use(Middlewares.timing())
43
+ .use(Middlewares.retry({ maxAttempts: 3 }))
44
+ .build();
45
+
46
+ // Wrapper un modèle
47
+ const wrappedModel = chain.wrap(baseModel);
48
+
49
+ // Utiliser le modèle wrappé
50
+ const result = await wrappedModel.process(prompt);
51
+ ```
52
+
53
+ ### Configuration
54
+
55
+ ```typescript
56
+ const chain = MiddlewareChain.create()
57
+ // Ordre d'exécution (priorités)
58
+ .use(Middlewares.logging({ priority: 100 }))
59
+ .use(Middlewares.caching({ priority: 90 }))
60
+ .use(Middlewares.timing({ priority: 80 }))
61
+
62
+ // Trier par priorité
63
+ .sortByPriority()
64
+
65
+ .build();
66
+ ```
67
+
68
+ ### Insertion Contrôlée
69
+
70
+ ```typescript
71
+ const chain = MiddlewareChain.create()
72
+ .use(Middlewares.logging())
73
+ .use(Middlewares.timing())
74
+
75
+ // Insérer avant un middleware spécifique
76
+ .useBefore('timing', Middlewares.validation())
77
+
78
+ // Insérer après
79
+ .useAfter('logging', Middlewares.metrics())
80
+
81
+ // Insérer à une position
82
+ .useAt(1, Middlewares.caching())
83
+
84
+ // Retirer un middleware
85
+ .remove('timing')
86
+
87
+ .build();
88
+ ```
89
+
90
+ ## Built-in Middlewares
91
+
92
+ ### Logging Middleware
93
+
94
+ ```typescript
95
+ import { Middlewares } from 'societyai';
96
+
97
+ const loggingMiddleware = Middlewares.logging({
98
+ logInput: true,
99
+ logOutput: true,
100
+ logDuration: true,
101
+ logMetadata: true,
102
+ formatter: (ctx, result) => {
103
+ return `[${ctx.agentId}] ${ctx.input} -> ${result.output} (${Date.now() - ctx.startTime}ms)`;
104
+ },
105
+ });
106
+ ```
107
+
108
+ ### Timing Middleware
109
+
110
+ ```typescript
111
+ const timingMiddleware = Middlewares.timing({
112
+ onComplete: (ctx, duration) => {
113
+ console.log(`Execution took ${duration}ms`);
114
+ metrics.recordDuration(ctx.agentId, duration);
115
+ },
116
+ });
117
+ ```
118
+
119
+ ### Caching Middleware
120
+
121
+ ```typescript
122
+ const cachingMiddleware = Middlewares.cache({
123
+ ttl: 60000, // 1 minute
124
+ maxSize: 100,
125
+ keyGenerator: (ctx) => {
126
+ // Générer une clé de cache
127
+ return `${ctx.agentId}:${JSON.stringify(ctx.input)}`;
128
+ },
129
+ shouldCache: (ctx, result) => {
130
+ // Décider si on cache
131
+ return result.output.length > 0 && !result.metadata?.error;
132
+ },
133
+ onHit: (key) => {
134
+ console.log(`Cache hit: ${key}`);
135
+ },
136
+ onMiss: (key) => {
137
+ console.log(`Cache miss: ${key}`);
138
+ },
139
+ });
140
+ ```
141
+
142
+ ### Retry Middleware
143
+
144
+ ```typescript
145
+ const retryMiddleware = Middlewares.retry({
146
+ maxAttempts: 3,
147
+ initialDelay: 1000,
148
+ maxDelay: 10000,
149
+ backoffFactor: 2,
150
+ retryIf: (error) => {
151
+ // Retry seulement pour certaines erreurs
152
+ return error.message.includes('timeout') || error.message.includes('rate limit');
153
+ },
154
+ onRetry: (attempt, error) => {
155
+ console.log(`Retry attempt ${attempt}: ${error.message}`);
156
+ },
157
+ });
158
+ ```
159
+
160
+ ### Rate Limiting Middleware
161
+
162
+ ```typescript
163
+ const rateLimitMiddleware = Middlewares.rateLimit({
164
+ maxRequests: 10,
165
+ windowMs: 60000, // 1 minute
166
+ keyGenerator: (ctx) => ctx.agentId || 'default',
167
+ onLimitReached: (ctx) => {
168
+ throw new Error('Rate limit exceeded');
169
+ },
170
+ });
171
+ ```
172
+
173
+ ### Validation Middleware
174
+
175
+ ```typescript
176
+ const validationMiddleware = Middlewares.validation({
177
+ validateInput: (input) => {
178
+ if (typeof input !== 'string' || input.length === 0) {
179
+ throw new Error('Input must be a non-empty string');
180
+ }
181
+ },
182
+ validateOutput: (output) => {
183
+ if (output.length < 10) {
184
+ throw new Error('Output too short');
185
+ }
186
+ },
187
+ });
188
+ ```
189
+
190
+ ### Circuit Breaker Middleware
191
+
192
+ ```typescript
193
+ const circuitBreakerMiddleware = Middlewares.circuitBreaker({
194
+ failureThreshold: 5,
195
+ timeout: 10000,
196
+ resetTimeout: 60000,
197
+ onOpen: (ctx) => {
198
+ console.error(`Circuit opened for ${ctx.agentId}`);
199
+ alertOps('Circuit breaker opened');
200
+ },
201
+ onClose: (ctx) => {
202
+ console.log(`Circuit closed for ${ctx.agentId}`);
203
+ },
204
+ onHalfOpen: (ctx) => {
205
+ console.log(`Circuit half-open for ${ctx.agentId}`);
206
+ },
207
+ });
208
+ ```
209
+
210
+ ### Timeout Middleware
211
+
212
+ ```typescript
213
+ const timeoutMiddleware = Middlewares.timeout({
214
+ duration: 30000, // 30 secondes
215
+ onTimeout: (ctx) => {
216
+ console.error(`Timeout for ${ctx.agentId} after 30s`);
217
+ },
218
+ });
219
+ ```
220
+
221
+ ### Metrics Middleware
222
+
223
+ ```typescript
224
+ const metricsMiddleware = Middlewares.metrics({
225
+ collector: metricsCollector,
226
+ recordInput: true,
227
+ recordOutput: true,
228
+ recordDuration: true,
229
+ recordErrors: true,
230
+ customMetrics: (ctx, result) => ({
231
+ inputLength: String(ctx.input).length,
232
+ outputLength: result.output.length,
233
+ model: ctx.metadata.get('modelName'),
234
+ }),
235
+ });
236
+ ```
237
+
238
+ ### Transform Middleware
239
+
240
+ ```typescript
241
+ const transformMiddleware = Middlewares.transform({
242
+ transformInput: (input, ctx) => {
243
+ // Enrichir l'input
244
+ return `[Context: ${ctx.agentId}] ${input}`;
245
+ },
246
+ transformOutput: (output, ctx) => {
247
+ // Nettoyer l'output
248
+ return output.trim().replace(/\s+/g, ' ');
249
+ },
250
+ });
251
+ ```
252
+
253
+ ## Custom Middlewares
254
+
255
+ ### Middleware Simple
256
+
257
+ ```typescript
258
+ import { Middleware, MiddlewareFn } from 'societyai';
259
+
260
+ // Fonction middleware
261
+ const simpleMiddleware: MiddlewareFn = async (ctx, next) => {
262
+ console.log('Before execution');
263
+
264
+ const result = await next(ctx);
265
+
266
+ console.log('After execution');
267
+
268
+ return result;
269
+ };
270
+
271
+ // Objet middleware
272
+ const namedMiddleware: Middleware = {
273
+ name: 'my-middleware',
274
+ description: 'Does something useful',
275
+ priority: 50,
276
+ fn: async (ctx, next) => {
277
+ // Logic here
278
+ return await next(ctx);
279
+ },
280
+ };
281
+ ```
282
+
283
+ ### Middleware avec État
284
+
285
+ ```typescript
286
+ class StatefulMiddleware {
287
+ private requestCount = 0;
288
+ private lastRequest = 0;
289
+
290
+ middleware(): Middleware {
291
+ return {
292
+ name: 'stateful',
293
+ fn: async (ctx, next) => {
294
+ this.requestCount++;
295
+ this.lastRequest = Date.now();
296
+
297
+ ctx.metadata.set('requestNumber', this.requestCount);
298
+
299
+ const result = await next(ctx);
300
+
301
+ console.log(`Request #${this.requestCount} completed`);
302
+
303
+ return result;
304
+ },
305
+ };
306
+ }
307
+ }
308
+
309
+ const stateful = new StatefulMiddleware();
310
+ chain.use(stateful.middleware());
311
+ ```
312
+
313
+ ### Middleware Conditionnel
314
+
315
+ ```typescript
316
+ const conditionalMiddleware: Middleware = {
317
+ name: 'conditional',
318
+ fn: async (ctx, next) => {
319
+ // Appliquer seulement pour certains agents
320
+ if (ctx.agentId?.startsWith('production-')) {
321
+ // Logic spécifique production
322
+ ctx.metadata.set('environment', 'production');
323
+ }
324
+
325
+ return await next(ctx);
326
+ },
327
+ };
328
+ ```
329
+
330
+ ### Middleware avec Configuration
331
+
332
+ ```typescript
333
+ function createCustomMiddleware(config: {
334
+ prefix?: string;
335
+ suffix?: string;
336
+ transform?: (s: string) => string;
337
+ }): Middleware {
338
+ return {
339
+ name: 'custom-transform',
340
+ fn: async (ctx, next) => {
341
+ // Transformer l'input
342
+ let input = String(ctx.input);
343
+ if (config.prefix) input = config.prefix + input;
344
+ if (config.suffix) input = input + config.suffix;
345
+ if (config.transform) input = config.transform(input);
346
+
347
+ ctx.processedInput = input;
348
+
349
+ const result = await next(ctx);
350
+
351
+ // Transformer l'output
352
+ if (config.transform) {
353
+ result.output = config.transform(result.output);
354
+ }
355
+
356
+ return result;
357
+ },
358
+ };
359
+ }
360
+
361
+ // Utilisation
362
+ chain.use(
363
+ createCustomMiddleware({
364
+ prefix: '>>> ',
365
+ suffix: ' <<<',
366
+ transform: (s) => s.toUpperCase(),
367
+ })
368
+ );
369
+ ```
370
+
371
+ ### Middleware Asynchrone
372
+
373
+ ```typescript
374
+ const asyncMiddleware: Middleware = {
375
+ name: 'async-logger',
376
+ fn: async (ctx, next) => {
377
+ // Opérations async avant
378
+ await logToDatabase({
379
+ type: 'request-start',
380
+ agentId: ctx.agentId,
381
+ input: ctx.input,
382
+ timestamp: Date.now(),
383
+ });
384
+
385
+ const result = await next(ctx);
386
+
387
+ // Opérations async après
388
+ await logToDatabase({
389
+ type: 'request-complete',
390
+ agentId: ctx.agentId,
391
+ output: result.output,
392
+ timestamp: Date.now(),
393
+ });
394
+
395
+ return result;
396
+ },
397
+ };
398
+ ```
399
+
400
+ ## Step Middlewares
401
+
402
+ Les step middlewares s'appliquent au niveau des steps de workflow plutôt qu'au niveau des modèles.
403
+
404
+ ### Step Middleware de Base
405
+
406
+ ```typescript
407
+ import { StepMiddlewares } from 'societyai';
408
+
409
+ const stepLogging = StepMiddlewares.logging({
410
+ logStepStart: true,
411
+ logStepComplete: true,
412
+ logResults: true,
413
+ });
414
+
415
+ const stepTiming = StepMiddlewares.timing({
416
+ onComplete: (stepId, duration) => {
417
+ console.log(`Step ${stepId} took ${duration}ms`);
418
+ },
419
+ });
420
+ ```
421
+
422
+ ### Step Middleware Personnalisé
423
+
424
+ ```typescript
425
+ import { StepMiddleware, StepMiddlewareContext } from 'societyai';
426
+
427
+ const customStepMiddleware: StepMiddleware = {
428
+ name: 'custom-step',
429
+ fn: async (ctx: StepMiddlewareContext, next) => {
430
+ console.log(`Executing step: ${ctx.step.id}`);
431
+ console.log(`Agents: ${ctx.step.agentIds?.join(', ')}`);
432
+
433
+ const results = await next(ctx);
434
+
435
+ console.log(`Step completed with ${results.length} results`);
436
+
437
+ return results;
438
+ },
439
+ };
440
+ ```
441
+
442
+ ### Utilisation avec Workflows
443
+
444
+ ```typescript
445
+ const workflow = WorkflowConfigBuilder.create()
446
+ .withId('workflow-1')
447
+ .withStepMiddleware(stepLogging)
448
+ .withStepMiddleware(stepTiming)
449
+ .withStepMiddleware(customStepMiddleware)
450
+ .addStep(/* ... */)
451
+ .build();
452
+ ```
453
+
454
+ ## Middleware Composition
455
+
456
+ ### Chaînes Complexes
457
+
458
+ ```typescript
459
+ const productionChain = MiddlewareChain.create()
460
+ // Layer 1: Observability
461
+ .use(Middlewares.logging({ priority: 100 }))
462
+ .use(Middlewares.metrics({ priority: 95 }))
463
+ .use(Middlewares.timing({ priority: 90 }))
464
+
465
+ // Layer 2: Performance
466
+ .use(Middlewares.cache({ priority: 80, ttl: 300000 }))
467
+ .use(Middlewares.rateLimit({ priority: 75, maxRequests: 100 }))
468
+
469
+ // Layer 3: Reliability
470
+ .use(Middlewares.retry({ priority: 70, maxAttempts: 3 }))
471
+ .use(Middlewares.circuitBreaker({ priority: 65 }))
472
+ .use(Middlewares.timeout({ priority: 60, duration: 30000 }))
473
+
474
+ // Layer 4: Validation
475
+ .use(Middlewares.validation({ priority: 50 }))
476
+ .use(Middlewares.transform({ priority: 40 }))
477
+
478
+ .sortByPriority()
479
+ .build();
480
+ ```
481
+
482
+ ### Environnements Différents
483
+
484
+ ```typescript
485
+ function createChainForEnvironment(env: 'dev' | 'staging' | 'prod') {
486
+ const chain = MiddlewareChain.create();
487
+
488
+ // Tous les environnements
489
+ chain.use(Middlewares.logging());
490
+ chain.use(Middlewares.timing());
491
+
492
+ if (env === 'dev') {
493
+ // Development: verbeux, pas de cache
494
+ chain.use(Middlewares.logging({ logInput: true, logOutput: true }));
495
+ }
496
+
497
+ if (env === 'staging' || env === 'prod') {
498
+ // Staging/Prod: cache, rate limiting
499
+ chain.use(Middlewares.cache({ ttl: 300000 }));
500
+ chain.use(Middlewares.rateLimit({ maxRequests: 100 }));
501
+ }
502
+
503
+ if (env === 'prod') {
504
+ // Production: retry, circuit breaker, metrics
505
+ chain.use(Middlewares.retry({ maxAttempts: 3 }));
506
+ chain.use(Middlewares.circuitBreaker());
507
+ chain.use(Middlewares.metrics({ collector: prodMetrics }));
508
+ }
509
+
510
+ return chain.build();
511
+ }
512
+
513
+ // Utilisation
514
+ const devChain = createChainForEnvironment('dev');
515
+ const prodChain = createChainForEnvironment('prod');
516
+ ```
517
+
518
+ ### Composition de Chaînes
519
+
520
+ ```typescript
521
+ // Créer des chaînes réutilisables
522
+ const loggingChain = MiddlewareChain.create()
523
+ .use(Middlewares.logging())
524
+ .use(Middlewares.timing())
525
+ .build();
526
+
527
+ const reliabilityChain = MiddlewareChain.create()
528
+ .use(Middlewares.retry())
529
+ .use(Middlewares.circuitBreaker())
530
+ .build();
531
+
532
+ // Combiner
533
+ const composedMiddleware = new ComposedMiddleware([
534
+ ...loggingChain.getMiddlewares(),
535
+ ...reliabilityChain.getMiddlewares(),
536
+ ]);
537
+ ```
538
+
539
+ ## Exemples Complets
540
+
541
+ ### Exemple 1: Production-Ready Chain
542
+
543
+ ```typescript
544
+ import { MiddlewareChain, Middlewares, InMemoryMetricsCollector, Society } from 'societyai';
545
+
546
+ // Métriques
547
+ const metricsCollector = new InMemoryMetricsCollector();
548
+
549
+ // Cache
550
+ const cache = new Map<string, { value: string; timestamp: number }>();
551
+
552
+ // Configuration production
553
+ const productionChain = MiddlewareChain.create()
554
+ // 1. Logging
555
+ .use(
556
+ Middlewares.logging({
557
+ logInput: false, // Ne pas logger les inputs sensibles
558
+ logOutput: false,
559
+ logDuration: true,
560
+ logMetadata: true,
561
+ formatter: (ctx, result) => {
562
+ return JSON.stringify({
563
+ timestamp: new Date().toISOString(),
564
+ agentId: ctx.agentId,
565
+ duration: Date.now() - ctx.startTime,
566
+ success: result.continue,
567
+ metadata: Object.fromEntries(result.metadata || []),
568
+ });
569
+ },
570
+ })
571
+ )
572
+
573
+ // 2. Métriques
574
+ .use(
575
+ Middlewares.metrics({
576
+ collector: metricsCollector,
577
+ recordInput: false,
578
+ recordOutput: false,
579
+ recordDuration: true,
580
+ recordErrors: true,
581
+ })
582
+ )
583
+
584
+ // 3. Rate limiting
585
+ .use(
586
+ Middlewares.rateLimit({
587
+ maxRequests: 100,
588
+ windowMs: 60000,
589
+ keyGenerator: (ctx) => ctx.agentId || 'default',
590
+ })
591
+ )
592
+
593
+ // 4. Caching
594
+ .use(
595
+ Middlewares.cache({
596
+ ttl: 300000, // 5 minutes
597
+ maxSize: 1000,
598
+ storage: cache,
599
+ keyGenerator: (ctx) => {
600
+ return `${ctx.agentId}:${hash(ctx.input)}`;
601
+ },
602
+ })
603
+ )
604
+
605
+ // 5. Circuit breaker
606
+ .use(
607
+ Middlewares.circuitBreaker({
608
+ failureThreshold: 5,
609
+ timeout: 10000,
610
+ resetTimeout: 60000,
611
+ onOpen: (ctx) => {
612
+ console.error(`Circuit opened for ${ctx.agentId}`);
613
+ alertOps({
614
+ type: 'circuit-breaker-open',
615
+ agentId: ctx.agentId,
616
+ timestamp: Date.now(),
617
+ });
618
+ },
619
+ })
620
+ )
621
+
622
+ // 6. Retry
623
+ .use(
624
+ Middlewares.retry({
625
+ maxAttempts: 3,
626
+ initialDelay: 1000,
627
+ backoffFactor: 2,
628
+ retryIf: (error) => {
629
+ // Retry sur timeout ou rate limit
630
+ return error.message.includes('timeout') || error.message.includes('rate limit');
631
+ },
632
+ onRetry: (attempt, error) => {
633
+ console.warn(`Retry ${attempt}: ${error.message}`);
634
+ },
635
+ })
636
+ )
637
+
638
+ // 7. Timeout
639
+ .use(
640
+ Middlewares.timeout({
641
+ duration: 30000,
642
+ onTimeout: (ctx) => {
643
+ console.error(`Timeout for ${ctx.agentId}`);
644
+ metricsCollector.increment('timeouts');
645
+ },
646
+ })
647
+ )
648
+
649
+ // 8. Validation
650
+ .use(
651
+ Middlewares.validation({
652
+ validateInput: (input) => {
653
+ if (!input || String(input).trim().length === 0) {
654
+ throw new Error('Input cannot be empty');
655
+ }
656
+ },
657
+ validateOutput: (output) => {
658
+ if (output.length < 10) {
659
+ throw new Error('Output too short - likely error');
660
+ }
661
+ },
662
+ })
663
+ )
664
+
665
+ .sortByPriority()
666
+ .build();
667
+
668
+ // Utiliser avec Society
669
+ const model = productionChain.wrap(baseModel);
670
+
671
+ const result = await Society.create()
672
+ .withName('Production Society')
673
+ .addAgent(
674
+ (a) =>
675
+ a
676
+ .withId('agent-1')
677
+ .withRole((r) => r.withSystemPrompt('...'))
678
+ .withModel(model) // Modèle wrappé
679
+ )
680
+ .execute(input);
681
+
682
+ // Consulter les métriques
683
+ console.log('Metrics:', metricsCollector.getMetrics());
684
+ ```
685
+
686
+ ### Exemple 2: Debug Chain
687
+
688
+ ```typescript
689
+ const debugChain = MiddlewareChain.create()
690
+ // Logging verbeux
691
+ .use(
692
+ Middlewares.logging({
693
+ logInput: true,
694
+ logOutput: true,
695
+ logDuration: true,
696
+ logMetadata: true,
697
+ formatter: (ctx, result) => {
698
+ console.log('═══════════════════════════════');
699
+ console.log('Agent:', ctx.agentId);
700
+ console.log('Input:', ctx.input);
701
+ console.log('Output:', result.output);
702
+ console.log('Duration:', Date.now() - ctx.startTime, 'ms');
703
+ console.log('Metadata:', Object.fromEntries(ctx.metadata));
704
+ console.log('═══════════════════════════════');
705
+ return '';
706
+ },
707
+ })
708
+ )
709
+
710
+ // Injecter des breakpoints
711
+ .use({
712
+ name: 'debugger',
713
+ fn: async (ctx, next) => {
714
+ // Breakpoint conditionnel
715
+ if (ctx.agentId === 'problematic-agent') {
716
+ debugger; // Pause l'exécution
717
+ }
718
+
719
+ return await next(ctx);
720
+ },
721
+ })
722
+
723
+ // Timing détaillé
724
+ .use(
725
+ Middlewares.timing({
726
+ onComplete: (ctx, duration) => {
727
+ if (duration > 1000) {
728
+ console.warn(`⚠️ Slow execution: ${duration}ms for ${ctx.agentId}`);
729
+ }
730
+ },
731
+ })
732
+ )
733
+
734
+ .build();
735
+ ```
736
+
737
+ ### Exemple 3: Testing Chain
738
+
739
+ ```typescript
740
+ const testingChain = MiddlewareChain.create()
741
+ // Mock responses
742
+ .use({
743
+ name: 'mock',
744
+ fn: async (ctx, next) => {
745
+ // Retourner des réponses mockées pour les tests
746
+ if (ctx.metadata.get('mock')) {
747
+ const mockResponse = ctx.metadata.get('mockResponse');
748
+ return {
749
+ output: String(mockResponse),
750
+ continue: true,
751
+ metadata: { mocked: true },
752
+ };
753
+ }
754
+
755
+ return await next(ctx);
756
+ },
757
+ })
758
+
759
+ // Capture pour assertions
760
+ .use({
761
+ name: 'capture',
762
+ fn: async (ctx, next) => {
763
+ const captures = (ctx.metadata.get('captures') as any[]) || [];
764
+
765
+ captures.push({
766
+ type: 'request',
767
+ input: ctx.input,
768
+ timestamp: Date.now(),
769
+ });
770
+
771
+ const result = await next(ctx);
772
+
773
+ captures.push({
774
+ type: 'response',
775
+ output: result.output,
776
+ timestamp: Date.now(),
777
+ });
778
+
779
+ ctx.metadata.set('captures', captures);
780
+
781
+ return result;
782
+ },
783
+ })
784
+
785
+ // Simulation d'erreurs
786
+ .use({
787
+ name: 'error-injection',
788
+ fn: async (ctx, next) => {
789
+ if (ctx.metadata.get('shouldFail')) {
790
+ throw new Error('Injected error for testing');
791
+ }
792
+
793
+ return await next(ctx);
794
+ },
795
+ })
796
+
797
+ .build();
798
+
799
+ // Utilisation dans les tests
800
+ describe('Agent Tests', () => {
801
+ it('should handle mocked responses', async () => {
802
+ const model = testingChain.wrap(baseModel);
803
+
804
+ // Configurer le mock
805
+ model.setMetadata('mock', true);
806
+ model.setMetadata('mockResponse', 'Mocked result');
807
+
808
+ const result = await model.process('test input');
809
+
810
+ expect(result).toBe('Mocked result');
811
+ });
812
+
813
+ it('should capture interactions', async () => {
814
+ const model = testingChain.wrap(baseModel);
815
+ const captures: any[] = [];
816
+
817
+ model.setMetadata('captures', captures);
818
+
819
+ await model.process('test input');
820
+
821
+ expect(captures).toHaveLength(2);
822
+ expect(captures[0].type).toBe('request');
823
+ expect(captures[1].type).toBe('response');
824
+ });
825
+ });
826
+ ```
827
+
828
+ ### Exemple 4: A/B Testing Middleware
829
+
830
+ ```typescript
831
+ class ABTestingMiddleware {
832
+ private variant: 'A' | 'B';
833
+ private results = {
834
+ A: { count: 0, totalDuration: 0, errors: 0 },
835
+ B: { count: 0, totalDuration: 0, errors: 0 },
836
+ };
837
+
838
+ constructor(
839
+ private splitRatio: number = 0.5 // 50/50 split
840
+ ) {
841
+ this.variant = Math.random() < splitRatio ? 'A' : 'B';
842
+ }
843
+
844
+ middleware(): Middleware {
845
+ return {
846
+ name: 'ab-testing',
847
+ fn: async (ctx, next) => {
848
+ // Déterminer la variante pour cette requête
849
+ const variant = this.getVariant();
850
+ ctx.metadata.set('variant', variant);
851
+
852
+ const startTime = Date.now();
853
+
854
+ try {
855
+ const result = await next(ctx);
856
+
857
+ const duration = Date.now() - startTime;
858
+ this.results[variant].count++;
859
+ this.results[variant].totalDuration += duration;
860
+
861
+ return result;
862
+ } catch (error) {
863
+ this.results[variant].errors++;
864
+ throw error;
865
+ }
866
+ },
867
+ };
868
+ }
869
+
870
+ private getVariant(): 'A' | 'B' {
871
+ return Math.random() < this.splitRatio ? 'A' : 'B';
872
+ }
873
+
874
+ getResults() {
875
+ return {
876
+ A: {
877
+ ...this.results.A,
878
+ avgDuration: this.results.A.totalDuration / this.results.A.count,
879
+ errorRate: this.results.A.errors / this.results.A.count,
880
+ },
881
+ B: {
882
+ ...this.results.B,
883
+ avgDuration: this.results.B.totalDuration / this.results.B.count,
884
+ errorRate: this.results.B.errors / this.results.B.count,
885
+ },
886
+ };
887
+ }
888
+ }
889
+
890
+ // Utilisation
891
+ const abTest = new ABTestingMiddleware(0.5);
892
+
893
+ const chainA = MiddlewareChain.create().use(abTest.middleware()).use(/* config A */).build();
894
+
895
+ const chainB = MiddlewareChain.create().use(abTest.middleware()).use(/* config B */).build();
896
+
897
+ // Après N requêtes
898
+ console.log('A/B Test Results:', abTest.getResults());
899
+ ```
900
+
901
+ ## Bonnes Pratiques
902
+
903
+ ### 1. Ordre des Middlewares
904
+
905
+ ```typescript
906
+ // ✅ Bon - ordre logique
907
+ MiddlewareChain.create()
908
+ .use(Middlewares.logging()) // 1. Observer
909
+ .use(Middlewares.validation()) // 2. Valider
910
+ .use(Middlewares.cache()) // 3. Cache (court-circuite)
911
+ .use(Middlewares.retry()) // 4. Retry (en cas d'échec)
912
+ .use(Middlewares.timeout()); // 5. Timeout (protection)
913
+
914
+ // ❌ Mauvais - cache avant validation
915
+ MiddlewareChain.create()
916
+ .use(Middlewares.cache()) // Pourrait cacher des inputs invalides
917
+ .use(Middlewares.validation());
918
+ ```
919
+
920
+ ### 2. Gestion d'Erreurs
921
+
922
+ ```typescript
923
+ // ✅ Bon - catch et log
924
+ const middleware: Middleware = {
925
+ name: 'safe',
926
+ fn: async (ctx, next) => {
927
+ try {
928
+ return await next(ctx);
929
+ } catch (error) {
930
+ console.error('Middleware error:', error);
931
+ // Re-throw ou retourner erreur
932
+ throw error;
933
+ }
934
+ },
935
+ };
936
+ ```
937
+
938
+ ### 3. Performance
939
+
940
+ ```typescript
941
+ // ✅ Bon - opérations légères
942
+ const fastMiddleware: Middleware = {
943
+ name: 'fast',
944
+ fn: async (ctx, next) => {
945
+ ctx.metadata.set('timestamp', Date.now());
946
+ return await next(ctx);
947
+ },
948
+ };
949
+
950
+ // ❌ Mauvais - opérations lourdes bloquantes
951
+ const slowMiddleware: Middleware = {
952
+ name: 'slow',
953
+ fn: async (ctx, next) => {
954
+ await heavyDatabaseOperation(); // Bloque l'exécution
955
+ return await next(ctx);
956
+ },
957
+ };
958
+ ```
959
+
960
+ ### 4. Metadata Usage
961
+
962
+ ```typescript
963
+ // ✅ Bon - utiliser metadata pour communication
964
+ const middleware1: Middleware = {
965
+ name: 'set-data',
966
+ fn: async (ctx, next) => {
967
+ ctx.metadata.set('userId', getCurrentUserId());
968
+ return await next(ctx);
969
+ },
970
+ };
971
+
972
+ const middleware2: Middleware = {
973
+ name: 'use-data',
974
+ fn: async (ctx, next) => {
975
+ const userId = ctx.metadata.get('userId');
976
+ // Utiliser userId...
977
+ return await next(ctx);
978
+ },
979
+ };
980
+ ```
981
+
982
+ ### 5. Naming
983
+
984
+ ```typescript
985
+ // ✅ Bon - noms descriptifs
986
+ Middlewares.logging({ name: 'request-logger' });
987
+ Middlewares.caching({ name: 'redis-cache' });
988
+
989
+ // ❌ Mauvais - noms génériques
990
+ Middlewares.logging({ name: 'middleware-1' });
991
+ ```
992
+
993
+ ## API Reference
994
+
995
+ ### `MiddlewareChain`
996
+
997
+ **Méthodes:**
998
+
999
+ - `static create(): MiddlewareChain`
1000
+ - `use(middleware: Middleware | MiddlewareFn): this`
1001
+ - `useAt(index: number, middleware: Middleware): this`
1002
+ - `useBefore(name: string, middleware: Middleware): this`
1003
+ - `useAfter(name: string, middleware: Middleware): this`
1004
+ - `remove(name: string): this`
1005
+ - `forModel(model: AIModel): this`
1006
+ - `sortByPriority(): this`
1007
+ - `getMiddlewares(): readonly Middleware[]`
1008
+ - `build(): ComposedMiddleware`
1009
+ - `wrap(model: AIModel): MiddlewareWrappedModel`
1010
+
1011
+ ### `Middlewares`
1012
+
1013
+ **Built-in Middlewares:**
1014
+
1015
+ - `static logging(config?: LoggingConfig): Middleware`
1016
+ - `static timing(config?: TimingConfig): Middleware`
1017
+ - `static cache(config: CacheConfig): Middleware`
1018
+ - `static retry(config: RetryConfig): Middleware`
1019
+ - `static rateLimit(config: RateLimitConfig): Middleware`
1020
+ - `static validation(config: ValidationConfig): Middleware`
1021
+ - `static circuitBreaker(config: CircuitBreakerConfig): Middleware`
1022
+ - `static timeout(config: TimeoutConfig): Middleware`
1023
+ - `static metrics(config: MetricsConfig): Middleware`
1024
+ - `static transform(config: TransformConfig): Middleware`
1025
+
1026
+ ### `StepMiddlewares`
1027
+
1028
+ **Step-Level Middlewares:**
1029
+
1030
+ - `static logging(config?: StepLoggingConfig): StepMiddleware`
1031
+ - `static timing(config?: StepTimingConfig): StepMiddleware`
1032
+
1033
+ ## Voir Aussi
1034
+
1035
+ - [Architecture](./architecture.md) - Concepts de base
1036
+ - [Advanced Features](./advanced.md) - Fonctionnalités avancées
1037
+ - [Metrics & Observability](./metrics-observability.md) - Métriques et monitoring
1038
+ - [Error Handling](./error-handling.md) - Gestion d'erreurs