@ai-orchestration/core 0.1.0 → 0.2.0

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 (43) hide show
  1. package/README.md +129 -5
  2. package/dist/core/interfaces.d.ts +80 -1
  3. package/dist/core/interfaces.d.ts.map +1 -1
  4. package/dist/core/metrics.d.ts +161 -0
  5. package/dist/core/metrics.d.ts.map +1 -0
  6. package/dist/core/metrics.js +276 -0
  7. package/dist/core/metrics.js.map +1 -0
  8. package/dist/core/orchestrator.d.ts +45 -3
  9. package/dist/core/orchestrator.d.ts.map +1 -1
  10. package/dist/core/orchestrator.js +271 -11
  11. package/dist/core/orchestrator.js.map +1 -1
  12. package/dist/core/types.d.ts +39 -0
  13. package/dist/core/types.d.ts.map +1 -1
  14. package/dist/factory/index.d.ts.map +1 -1
  15. package/dist/factory/index.js +27 -7
  16. package/dist/factory/index.js.map +1 -1
  17. package/dist/index.d.ts +3 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/providers/cerebras.d.ts.map +1 -1
  22. package/dist/providers/cerebras.js +22 -4
  23. package/dist/providers/cerebras.js.map +1 -1
  24. package/dist/providers/groq.d.ts.map +1 -1
  25. package/dist/providers/groq.js +22 -4
  26. package/dist/providers/groq.js.map +1 -1
  27. package/dist/providers/local.d.ts.map +1 -1
  28. package/dist/providers/local.js +9 -1
  29. package/dist/providers/local.js.map +1 -1
  30. package/dist/providers/openrouter.d.ts.map +1 -1
  31. package/dist/providers/openrouter.js +18 -2
  32. package/dist/providers/openrouter.js.map +1 -1
  33. package/docs/CHANGELOG.md +36 -0
  34. package/examples/README.md +205 -0
  35. package/examples/basic.ts +51 -17
  36. package/examples/chat-app/config.json +20 -1
  37. package/examples/chat-app/index.ts +41 -2
  38. package/examples/config.example.json +26 -4
  39. package/examples/language.ts +160 -0
  40. package/examples/metrics.ts +150 -0
  41. package/examples/strategies.ts +4 -2
  42. package/examples/test-local.ts +6 -6
  43. package/package.json +6 -2
@@ -35,6 +35,26 @@ interface AppConfig {
35
35
  chatOptions?: {
36
36
  temperature?: number;
37
37
  maxTokens?: number;
38
+ frequencyPenalty?: number;
39
+ presencePenalty?: number;
40
+ responseLanguage?: string;
41
+ };
42
+ orchestratorConfig?: {
43
+ maxRetries?: number;
44
+ requestTimeout?: number;
45
+ retryDelay?: number | 'exponential';
46
+ circuitBreaker?: {
47
+ enabled?: boolean;
48
+ failureThreshold?: number;
49
+ resetTimeout?: number;
50
+ };
51
+ healthCheck?: {
52
+ enabled?: boolean;
53
+ interval?: number;
54
+ timeout?: number;
55
+ maxConsecutiveFailures?: number;
56
+ latencyThreshold?: number;
57
+ };
38
58
  };
39
59
  }
40
60
 
@@ -102,6 +122,25 @@ async function initOrchestrator() {
102
122
  order: providers.map((p) => p.id),
103
123
  }),
104
124
  },
125
+ // Advanced configuration from config.json
126
+ ...(appConfig.orchestratorConfig || {
127
+ maxRetries: 3,
128
+ requestTimeout: 30000,
129
+ retryDelay: 'exponential',
130
+ circuitBreaker: {
131
+ enabled: true,
132
+ failureThreshold: 5,
133
+ resetTimeout: 60000,
134
+ },
135
+ healthCheck: {
136
+ enabled: true,
137
+ interval: 60000,
138
+ timeout: 5000,
139
+ maxConsecutiveFailures: 3,
140
+ latencyThreshold: 10000,
141
+ },
142
+ }),
143
+ defaultOptions: defaultChatOptions,
105
144
  });
106
145
 
107
146
  const totalProviders = orchestrator.getAllProviders();
