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
package/docs/advanced.md
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
# Advanced Features
|
|
2
|
+
|
|
3
|
+
Advanced features and best practices for SocietyAI.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Error Handling](#error-handling)
|
|
8
|
+
- [Retry Mechanism](#retry-mechanism)
|
|
9
|
+
- [Timeout & Cancellation](#timeout--cancellation)
|
|
10
|
+
- [Observability](#observability)
|
|
11
|
+
- [Performance Optimization](#performance-optimization)
|
|
12
|
+
- [Testing](#testing)
|
|
13
|
+
- [Production Deployment](#production-deployment)
|
|
14
|
+
|
|
15
|
+
## Error Handling
|
|
16
|
+
|
|
17
|
+
### Error Types
|
|
18
|
+
|
|
19
|
+
SocietyAI provides specific error types for different failure scenarios:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import {
|
|
23
|
+
SocietyError,
|
|
24
|
+
ProcessingFailedError,
|
|
25
|
+
TimeoutError,
|
|
26
|
+
InvalidConfigurationError,
|
|
27
|
+
OperationCancelledError,
|
|
28
|
+
} from 'societyai';
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const result = await executor.execute(workflow, input);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if (error instanceof ProcessingFailedError) {
|
|
34
|
+
console.error('AI model processing failed:', error.message);
|
|
35
|
+
// Retry with different model or parameters
|
|
36
|
+
} else if (error instanceof TimeoutError) {
|
|
37
|
+
console.error('Operation timed out');
|
|
38
|
+
// Increase timeout or optimize workflow
|
|
39
|
+
} else if (error instanceof InvalidConfigurationError) {
|
|
40
|
+
console.error('Invalid workflow configuration:', error.message);
|
|
41
|
+
// Fix configuration
|
|
42
|
+
} else if (error instanceof OperationCancelledError) {
|
|
43
|
+
console.log('Operation was cancelled by user');
|
|
44
|
+
// Handle graceful shutdown
|
|
45
|
+
} else {
|
|
46
|
+
console.error('Unexpected error:', error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Step-Level Error Handling
|
|
52
|
+
|
|
53
|
+
Each step result includes success/error information:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const result = await executor.execute(workflow, input);
|
|
57
|
+
|
|
58
|
+
for (const [stepId, stepResults] of result.stepResults) {
|
|
59
|
+
for (const stepResult of stepResults) {
|
|
60
|
+
if (!stepResult.success) {
|
|
61
|
+
console.error(
|
|
62
|
+
`Agent ${stepResult.agentId} failed in step ${stepId}:`,
|
|
63
|
+
stepResult.error?.message
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Conditional Error Recovery
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const workflow = WorkflowConfigBuilder.create()
|
|
74
|
+
.addSteps([
|
|
75
|
+
// Main step
|
|
76
|
+
StepBuilder.create().withId('main').withAgents(['main-agent']).build(),
|
|
77
|
+
|
|
78
|
+
// Error recovery step
|
|
79
|
+
StepBuilder.create()
|
|
80
|
+
.withId('recovery')
|
|
81
|
+
.withAgents(['backup-agent'])
|
|
82
|
+
.withExecutionType('conditional')
|
|
83
|
+
.withCondition((results) => {
|
|
84
|
+
const mainResults = results.get('main');
|
|
85
|
+
return mainResults?.some((r) => !r.success) ?? false;
|
|
86
|
+
})
|
|
87
|
+
.build(),
|
|
88
|
+
])
|
|
89
|
+
.build();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Lifecycle Hook Error Handling
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const workflow = WorkflowConfigBuilder.create()
|
|
96
|
+
.onAfterStep(async (step, results, context) => {
|
|
97
|
+
const failures = results.filter((r) => !r.success);
|
|
98
|
+
|
|
99
|
+
if (failures.length > 0) {
|
|
100
|
+
console.error(`Step ${step.id} had ${failures.length} failures`);
|
|
101
|
+
|
|
102
|
+
// Store error info in context
|
|
103
|
+
context.metadata.errors = context.metadata.errors || [];
|
|
104
|
+
context.metadata.errors.push({
|
|
105
|
+
step: step.id,
|
|
106
|
+
failures: failures.map((f) => ({
|
|
107
|
+
agent: f.agentId,
|
|
108
|
+
error: f.error?.message,
|
|
109
|
+
})),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Optionally throw to stop workflow
|
|
113
|
+
if (failures.length === results.length) {
|
|
114
|
+
throw new Error(`All agents failed in step ${step.id}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
.build();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Retry Mechanism
|
|
122
|
+
|
|
123
|
+
### Built-in Retry
|
|
124
|
+
|
|
125
|
+
StandardModelBase includes automatic retry with exponential backoff:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { StandardModelBase, defaultRetryOptions } from 'societyai';
|
|
129
|
+
|
|
130
|
+
const model = new StandardModelBase(
|
|
131
|
+
{
|
|
132
|
+
name: 'MyModel',
|
|
133
|
+
retryOptions: {
|
|
134
|
+
maxRetries: 3,
|
|
135
|
+
initialBackoff: 1000, // 1 second
|
|
136
|
+
maxBackoff: 10000, // 10 seconds
|
|
137
|
+
backoffFactor: 2, // Double each retry
|
|
138
|
+
jitter: true, // Add randomness
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
async (prompt) => {
|
|
142
|
+
// Your API call
|
|
143
|
+
return response;
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Per-Agent Retry Configuration
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const agent = AgentBuilder.create()
|
|
152
|
+
.withId('unreliable-agent')
|
|
153
|
+
.withRole(role)
|
|
154
|
+
.withModel(model)
|
|
155
|
+
.withInitialContext({
|
|
156
|
+
retryConfig: {
|
|
157
|
+
maxRetries: 5,
|
|
158
|
+
initialBackoff: 2000,
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
.build();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Custom Retry Logic
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
import { withRetry } from 'societyai';
|
|
168
|
+
|
|
169
|
+
const result = await withRetry(
|
|
170
|
+
async () => {
|
|
171
|
+
return await someUnreliableOperation();
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
maxRetries: 3,
|
|
175
|
+
initialBackoff: 1000,
|
|
176
|
+
maxBackoff: 5000,
|
|
177
|
+
backoffFactor: 2,
|
|
178
|
+
jitter: true,
|
|
179
|
+
},
|
|
180
|
+
signal // Optional AbortSignal
|
|
181
|
+
);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Timeout & Cancellation
|
|
185
|
+
|
|
186
|
+
### Model-Level Timeout
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const model = new StandardModelBase(
|
|
190
|
+
{
|
|
191
|
+
name: 'MyModel',
|
|
192
|
+
timeout: 30000, // 30 seconds
|
|
193
|
+
},
|
|
194
|
+
async (prompt) => {
|
|
195
|
+
return await apiCall(prompt);
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Workflow-Level Cancellation
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const controller = new AbortController();
|
|
204
|
+
|
|
205
|
+
// Set a timeout
|
|
206
|
+
const timeoutId = setTimeout(() => {
|
|
207
|
+
controller.abort();
|
|
208
|
+
}, 60000); // 60 seconds
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const result = await executor.execute(workflow, input, controller.signal);
|
|
212
|
+
clearTimeout(timeoutId);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
clearTimeout(timeoutId);
|
|
215
|
+
if (error.name === 'AbortError') {
|
|
216
|
+
console.log('Operation cancelled');
|
|
217
|
+
} else {
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### User-Triggered Cancellation
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
const controller = new AbortController();
|
|
227
|
+
|
|
228
|
+
// Start long-running operation
|
|
229
|
+
const promise = executor.execute(workflow, input, controller.signal);
|
|
230
|
+
|
|
231
|
+
// User clicks cancel button
|
|
232
|
+
cancelButton.addEventListener('click', () => {
|
|
233
|
+
controller.abort();
|
|
234
|
+
console.log('Cancelling workflow...');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const result = await promise;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error.name === 'AbortError') {
|
|
241
|
+
console.log('User cancelled the operation');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Observability
|
|
247
|
+
|
|
248
|
+
### Complete Observer Implementation
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { SocietyObserver } from 'societyai';
|
|
252
|
+
|
|
253
|
+
class MetricsObserver implements SocietyObserver {
|
|
254
|
+
private startTime: number = 0;
|
|
255
|
+
private agentTimes: Map<number, number> = new Map();
|
|
256
|
+
|
|
257
|
+
onSocietyStart(prompt: string, agentCount: number): void {
|
|
258
|
+
this.startTime = Date.now();
|
|
259
|
+
console.log(`[SOCIETY] Starting with ${agentCount} agents`);
|
|
260
|
+
console.log(`[SOCIETY] Prompt: ${prompt.substring(0, 100)}...`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
onAgentStart(agentId: number, modelName: string, prompt: unknown): void {
|
|
264
|
+
this.agentTimes.set(agentId, Date.now());
|
|
265
|
+
console.log(`[AGENT ${agentId}] Started using ${modelName}`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
onAgentComplete(agentId: number, modelName: string, result: string): void {
|
|
269
|
+
const duration = Date.now() - (this.agentTimes.get(agentId) || 0);
|
|
270
|
+
console.log(`[AGENT ${agentId}] Completed in ${duration}ms`);
|
|
271
|
+
console.log(`[AGENT ${agentId}] Result length: ${result.length} chars`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
onAgentError(agentId: number, modelName: string, error: Error): void {
|
|
275
|
+
const duration = Date.now() - (this.agentTimes.get(agentId) || 0);
|
|
276
|
+
console.error(`[AGENT ${agentId}] Failed after ${duration}ms: ${error.message}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
onPhaseStart(phase: string): void {
|
|
280
|
+
console.log(`[PHASE] Starting: ${phase}`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
onPhaseComplete(phase: string): void {
|
|
284
|
+
console.log(`[PHASE] Completed: ${phase}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
onSocietyComplete(finalResult: string): void {
|
|
288
|
+
const totalDuration = Date.now() - this.startTime;
|
|
289
|
+
console.log(`[SOCIETY] Completed in ${totalDuration}ms`);
|
|
290
|
+
console.log(`[SOCIETY] Final result length: ${finalResult.length} chars`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const observer = new MetricsObserver();
|
|
295
|
+
const executor = new DefaultWorkflowExecutor(observer);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Logging Configuration
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import { setGlobalLogLevel, LogLevel } from 'societyai';
|
|
302
|
+
|
|
303
|
+
// Development
|
|
304
|
+
setGlobalLogLevel(LogLevel.DEBUG);
|
|
305
|
+
|
|
306
|
+
// Production
|
|
307
|
+
setGlobalLogLevel(LogLevel.ERROR);
|
|
308
|
+
|
|
309
|
+
// Silent
|
|
310
|
+
setGlobalLogLevel(LogLevel.SILENT);
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Custom Logger
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
import { Logger, LogLevel } from 'societyai';
|
|
317
|
+
|
|
318
|
+
class CustomLogger implements Logger {
|
|
319
|
+
private level: LogLevel = LogLevel.INFO;
|
|
320
|
+
|
|
321
|
+
debug(message: string, ...args: unknown[]): void {
|
|
322
|
+
if (this.level >= LogLevel.DEBUG) {
|
|
323
|
+
// Send to external logging service
|
|
324
|
+
logService.debug(message, ...args);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
info(message: string, ...args: unknown[]): void {
|
|
329
|
+
if (this.level >= LogLevel.INFO) {
|
|
330
|
+
logService.info(message, ...args);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
error(message: string, ...args: unknown[]): void {
|
|
335
|
+
if (this.level >= LogLevel.ERROR) {
|
|
336
|
+
logService.error(message, ...args);
|
|
337
|
+
// Alert on errors
|
|
338
|
+
alerting.sendAlert(message);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
setLevel(level: LogLevel): void {
|
|
343
|
+
this.level = level;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
## Performance Optimization
|
|
349
|
+
|
|
350
|
+
### 1. Use Parallel Execution
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Slow: Sequential
|
|
354
|
+
const slowStep = StepBuilder.create()
|
|
355
|
+
.withAgents(['agent-1', 'agent-2', 'agent-3'])
|
|
356
|
+
.withExecutionType('sequential')
|
|
357
|
+
.build();
|
|
358
|
+
// Total time: 3 * agent_time
|
|
359
|
+
|
|
360
|
+
// Fast: Parallel
|
|
361
|
+
const fastStep = StepBuilder.create()
|
|
362
|
+
.withAgents(['agent-1', 'agent-2', 'agent-3'])
|
|
363
|
+
.withExecutionType('parallel')
|
|
364
|
+
.build();
|
|
365
|
+
// Total time: max(agent_times)
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 2. Cache Expensive Results
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const workflow = WorkflowConfigBuilder.create()
|
|
372
|
+
.onAfterStep(async (step, results, context) => {
|
|
373
|
+
if (step.id === 'expensive-analysis') {
|
|
374
|
+
// Cache the result
|
|
375
|
+
context.sharedData.set('cached-analysis', results[0].content);
|
|
376
|
+
}
|
|
377
|
+
})
|
|
378
|
+
.addSteps([
|
|
379
|
+
expensiveAnalysisStep,
|
|
380
|
+
|
|
381
|
+
StepBuilder.create()
|
|
382
|
+
.withId('use-cached')
|
|
383
|
+
.withExecutionType('conditional')
|
|
384
|
+
.withCondition((results) => {
|
|
385
|
+
// Skip if we have cached data
|
|
386
|
+
return !context.sharedData.has('cached-analysis');
|
|
387
|
+
})
|
|
388
|
+
.build(),
|
|
389
|
+
])
|
|
390
|
+
.build();
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### 3. Optimize Collaborative Iterations
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// Limit iterations
|
|
397
|
+
const step = StepBuilder.create()
|
|
398
|
+
.withExecutionType('collaborative')
|
|
399
|
+
.withMaxIterations(3) // Not too high
|
|
400
|
+
.withCompletionCondition((results, iteration) => {
|
|
401
|
+
// Exit early when done
|
|
402
|
+
return checkConsensus(results) || iteration >= 2;
|
|
403
|
+
})
|
|
404
|
+
.build();
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### 4. Worker Pool Sizing
|
|
408
|
+
|
|
409
|
+
The worker pool automatically sizes based on the number of agents, but you can optimize by:
|
|
410
|
+
|
|
411
|
+
- Grouping agents with similar workloads
|
|
412
|
+
- Balancing parallel steps
|
|
413
|
+
- Considering API rate limits
|
|
414
|
+
|
|
415
|
+
### 5. Result Streaming
|
|
416
|
+
|
|
417
|
+
For large outputs, use result transformers to reduce data size:
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
const step = StepBuilder.create()
|
|
421
|
+
.withResultTransformer((results) => {
|
|
422
|
+
// Only keep essential data
|
|
423
|
+
return results.map((r) => ({
|
|
424
|
+
agentId: r.agentId,
|
|
425
|
+
summary: r.content.substring(0, 500),
|
|
426
|
+
keywords: extractKeywords(r.content),
|
|
427
|
+
}));
|
|
428
|
+
})
|
|
429
|
+
.build();
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Testing
|
|
433
|
+
|
|
434
|
+
### Unit Testing Workflows
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
import { describe, it, expect } from 'jest';
|
|
438
|
+
import {
|
|
439
|
+
WorkflowConfigBuilder,
|
|
440
|
+
StepBuilder,
|
|
441
|
+
AgentBuilder,
|
|
442
|
+
RoleBuilder,
|
|
443
|
+
StandardModelBase,
|
|
444
|
+
DefaultWorkflowExecutor,
|
|
445
|
+
} from 'societyai';
|
|
446
|
+
|
|
447
|
+
// Mock model for testing
|
|
448
|
+
class MockModel extends StandardModelBase {
|
|
449
|
+
constructor(name: string, mockResponse: string) {
|
|
450
|
+
super({ name }, async () => mockResponse);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
describe('MyWorkflow', () => {
|
|
455
|
+
it('should complete successfully', async () => {
|
|
456
|
+
const model = new MockModel('test', 'Mock response');
|
|
457
|
+
|
|
458
|
+
const role = RoleBuilder.create().withId('test-role').withSystemPrompt('Test prompt').build();
|
|
459
|
+
|
|
460
|
+
const agent = AgentBuilder.create()
|
|
461
|
+
.withId('test-agent')
|
|
462
|
+
.withRole(role)
|
|
463
|
+
.withModel(model)
|
|
464
|
+
.build();
|
|
465
|
+
|
|
466
|
+
const workflow = WorkflowConfigBuilder.create()
|
|
467
|
+
.withId('test-workflow')
|
|
468
|
+
.addAgent(agent)
|
|
469
|
+
.addStep(StepBuilder.create().withId('test-step').withAgents(['test-agent']).build())
|
|
470
|
+
.build();
|
|
471
|
+
|
|
472
|
+
const executor = new DefaultWorkflowExecutor();
|
|
473
|
+
const result = await executor.execute(workflow, 'test input');
|
|
474
|
+
|
|
475
|
+
expect(result.success).toBe(true);
|
|
476
|
+
expect(result.output).toContain('Mock response');
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('should handle errors gracefully', async () => {
|
|
480
|
+
const errorModel = new StandardModelBase({ name: 'error-model' }, async () => {
|
|
481
|
+
throw new Error('Test error');
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Test error handling...
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### Integration Testing
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
describe('Integration Tests', () => {
|
|
493
|
+
it('should work with real API', async () => {
|
|
494
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
495
|
+
if (!apiKey) {
|
|
496
|
+
console.log('Skipping integration test (no API key)');
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const model = new OpenAIModel(apiKey);
|
|
501
|
+
// Test with real model...
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
## Production Deployment
|
|
507
|
+
|
|
508
|
+
### Environment Configuration
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
// config.ts
|
|
512
|
+
export const config = {
|
|
513
|
+
ai: {
|
|
514
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
515
|
+
timeout: parseInt(process.env.AI_TIMEOUT || '30000'),
|
|
516
|
+
maxRetries: parseInt(process.env.AI_MAX_RETRIES || '3'),
|
|
517
|
+
},
|
|
518
|
+
logging: {
|
|
519
|
+
level: process.env.LOG_LEVEL === 'debug' ? LogLevel.DEBUG : LogLevel.INFO,
|
|
520
|
+
},
|
|
521
|
+
workflow: {
|
|
522
|
+
maxConcurrency: parseInt(process.env.MAX_CONCURRENCY || '5'),
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Monitoring
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
class ProductionObserver implements SocietyObserver {
|
|
531
|
+
onSocietyStart(prompt: string, agentCount: number): void {
|
|
532
|
+
metrics.increment('workflow.started');
|
|
533
|
+
metrics.gauge('workflow.agent_count', agentCount);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
onAgentComplete(agentId: number, modelName: string, result: string): void {
|
|
537
|
+
metrics.increment('agent.completed');
|
|
538
|
+
metrics.histogram('agent.result_length', result.length);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
onAgentError(agentId: number, modelName: string, error: Error): void {
|
|
542
|
+
metrics.increment('agent.error');
|
|
543
|
+
errorTracking.captureException(error);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
onSocietyComplete(finalResult: string): void {
|
|
547
|
+
metrics.increment('workflow.completed');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Rate Limiting
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
class RateLimitedModel extends StandardModelBase {
|
|
556
|
+
private queue: Array<() => void> = [];
|
|
557
|
+
private activeRequests = 0;
|
|
558
|
+
private maxConcurrent = 5;
|
|
559
|
+
|
|
560
|
+
constructor(baseModel: AIModel) {
|
|
561
|
+
super({ name: baseModel.name() }, async (prompt, signal) => {
|
|
562
|
+
await this.acquireSlot();
|
|
563
|
+
try {
|
|
564
|
+
return await baseModel.process(prompt, signal);
|
|
565
|
+
} finally {
|
|
566
|
+
this.releaseSlot();
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
private async acquireSlot(): Promise<void> {
|
|
572
|
+
if (this.activeRequests < this.maxConcurrent) {
|
|
573
|
+
this.activeRequests++;
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return new Promise((resolve) => {
|
|
578
|
+
this.queue.push(resolve);
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
private releaseSlot(): void {
|
|
583
|
+
this.activeRequests--;
|
|
584
|
+
const next = this.queue.shift();
|
|
585
|
+
if (next) {
|
|
586
|
+
this.activeRequests++;
|
|
587
|
+
next();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Graceful Shutdown
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
const controller = new AbortController();
|
|
597
|
+
|
|
598
|
+
process.on('SIGTERM', () => {
|
|
599
|
+
console.log('Received SIGTERM, shutting down gracefully...');
|
|
600
|
+
controller.abort();
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
try {
|
|
604
|
+
await executor.execute(workflow, input, controller.signal);
|
|
605
|
+
} catch (error) {
|
|
606
|
+
if (error.name === 'AbortError') {
|
|
607
|
+
console.log('Workflow cancelled during shutdown');
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
**Next**: [Migration Guide](./migration.md) →
|
|
615
|
+
|
|
616
|
+
**Previous**: [API Reference](./api-reference.md) ←
|