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.
- package/CHANGELOG.md +111 -0
- package/LICENSE +21 -0
- package/README.md +879 -0
- package/dist/builder.d.ts +181 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +667 -0
- package/dist/builder.js.map +1 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +11 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +107 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +319 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +85 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +219 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +395 -0
- package/dist/events.js.map +1 -0
- package/dist/graph.d.ts +104 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +366 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +113 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +13 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +78 -0
- package/dist/logger.js.map +1 -0
- package/dist/memory.d.ts +146 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +353 -0
- package/dist/memory.js.map +1 -0
- package/dist/metrics.d.ts +143 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +271 -0
- package/dist/metrics.js.map +1 -0
- package/dist/middleware.d.ts +147 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +484 -0
- package/dist/middleware.js.map +1 -0
- package/dist/models.d.ts +32 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +211 -0
- package/dist/models.js.map +1 -0
- package/dist/patterns.d.ts +6 -0
- package/dist/patterns.d.ts.map +1 -0
- package/dist/patterns.js +68 -0
- package/dist/patterns.js.map +1 -0
- package/dist/pipeline.d.ts +84 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +569 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/retry.d.ts +5 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +70 -0
- package/dist/retry.js.map +1 -0
- package/dist/society.d.ts +94 -0
- package/dist/society.d.ts.map +1 -0
- package/dist/society.js +721 -0
- package/dist/society.js.map +1 -0
- package/dist/strategies.d.ts +55 -0
- package/dist/strategies.d.ts.map +1 -0
- package/dist/strategies.js +678 -0
- package/dist/strategies.js.map +1 -0
- package/dist/tools.d.ts +88 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +366 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +213 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +64 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +334 -0
- package/dist/validation.js.map +1 -0
- package/dist/worker-pool.d.ts +17 -0
- package/dist/worker-pool.d.ts.map +1 -0
- package/dist/worker-pool.js +80 -0
- package/dist/worker-pool.js.map +1 -0
- package/docs/README.md +468 -0
- package/docs/advanced.md +616 -0
- package/docs/aggregation-strategies.md +926 -0
- package/docs/api-reference.md +771 -0
- package/docs/architecture.md +648 -0
- package/docs/context-system.md +642 -0
- package/docs/event-system.md +1047 -0
- package/docs/examples.md +576 -0
- package/docs/getting-started.md +564 -0
- package/docs/graph-execution.md +389 -0
- package/docs/memory-system.md +497 -0
- package/docs/metrics-observability.md +560 -0
- package/docs/middleware-system.md +1038 -0
- package/docs/migration.md +296 -0
- package/docs/pipeline-patterns.md +761 -0
- package/docs/structured-output.md +612 -0
- package/docs/tool-calling.md +491 -0
- package/docs/workflows.md +740 -0
- package/examples/README.md +234 -0
- package/examples/advanced-patterns.ts +115 -0
- package/examples/complete-integration.ts +327 -0
- package/examples/graph-workflow.ts +161 -0
- package/examples/memory-system.ts +155 -0
- package/examples/metrics-tracking.ts +243 -0
- package/examples/structured-output.ts +231 -0
- package/examples/tool-calling.ts +163 -0
- 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
|