@@ -228,8 +267,8 @@ async function sendMessage(message: string) {
228
267
 
229
268
  const startTime = Date.now();
230
269
  const response = await orchestrator.chat(messages, {
231
- temperature: 0.7,
232
- maxTokens: 1000,
270
+ ...defaultChatOptions,
271
+ // You can override per-request options here
233
272
  });
234
273
  const duration = Date.now() - startTime;
235
274
 
@@ -4,7 +4,7 @@
4
4
  "id": "groq-primary",
5
5
  "type": "groq",
6
6
  "apiKey": "${GROQ_API_KEY}",
7
- "model": "llama-3.1-70b-versatile",
7
+ "model": "llama-3.3-70b-versatile",
8
8
  "enabled": true,
9
9
  "priority": 1,
10
10
  "weight": 0.7
@@ -25,6 +25,13 @@
25
25
  "model": "gemini-pro",
26
26
  "enabled": true,
27
27
  "priority": 3
28
+ },
29
+ {
30
+ "id": "cerebras-1",
31
+ "type": "cerebras",
32
+ "apiKey": "${CEREBRAS_API_KEY}",
33
+ "model": "llama-3.3-70b",
34
+ "enabled": true
28
35
  }
29
36
  ],
30
37
  "strategy": {
@@ -34,9 +41,24 @@
34
41
  },
35
42
  "defaultOptions": {
36
43
  "temperature": 0.7,
37
- "maxTokens": 1000
44
+ "maxTokens": 1000,
45
+ "frequencyPenalty": 0.5,
46
+ "presencePenalty": 0.3
47
+ },
48
+ "maxRetries": 3,
49
+ "requestTimeout": 30000,
50
+ "retryDelay": "exponential",
51
+ "circuitBreaker": {
52
+ "enabled": true,
53
+ "failureThreshold": 5,
54
+ "resetTimeout": 60000
38
55
  },
39
- "enableHealthChecks": true,
40
- "healthCheckInterval": 60000
56
+ "healthCheck": {
57
+ "enabled": true,
58
+ "interval": 60000,
59
+ "timeout": 5000,
60
+ "maxConsecutiveFailures": 3,
61
+ "latencyThreshold": 10000
62
+ }
41
63
  }
42
64
 
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Example: Forcing response language
3
+ *
4
+ * This example demonstrates how to force the AI to respond in a specific language
5
+ * using the responseLanguage option.
6
+ */
7
+
8
+ import { createOrchestrator, type ProviderConfig } from '../src/index.js';
9
+
10
+ async function main() {
11
+ // Check for API keys
12
+ const hasGroq = !!process.env.GROQ_API_KEY;
13
+ const hasOpenRouter = !!process.env.OPENROUTER_API_KEY;
14
+ const hasGemini = !!process.env.GEMINI_API_KEY;
15
+ const hasCerebras = !!process.env.CEREBRAS_API_KEY;
16
+
17
+ if (!hasGroq && !hasOpenRouter && !hasGemini && !hasCerebras) {
18
+ console.error('āŒ Error: No API keys found!\n');
19
+ console.error(' Please set at least one of the following environment variables:');
20
+ console.error(' - GROQ_API_KEY (recommended)');
21
+ console.error(' - OPENROUTER_API_KEY');
22
+ console.error(' - GEMINI_API_KEY');
23
+ console.error(' - CEREBRAS_API_KEY\n');
24
+ console.error(' Example:');
25
+ console.error(' export GROQ_API_KEY="your-key"');
26
+ console.error(' npm run example:language\n');
27
+ process.exit(1);
28
+ }
29
+
30
+ // Use first available provider
31
+ const providers: ProviderConfig[] = [];
32
+ if (hasGroq) {
33
+ providers.push({
34
+ id: 'groq-1',
35
+ type: 'groq',
36
+ apiKey: process.env.GROQ_API_KEY!,
37
+ });
38
+ } else if (hasOpenRouter) {
39
+ providers.push({
40
+ id: 'openrouter-1',
41
+ type: 'openrouter',
42
+ apiKey: process.env.OPENROUTER_API_KEY!,
43
+ });
44
+ } else if (hasGemini) {
45
+ providers.push({
46
+ id: 'gemini-1',
47
+ type: 'gemini',
48
+ apiKey: process.env.GEMINI_API_KEY!,
49
+ });
50
+ } else if (hasCerebras) {
51
+ providers.push({
52
+ id: 'cerebras-1',
53
+ type: 'cerebras',
54
+ apiKey: process.env.CEREBRAS_API_KEY!,
55
+ });
56
+ }
57
+
58
+ console.log(`āœ… Using provider: ${providers[0].id}\n`);
59
+
60
+ // Create orchestrator with a simple configuration
61
+ const orchestrator = createOrchestrator({
62
+ providers,
63
+ strategy: {
64
+ type: 'round-robin',
65
+ },
66
+ });
67
+
68
+ const messages = [
69
+ {
70
+ role: 'user' as const,
71
+ content: 'Tell me a joke',
72
+ },
73
+ ];
74
+
75
+ console.log('=== Example 1: Force Spanish ===\n');
76
+ try {
77
+ const response1 = await orchestrator.chat(messages, {
78
+ responseLanguage: 'es', // Force Spanish response
79
+ temperature: 0.7,
80
+ });
81
+ console.log('Response:', response1.content);
82
+ console.log('');
83
+ } catch (error) {
84
+ console.error('Error:', error);
85
+ }
86
+
87
+ console.log('=== Example 2: Force English ===\n');
88
+ try {
89
+ const response2 = await orchestrator.chat(messages, {
90
+ responseLanguage: 'en', // Force English response
91
+ temperature: 0.7,
92
+ });
93
+ console.log('Response:', response2.content);
94
+ console.log('');
95
+ } catch (error) {
96
+ console.error('Error:', error);
97
+ }
98
+
99
+ console.log('=== Example 3: Force French ===\n');
100
+ try {
101
+ const response3 = await orchestrator.chat(messages, {
102
+ responseLanguage: 'french', // Can also use full language name
103
+ temperature: 0.7,
104
+ });
105
+ console.log('Response:', response3.content);
106
+ console.log('');
107
+ } catch (error) {
108
+ console.error('Error:', error);
109
+ }
110
+
111
+ console.log('=== Example 4: With existing system message ===\n');
112
+ try {
113
+ // If you already have a system message, the language instruction will be prepended
114
+ const messagesWithSystem = [
115
+ {
116
+ role: 'system' as const,
117
+ content: 'You are a helpful assistant.',
118
+ },
119
+ {
120
+ role: 'user' as const,
121
+ content: 'What is the capital of France?',
122
+ },
123
+ ];
124
+
125
+ const response4 = await orchestrator.chat(messagesWithSystem, {
126
+ responseLanguage: 'es', // Will prepend Spanish instruction to system message
127
+ temperature: 0.7,
128
+ });
129
+ console.log('Response:', response4.content);
130
+ console.log('');
131
+ } catch (error) {
132
+ console.error('Error:', error);
133
+ }
134
+
135
+ console.log('=== Example 5: Streaming with language ===\n');
136
+ try {
137
+ const stream = await orchestrator.chatStream(messages, {
138
+ responseLanguage: 'es',
139
+ temperature: 0.7,
140
+ });
141
+
142
+ const reader = stream.getReader();
143
+ const decoder = new TextDecoder();
144
+
145
+ console.log('Streaming response: ');
146
+ while (true) {
147
+ const { done, value } = await reader.read();
148
+ if (done) break;
149
+ process.stdout.write(value.content);
150
+ }
151
+ console.log('\n');
152
+ } catch (error) {
153
+ console.error('Error:', error);
154
+ }
155
+
156
+ orchestrator.dispose();
157
+ }
158
+
159
+ main().catch(console.error);
160
+
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Example: Using Metrics and Analytics
3
+ *
4
+ * This example demonstrates how to track provider usage, costs, and strategy effectiveness
5
+ */
6
+
7
+ import { createOrchestrator, type MetricsEvent } from '../src/index.js';
8
+
9
+ async function main() {
10
+ // Check for API keys
11
+ const hasGroq = !!process.env.GROQ_API_KEY;
12
+ const hasOpenRouter = !!process.env.OPENROUTER_API_KEY;
13
+
14
+ if (!hasGroq && !hasOpenRouter) {
15
+ console.error('āŒ Error: No API keys found!\n');
16
+ console.error(' Please set at least one API key:');
17
+ console.error(' export GROQ_API_KEY="your-key"\n');
18
+ process.exit(1);
19
+ }
20
+
21
+ const providers = [];
22
+ if (hasGroq) {
23
+ providers.push({
24
+ id: 'groq-1',
25
+ type: 'groq',
26
+ apiKey: process.env.GROQ_API_KEY!,
27
+ model: 'llama-3.3-70b-versatile',
28
+ // Optional: Add cost per token for cost tracking
29
+ metadata: {
30
+ costPerToken: {
31
+ prompt: 0.0000002, // $0.20 per 1M tokens
32
+ completion: 0.0000002,
33
+ },
34
+ },
35
+ });
36
+ }
37
+
38
+ if (hasOpenRouter) {
39
+ providers.push({
40
+ id: 'openrouter-1',
41
+ type: 'openrouter',
42
+ apiKey: process.env.OPENROUTER_API_KEY!,
43
+ model: 'openai/gpt-3.5-turbo',
44
+ metadata: {
45
+ costPerToken: {
46
+ prompt: 0.0000015, // $1.50 per 1M tokens
47
+ completion: 0.000002, // $2.00 per 1M tokens
48
+ },
49
+ },
50
+ });
51
+ }
52
+
53
+ console.log(`āœ… Using ${providers.length} provider(s)\n`);
54
+
55
+ // Create orchestrator with metrics enabled
56
+ const orchestrator = createOrchestrator({
57
+ providers,
58
+ strategy: {
59
+ type: 'round-robin',
60
+ },
61
+ enableMetrics: true, // Enable metrics (default: true)
62
+ onMetricsEvent: (event: MetricsEvent) => {
63
+ // Optional: Log events in real-time
64
+ // console.log('šŸ“Š Event:', event.type, event.providerId);
65
+ },
66
+ });
67
+
68
+ // Make some requests
69
+ console.log('šŸ“ Making requests...\n');
70
+
71
+ for (let i = 0; i < 3; i++) {
72
+ try {
73
+ const response = await orchestrator.chat([
74
+ { role: 'user', content: `Say "Request ${i + 1}" in one word` },
75
+ ]);
76
+ console.log(`āœ… Request ${i + 1}: ${response.content.substring(0, 20)}...`);
77
+ } catch (error) {
78
+ console.error(`āŒ Request ${i + 1} failed:`, error);
79
+ }
80
+ }
81
+
82
+ console.log('\nšŸ“Š ===== METRICS REPORT =====\n');
83
+
84
+ // Get overall metrics
85
+ const overallMetrics = orchestrator.getMetrics().getOrchestratorMetrics();
86
+
87
+ console.log('šŸ“ˆ Overall Statistics:');
88
+ console.log(` Total Requests: ${overallMetrics.totalRequests}`);
89
+ console.log(` Successful: ${overallMetrics.successfulRequests}`);
90
+ console.log(` Failed: ${overallMetrics.failedRequests}`);
91
+ console.log(` Error Rate: ${(overallMetrics.errorRate * 100).toFixed(2)}%`);
92
+ console.log(` Average Latency: ${overallMetrics.averageRequestLatency.toFixed(2)}ms`);
93
+ console.log(` Requests/Minute: ${overallMetrics.requestsPerMinute}`);
94
+ console.log(` Total Cost: $${overallMetrics.totalCost.toFixed(6)}\n`);
95
+
96
+ // Get provider-specific metrics
97
+ console.log('šŸ”Œ Provider Metrics:\n');
98
+ const providerMetrics = orchestrator.getMetrics().getAllProviderMetrics();
99
+
100
+ for (const [providerId, metrics] of providerMetrics) {
101
+ console.log(` ${metrics.providerName} (${providerId}):`);
102
+ console.log(` Model: ${metrics.model || 'N/A'}`);
103
+ console.log(` Total Requests: ${metrics.totalRequests}`);
104
+ console.log(` Successful: ${metrics.successfulRequests}`);
105
+ console.log(` Failed: ${metrics.failedRequests}`);
106
+ console.log(` Success Rate: ${metrics.totalRequests > 0 ? ((metrics.successfulRequests / metrics.totalRequests) * 100).toFixed(2) : 0}%`);
107
+ console.log(` Average Latency: ${metrics.averageLatency.toFixed(2)}ms`);
108
+ console.log(` Total Tokens: ${metrics.totalTokens.total.toLocaleString()}`);
109
+ console.log(` - Prompt: ${metrics.totalTokens.prompt.toLocaleString()}`);
110
+ console.log(` - Completion: ${metrics.totalTokens.completion.toLocaleString()}`);
111
+ console.log(` Total Cost: $${metrics.totalCost.toFixed(6)}`);
112
+ if (metrics.lastUsed) {
113
+ console.log(` Last Used: ${metrics.lastUsed.toLocaleString()}`);
114
+ }
115
+ console.log('');
116
+ }
117
+
118
+ // Get strategy metrics
119
+ console.log('šŸŽÆ Strategy Metrics:\n');
120
+ const strategyMetrics = orchestrator.getMetrics().getStrategyMetrics();
121
+
122
+ console.log(` Total Selections: ${strategyMetrics.totalSelections}`);
123
+ console.log(` Average Selection Time: ${strategyMetrics.averageSelectionTime.toFixed(2)}ms`);
124
+ console.log(` Selections by Provider:`);
125
+ for (const [providerId, count] of strategyMetrics.selectionsByProvider) {
126
+ const percentage = strategyMetrics.totalSelections > 0
127
+ ? ((count / strategyMetrics.totalSelections) * 100).toFixed(1)
128
+ : '0';
129
+ console.log(` ${providerId}: ${count} (${percentage}%)`);
130
+ }
131
+ console.log('');
132
+
133
+ // Get request history
134
+ console.log('šŸ“œ Recent Request History:\n');
135
+ const history = orchestrator.getMetrics().getRequestHistory({ limit: 5 });
136
+ for (const request of history) {
137
+ const status = request.success ? 'āœ…' : 'āŒ';
138
+ const tokens = request.tokens ? ` (${request.tokens.total} tokens)` : '';
139
+ const cost = request.cost ? ` - $${request.cost.toFixed(6)}` : '';
140
+ console.log(` ${status} ${request.providerId} - ${request.latency}ms${tokens}${cost}`);
141
+ }
142
+
143
+ console.log('\nšŸ’” Tip: Use orchestrator.getMetrics() to access all metrics programmatically');
144
+ console.log(' You can also register callbacks with onMetricsEvent() for real-time tracking\n');
145
+
146
+ orchestrator.dispose();
147
+ }
148
+
149
+ main().catch(console.error);
150
+
@@ -105,8 +105,10 @@ export function createHealthAwareOrchestrator() {
105
105
  preferLowLatency: true,
106
106
  minHealthScore: 0.5,
107
107
  },
108
- enableHealthChecks: true,
109
- healthCheckInterval: 30000, // 30 seconds
108
+ healthCheck: {
109
+ enabled: true,
110
+ interval: 30000, // 30 seconds
111
+ },
110
112
  });
111
113
  }
112
114
 
@@ -36,12 +36,12 @@ async function main() {
36
36
  const providers: any[] = [];
37
37
 
38
38
  if (hasGroq) {
39
- providers.push({
40
- id: 'groq-1',
41
- type: 'groq',
42
- apiKey: process.env.GROQ_API_KEY,
43
- model: 'llama-3.1-70b-versatile',
44
- });
39
+ providers.push( {
40
+ id: 'groq-1',
41
+ type: 'groq',
42
+ apiKey: process.env.GROQ_API_KEY,
43
+ model: 'llama-3.3-70b-versatile',
44
+ });
45
45
  }
46
46
 
47
47
  if (hasOpenRouter) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-orchestration/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Modular AI orchestration framework for multiple LLM providers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -14,9 +14,13 @@
14
14
  "scripts": {
15
15
  "build": "tsc",
16
16
  "dev": "tsc --watch",
17
- "test": "npx tsx tests/basic.test.ts",
17
+ "test": "node --test --loader tsx tests/**/*.test.ts",
18
18
  "test:local": "npx tsx examples/test-local.ts",
19
19
  "test:mock": "npx tsx examples/test-mock.ts",
20
+ "example:basic": "npx tsx examples/basic.ts",
21
+ "example:strategies": "npx tsx examples/strategies.ts",
22
+ "example:language": "npx tsx examples/language.ts",
23
+ "example:metrics": "npx tsx examples/metrics.ts",
20
24
  "lint": "eslint src --ext .ts",
21
25
  "typecheck": "tsc --noEmit",
22
26
  "link": "npm run build && npm link",