@ai-orchestration/core 0.1.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.
- package/.eslintrc.json +27 -0
- package/README.md +516 -0
- package/dist/core/errors.d.ts +21 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +38 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/interfaces.d.ts +87 -0
- package/dist/core/interfaces.d.ts.map +1 -0
- package/dist/core/interfaces.js +5 -0
- package/dist/core/interfaces.js.map +1 -0
- package/dist/core/orchestrator.d.ts +55 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +147 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/types.d.ts +72 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +5 -0
- package/dist/core/types.js.map +1 -0
- package/dist/factory/index.d.ts +14 -0
- package/dist/factory/index.d.ts.map +1 -0
- package/dist/factory/index.js +184 -0
- package/dist/factory/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/base.d.ts +30 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +31 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/cerebras.d.ts +26 -0
- package/dist/providers/cerebras.d.ts.map +1 -0
- package/dist/providers/cerebras.js +190 -0
- package/dist/providers/cerebras.js.map +1 -0
- package/dist/providers/gemini.d.ts +26 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +181 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/groq.d.ts +26 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/groq.js +225 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/providers/index.d.ts +15 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +10 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/local.d.ts +27 -0
- package/dist/providers/local.d.ts.map +1 -0
- package/dist/providers/local.js +198 -0
- package/dist/providers/local.js.map +1 -0
- package/dist/providers/openrouter.d.ts +26 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +172 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/strategies/base.d.ts +16 -0
- package/dist/strategies/base.d.ts.map +1 -0
- package/dist/strategies/base.js +21 -0
- package/dist/strategies/base.js.map +1 -0
- package/dist/strategies/fallback.d.ts +14 -0
- package/dist/strategies/fallback.d.ts.map +1 -0
- package/dist/strategies/fallback.js +29 -0
- package/dist/strategies/fallback.js.map +1 -0
- package/dist/strategies/health-aware.d.ts +20 -0
- package/dist/strategies/health-aware.d.ts.map +1 -0
- package/dist/strategies/health-aware.js +106 -0
- package/dist/strategies/health-aware.js.map +1 -0
- package/dist/strategies/index.d.ts +14 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +10 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/strategies/priority.d.ts +18 -0
- package/dist/strategies/priority.d.ts.map +1 -0
- package/dist/strategies/priority.js +35 -0
- package/dist/strategies/priority.js.map +1 -0
- package/dist/strategies/round-robin.d.ts +10 -0
- package/dist/strategies/round-robin.d.ts.map +1 -0
- package/dist/strategies/round-robin.js +21 -0
- package/dist/strategies/round-robin.js.map +1 -0
- package/dist/strategies/weighted.d.ts +20 -0
- package/dist/strategies/weighted.d.ts.map +1 -0
- package/dist/strategies/weighted.js +58 -0
- package/dist/strategies/weighted.js.map +1 -0
- package/docs/ARCHITECTURE.md +126 -0
- package/docs/CHANGELOG.md +30 -0
- package/docs/CONTRIBUTING.md +198 -0
- package/examples/basic.ts +58 -0
- package/examples/chat-app/README.md +146 -0
- package/examples/chat-app/config.json +34 -0
- package/examples/chat-app/index.ts +330 -0
- package/examples/chat-app/package-lock.json +570 -0
- package/examples/chat-app/package.json +20 -0
- package/examples/config.example.json +42 -0
- package/examples/strategies.ts +112 -0
- package/examples/test-local.ts +150 -0
- package/examples/test-mock.ts +172 -0
- package/package/.eslintrc.json +27 -0
- package/package/CHANGELOG.md +31 -0
- package/package/CONTRIBUTING.md +156 -0
- package/package/PUBLISHING.md +213 -0
- package/package/README.md +515 -0
- package/package/dist/core/errors.d.ts +21 -0
- package/package/dist/core/errors.d.ts.map +1 -0
- package/package/dist/core/errors.js +38 -0
- package/package/dist/core/errors.js.map +1 -0
- package/package/dist/core/interfaces.d.ts +87 -0
- package/package/dist/core/interfaces.d.ts.map +1 -0
- package/package/dist/core/interfaces.js +5 -0
- package/package/dist/core/interfaces.js.map +1 -0
- package/package/dist/core/orchestrator.d.ts +55 -0
- package/package/dist/core/orchestrator.d.ts.map +1 -0
- package/package/dist/core/orchestrator.js +147 -0
- package/package/dist/core/orchestrator.js.map +1 -0
- package/package/dist/core/types.d.ts +72 -0
- package/package/dist/core/types.d.ts.map +1 -0
- package/package/dist/core/types.js +5 -0
- package/package/dist/core/types.js.map +1 -0
- package/package/dist/factory/index.d.ts +14 -0
- package/package/dist/factory/index.d.ts.map +1 -0
- package/package/dist/factory/index.js +184 -0
- package/package/dist/factory/index.js.map +1 -0
- package/package/dist/index.d.ts +14 -0
- package/package/dist/index.d.ts.map +1 -0
- package/package/dist/index.js +15 -0
- package/package/dist/index.js.map +1 -0
- package/package/dist/providers/base.d.ts +30 -0
- package/package/dist/providers/base.d.ts.map +1 -0
- package/package/dist/providers/base.js +31 -0
- package/package/dist/providers/base.js.map +1 -0
- package/package/dist/providers/cerebras.d.ts +26 -0
- package/package/dist/providers/cerebras.d.ts.map +1 -0
- package/package/dist/providers/cerebras.js +190 -0
- package/package/dist/providers/cerebras.js.map +1 -0
- package/package/dist/providers/gemini.d.ts +26 -0
- package/package/dist/providers/gemini.d.ts.map +1 -0
- package/package/dist/providers/gemini.js +181 -0
- package/package/dist/providers/gemini.js.map +1 -0
- package/package/dist/providers/groq.d.ts +26 -0
- package/package/dist/providers/groq.d.ts.map +1 -0
- package/package/dist/providers/groq.js +225 -0
- package/package/dist/providers/groq.js.map +1 -0
- package/package/dist/providers/index.d.ts +15 -0
- package/package/dist/providers/index.d.ts.map +1 -0
- package/package/dist/providers/index.js +10 -0
- package/package/dist/providers/index.js.map +1 -0
- package/package/dist/providers/local.d.ts +27 -0
- package/package/dist/providers/local.d.ts.map +1 -0
- package/package/dist/providers/local.js +198 -0
- package/package/dist/providers/local.js.map +1 -0
- package/package/dist/providers/openrouter.d.ts +26 -0
- package/package/dist/providers/openrouter.d.ts.map +1 -0
- package/package/dist/providers/openrouter.js +172 -0
- package/package/dist/providers/openrouter.js.map +1 -0
- package/package/dist/strategies/base.d.ts +16 -0
- package/package/dist/strategies/base.d.ts.map +1 -0
- package/package/dist/strategies/base.js +21 -0
- package/package/dist/strategies/base.js.map +1 -0
- package/package/dist/strategies/fallback.d.ts +14 -0
- package/package/dist/strategies/fallback.d.ts.map +1 -0
- package/package/dist/strategies/fallback.js +29 -0
- package/package/dist/strategies/fallback.js.map +1 -0
- package/package/dist/strategies/health-aware.d.ts +20 -0
- package/package/dist/strategies/health-aware.d.ts.map +1 -0
- package/package/dist/strategies/health-aware.js +106 -0
- package/package/dist/strategies/health-aware.js.map +1 -0
- package/package/dist/strategies/index.d.ts +14 -0
- package/package/dist/strategies/index.d.ts.map +1 -0
- package/package/dist/strategies/index.js +10 -0
- package/package/dist/strategies/index.js.map +1 -0
- package/package/dist/strategies/priority.d.ts +18 -0
- package/package/dist/strategies/priority.d.ts.map +1 -0
- package/package/dist/strategies/priority.js +35 -0
- package/package/dist/strategies/priority.js.map +1 -0
- package/package/dist/strategies/round-robin.d.ts +10 -0
- package/package/dist/strategies/round-robin.d.ts.map +1 -0
- package/package/dist/strategies/round-robin.js +21 -0
- package/package/dist/strategies/round-robin.js.map +1 -0
- package/package/dist/strategies/weighted.d.ts +20 -0
- package/package/dist/strategies/weighted.d.ts.map +1 -0
- package/package/dist/strategies/weighted.js +58 -0
- package/package/dist/strategies/weighted.js.map +1 -0
- package/package/docs/ARCHITECTURE.md +127 -0
- package/package/examples/basic.ts +58 -0
- package/package/examples/chat-app/README.md +146 -0
- package/package/examples/chat-app/config.json +34 -0
- package/package/examples/chat-app/index.ts +330 -0
- package/package/examples/chat-app/package-lock.json +570 -0
- package/package/examples/chat-app/package.json +20 -0
- package/package/examples/config.example.json +42 -0
- package/package/examples/strategies.ts +112 -0
- package/package/examples/test-local.ts +150 -0
- package/package/examples/test-mock.ts +172 -0
- package/package/package.json +62 -0
- package/package/scripts/check-types.ts +15 -0
- package/package/scripts/link.sh +30 -0
- package/package/scripts/pack.sh +36 -0
- package/package.json +62 -0
- package/scripts/check-types.ts +15 -0
- package/scripts/link.sh +30 -0
- package/scripts/pack.sh +36 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Examples of different strategies
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { createOrchestrator } from '../src/index.js';
|
|
6
|
+
|
|
7
|
+
// Example 1: Priority Strategy
|
|
8
|
+
export function createPriorityOrchestrator() {
|
|
9
|
+
return createOrchestrator({
|
|
10
|
+
providers: [
|
|
11
|
+
{
|
|
12
|
+
id: 'groq-1',
|
|
13
|
+
type: 'groq',
|
|
14
|
+
apiKey: process.env.GROQ_API_KEY || '',
|
|
15
|
+
priority: 1,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'openrouter-1',
|
|
19
|
+
type: 'openrouter',
|
|
20
|
+
apiKey: process.env.OPENROUTER_API_KEY || '',
|
|
21
|
+
priority: 2,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
strategy: {
|
|
25
|
+
type: 'priority',
|
|
26
|
+
priorities: {
|
|
27
|
+
'groq-1': 1,
|
|
28
|
+
'openrouter-1': 2,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Example 2: Fallback Strategy
|
|
35
|
+
export function createFallbackOrchestrator() {
|
|
36
|
+
return createOrchestrator({
|
|
37
|
+
providers: [
|
|
38
|
+
{
|
|
39
|
+
id: 'groq-1',
|
|
40
|
+
type: 'groq',
|
|
41
|
+
apiKey: process.env.GROQ_API_KEY || '',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'openrouter-1',
|
|
45
|
+
type: 'openrouter',
|
|
46
|
+
apiKey: process.env.OPENROUTER_API_KEY || '',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'gemini-1',
|
|
50
|
+
type: 'gemini',
|
|
51
|
+
apiKey: process.env.GEMINI_API_KEY || '',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
strategy: {
|
|
55
|
+
type: 'fallback',
|
|
56
|
+
order: ['groq-1', 'openrouter-1', 'gemini-1'],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Example 3: Weighted Strategy (Load Balancing)
|
|
62
|
+
export function createWeightedOrchestrator() {
|
|
63
|
+
return createOrchestrator({
|
|
64
|
+
providers: [
|
|
65
|
+
{
|
|
66
|
+
id: 'groq-1',
|
|
67
|
+
type: 'groq',
|
|
68
|
+
apiKey: process.env.GROQ_API_KEY || '',
|
|
69
|
+
weight: 0.7,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'openrouter-1',
|
|
73
|
+
type: 'openrouter',
|
|
74
|
+
apiKey: process.env.OPENROUTER_API_KEY || '',
|
|
75
|
+
weight: 0.3,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
strategy: {
|
|
79
|
+
type: 'weighted',
|
|
80
|
+
weights: {
|
|
81
|
+
'groq-1': 0.7,
|
|
82
|
+
'openrouter-1': 0.3,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Example 4: Health-Aware Strategy
|
|
89
|
+
export function createHealthAwareOrchestrator() {
|
|
90
|
+
return createOrchestrator({
|
|
91
|
+
providers: [
|
|
92
|
+
{
|
|
93
|
+
id: 'groq-1',
|
|
94
|
+
type: 'groq',
|
|
95
|
+
apiKey: process.env.GROQ_API_KEY || '',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: 'openrouter-1',
|
|
99
|
+
type: 'openrouter',
|
|
100
|
+
apiKey: process.env.OPENROUTER_API_KEY || '',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
strategy: {
|
|
104
|
+
type: 'health-aware',
|
|
105
|
+
preferLowLatency: true,
|
|
106
|
+
minHealthScore: 0.5,
|
|
107
|
+
},
|
|
108
|
+
enableHealthChecks: true,
|
|
109
|
+
healthCheckInterval: 30000, // 30 seconds
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local test script
|
|
3
|
+
* Run with: npm run test:local
|
|
4
|
+
* or: tsx examples/test-local.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createOrchestrator } from '../src/index.js';
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log('🚀 Iniciando prueba del framework...\n');
|
|
11
|
+
|
|
12
|
+
// Verificar variables de entorno
|
|
13
|
+
const hasGroq = !!process.env.GROQ_API_KEY;
|
|
14
|
+
const hasOpenRouter = !!process.env.OPENROUTER_API_KEY;
|
|
15
|
+
const hasGemini = !!process.env.GEMINI_API_KEY;
|
|
16
|
+
|
|
17
|
+
console.log('📋 Estado de API Keys:');
|
|
18
|
+
console.log(` Groq: ${hasGroq ? '✅' : '❌'}`);
|
|
19
|
+
console.log(` OpenRouter: ${hasOpenRouter ? '✅' : '❌'}`);
|
|
20
|
+
console.log(` Gemini: ${hasGemini ? '✅' : '❌'}\n`);
|
|
21
|
+
|
|
22
|
+
if (!hasGroq && !hasOpenRouter && !hasGemini) {
|
|
23
|
+
console.log('⚠️ No se encontraron API keys.');
|
|
24
|
+
console.log(' Configura las variables de entorno:');
|
|
25
|
+
console.log(' export GROQ_API_KEY="tu-key"');
|
|
26
|
+
console.log(' export OPENROUTER_API_KEY="tu-key"');
|
|
27
|
+
console.log(' export GEMINI_API_KEY="tu-key"\n');
|
|
28
|
+
console.log(' O crea un archivo .env con:');
|
|
29
|
+
console.log(' GROQ_API_KEY=tu-key');
|
|
30
|
+
console.log(' OPENROUTER_API_KEY=tu-key');
|
|
31
|
+
console.log(' GEMINI_API_KEY=tu-key\n');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Crear configuración dinámica basada en las keys disponibles
|
|
36
|
+
const providers: any[] = [];
|
|
37
|
+
|
|
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
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (hasOpenRouter) {
|
|
48
|
+
providers.push({
|
|
49
|
+
id: 'openrouter-1',
|
|
50
|
+
type: 'openrouter',
|
|
51
|
+
apiKey: process.env.OPENROUTER_API_KEY,
|
|
52
|
+
model: 'openai/gpt-3.5-turbo',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (hasGemini) {
|
|
57
|
+
providers.push({
|
|
58
|
+
id: 'gemini-1',
|
|
59
|
+
type: 'gemini',
|
|
60
|
+
apiKey: process.env.GEMINI_API_KEY,
|
|
61
|
+
model: 'gemini-pro',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
console.log('🔧 Creando orchestrator...');
|
|
67
|
+
const orchestrator = createOrchestrator({
|
|
68
|
+
providers,
|
|
69
|
+
strategy: {
|
|
70
|
+
type: 'round-robin',
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log(`✅ Orchestrator creado con ${providers.length} proveedor(es)\n`);
|
|
75
|
+
|
|
76
|
+
// Test 1: Chat simple
|
|
77
|
+
console.log('📝 Test 1: Chat simple');
|
|
78
|
+
console.log('─────────────────────────────────');
|
|
79
|
+
const messages = [
|
|
80
|
+
{ role: 'user' as const, content: 'Di "Hola" en una sola palabra' },
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
const response = await orchestrator.chat(messages, {
|
|
85
|
+
temperature: 0.7,
|
|
86
|
+
maxTokens: 50,
|
|
87
|
+
});
|
|
88
|
+
const duration = Date.now() - startTime;
|
|
89
|
+
|
|
90
|
+
console.log(`Respuesta: ${response.content}`);
|
|
91
|
+
console.log(`Modelo: ${response.model || 'N/A'}`);
|
|
92
|
+
console.log(`Tiempo: ${duration}ms`);
|
|
93
|
+
if (response.usage) {
|
|
94
|
+
console.log(`Tokens: ${response.usage.totalTokens} (prompt: ${response.usage.promptTokens}, completion: ${response.usage.completionTokens})`);
|
|
95
|
+
}
|
|
96
|
+
console.log('');
|
|
97
|
+
|
|
98
|
+
// Test 2: Verificar salud de proveedores
|
|
99
|
+
console.log('🏥 Test 2: Health checks');
|
|
100
|
+
console.log('─────────────────────────────────');
|
|
101
|
+
const allProviders = orchestrator.getAllProviders();
|
|
102
|
+
for (const provider of allProviders) {
|
|
103
|
+
try {
|
|
104
|
+
const health = await provider.checkHealth();
|
|
105
|
+
console.log(`${provider.id}: ${health.healthy ? '✅' : '❌'} ${health.latency ? `(${health.latency}ms)` : ''}`);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.log(`${provider.id}: ❌ Error - ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
console.log('');
|
|
111
|
+
|
|
112
|
+
// Test 3: Streaming (opcional, comentado por defecto)
|
|
113
|
+
if (process.env.TEST_STREAMING === 'true') {
|
|
114
|
+
console.log('🌊 Test 3: Streaming');
|
|
115
|
+
console.log('─────────────────────────────────');
|
|
116
|
+
const stream = await orchestrator.chatStream([
|
|
117
|
+
{ role: 'user' as const, content: 'Cuenta del 1 al 5, un número por línea' },
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
const reader = stream.getReader();
|
|
121
|
+
let fullContent = '';
|
|
122
|
+
while (true) {
|
|
123
|
+
const { done, value } = await reader.read();
|
|
124
|
+
if (done) break;
|
|
125
|
+
fullContent += value.content;
|
|
126
|
+
process.stdout.write(value.content);
|
|
127
|
+
}
|
|
128
|
+
console.log('\n✅ Streaming completado\n');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Cleanup
|
|
132
|
+
orchestrator.dispose();
|
|
133
|
+
console.log('✅ Pruebas completadas exitosamente!');
|
|
134
|
+
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('\n❌ Error durante las pruebas:');
|
|
137
|
+
if (error instanceof Error) {
|
|
138
|
+
console.error(` ${error.name}: ${error.message}`);
|
|
139
|
+
if (error.stack) {
|
|
140
|
+
console.error(` Stack: ${error.stack}`);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
console.error(` ${String(error)}`);
|
|
144
|
+
}
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
main().catch(console.error);
|
|
150
|
+
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test with mock provider (no API keys required)
|
|
3
|
+
* Run with: npm run test:mock
|
|
4
|
+
* or: tsx examples/test-mock.ts
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
Orchestrator,
|
|
9
|
+
RoundRobinStrategy,
|
|
10
|
+
PriorityStrategy,
|
|
11
|
+
FallbackStrategy,
|
|
12
|
+
} from '../src/index.js';
|
|
13
|
+
import type { AIService, ChatMessage, ChatResponse, ChatChunk, ProviderHealth } from '../src/index.js';
|
|
14
|
+
|
|
15
|
+
// Mock provider para pruebas sin API keys
|
|
16
|
+
class MockProvider implements AIService {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly metadata = {
|
|
19
|
+
id: '',
|
|
20
|
+
name: 'Mock Provider',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
constructor(id: string) {
|
|
24
|
+
this.id = id;
|
|
25
|
+
this.metadata.id = id;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async checkHealth(): Promise<ProviderHealth> {
|
|
29
|
+
return {
|
|
30
|
+
healthy: true,
|
|
31
|
+
latency: Math.random() * 100,
|
|
32
|
+
lastChecked: new Date(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async chat(messages: ChatMessage[]): Promise<ChatResponse> {
|
|
37
|
+
const lastMessage = messages[messages.length - 1]?.content || '';
|
|
38
|
+
return {
|
|
39
|
+
content: `[Mock ${this.id}] Respuesta a: "${lastMessage}"`,
|
|
40
|
+
usage: {
|
|
41
|
+
promptTokens: 10,
|
|
42
|
+
completionTokens: 5,
|
|
43
|
+
totalTokens: 15,
|
|
44
|
+
},
|
|
45
|
+
model: 'mock-model',
|
|
46
|
+
finishReason: 'stop',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async chatStream(messages: ChatMessage[]): Promise<ReadableStream<ChatChunk>> {
|
|
51
|
+
const lastMessage = messages[messages.length - 1]?.content || '';
|
|
52
|
+
const words = `[Mock ${this.id}] Respuesta a: "${lastMessage}"`.split(' ');
|
|
53
|
+
|
|
54
|
+
return new ReadableStream({
|
|
55
|
+
async start(controller) {
|
|
56
|
+
for (const word of words) {
|
|
57
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
58
|
+
controller.enqueue({
|
|
59
|
+
content: word + ' ',
|
|
60
|
+
done: false,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
controller.enqueue({
|
|
64
|
+
content: '',
|
|
65
|
+
done: true,
|
|
66
|
+
finishReason: 'stop',
|
|
67
|
+
});
|
|
68
|
+
controller.close();
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function main() {
|
|
75
|
+
console.log('🧪 Prueba con proveedores Mock\n');
|
|
76
|
+
|
|
77
|
+
// Test 1: Round-Robin Strategy
|
|
78
|
+
console.log('📋 Test 1: Round-Robin Strategy');
|
|
79
|
+
console.log('─────────────────────────────────');
|
|
80
|
+
const roundRobinStrategy = new RoundRobinStrategy();
|
|
81
|
+
const orchestrator1 = new Orchestrator(roundRobinStrategy);
|
|
82
|
+
|
|
83
|
+
orchestrator1.registerProvider(new MockProvider('mock-1'));
|
|
84
|
+
orchestrator1.registerProvider(new MockProvider('mock-2'));
|
|
85
|
+
orchestrator1.registerProvider(new MockProvider('mock-3'));
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < 5; i++) {
|
|
88
|
+
const provider = await orchestrator1.selectProvider();
|
|
89
|
+
console.log(` Request ${i + 1}: ${provider?.id}`);
|
|
90
|
+
}
|
|
91
|
+
console.log('');
|
|
92
|
+
|
|
93
|
+
// Test 2: Priority Strategy
|
|
94
|
+
console.log('📋 Test 2: Priority Strategy');
|
|
95
|
+
console.log('─────────────────────────────────');
|
|
96
|
+
const priorityStrategy = new PriorityStrategy({
|
|
97
|
+
priorities: {
|
|
98
|
+
'mock-1': 1,
|
|
99
|
+
'mock-2': 2,
|
|
100
|
+
'mock-3': 3,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const orchestrator2 = new Orchestrator(priorityStrategy);
|
|
104
|
+
|
|
105
|
+
orchestrator2.registerProvider(new MockProvider('mock-1'));
|
|
106
|
+
orchestrator2.registerProvider(new MockProvider('mock-2'));
|
|
107
|
+
orchestrator2.registerProvider(new MockProvider('mock-3'));
|
|
108
|
+
|
|
109
|
+
for (let i = 0; i < 3; i++) {
|
|
110
|
+
const provider = await orchestrator2.selectProvider();
|
|
111
|
+
console.log(` Request ${i + 1}: ${provider?.id} (siempre el mismo por prioridad)`);
|
|
112
|
+
}
|
|
113
|
+
console.log('');
|
|
114
|
+
|
|
115
|
+
// Test 3: Fallback Strategy
|
|
116
|
+
console.log('📋 Test 3: Fallback Strategy');
|
|
117
|
+
console.log('─────────────────────────────────');
|
|
118
|
+
const fallbackStrategy = new FallbackStrategy({
|
|
119
|
+
order: ['mock-1', 'mock-2', 'mock-3'],
|
|
120
|
+
});
|
|
121
|
+
const orchestrator3 = new Orchestrator(fallbackStrategy);
|
|
122
|
+
|
|
123
|
+
orchestrator3.registerProvider(new MockProvider('mock-1'));
|
|
124
|
+
orchestrator3.registerProvider(new MockProvider('mock-2'));
|
|
125
|
+
orchestrator3.registerProvider(new MockProvider('mock-3'));
|
|
126
|
+
|
|
127
|
+
const provider = await orchestrator3.selectProvider();
|
|
128
|
+
console.log(` Seleccionado: ${provider?.id} (siempre el primero de la lista)`);
|
|
129
|
+
console.log('');
|
|
130
|
+
|
|
131
|
+
// Test 4: Chat real con mock
|
|
132
|
+
console.log('📋 Test 4: Chat con Mock Provider');
|
|
133
|
+
console.log('─────────────────────────────────');
|
|
134
|
+
const orchestrator4 = new Orchestrator(new RoundRobinStrategy());
|
|
135
|
+
orchestrator4.registerProvider(new MockProvider('mock-1'));
|
|
136
|
+
orchestrator4.registerProvider(new MockProvider('mock-2'));
|
|
137
|
+
|
|
138
|
+
const response = await orchestrator4.chat([
|
|
139
|
+
{ role: 'user', content: 'Hola, ¿cómo estás?' },
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
console.log(` Respuesta: ${response.content}`);
|
|
143
|
+
console.log(` Tokens: ${response.usage?.totalTokens}`);
|
|
144
|
+
console.log('');
|
|
145
|
+
|
|
146
|
+
// Test 5: Streaming con mock
|
|
147
|
+
console.log('📋 Test 5: Streaming con Mock Provider');
|
|
148
|
+
console.log('─────────────────────────────────');
|
|
149
|
+
const stream = await orchestrator4.chatStream([
|
|
150
|
+
{ role: 'user', content: 'Cuenta hasta 3' },
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
const reader = stream.getReader();
|
|
154
|
+
process.stdout.write(' Stream: ');
|
|
155
|
+
while (true) {
|
|
156
|
+
const { done, value } = await reader.read();
|
|
157
|
+
if (done) break;
|
|
158
|
+
process.stdout.write(value.content);
|
|
159
|
+
}
|
|
160
|
+
console.log('\n');
|
|
161
|
+
|
|
162
|
+
// Cleanup
|
|
163
|
+
orchestrator1.dispose();
|
|
164
|
+
orchestrator2.dispose();
|
|
165
|
+
orchestrator3.dispose();
|
|
166
|
+
orchestrator4.dispose();
|
|
167
|
+
|
|
168
|
+
console.log('✅ Todas las pruebas completadas!');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
main().catch(console.error);
|
|
172
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"extends": [
|
|
4
|
+
"eslint:recommended",
|
|
5
|
+
"plugin:@typescript-eslint/recommended"
|
|
6
|
+
],
|
|
7
|
+
"parserOptions": {
|
|
8
|
+
"ecmaVersion": 2022,
|
|
9
|
+
"sourceType": "module"
|
|
10
|
+
},
|
|
11
|
+
"env": {
|
|
12
|
+
"node": true,
|
|
13
|
+
"es2022": true
|
|
14
|
+
},
|
|
15
|
+
"rules": {
|
|
16
|
+
"@typescript-eslint/no-unused-vars": [
|
|
17
|
+
"error",
|
|
18
|
+
{
|
|
19
|
+
"argsIgnorePattern": "^_",
|
|
20
|
+
"varsIgnorePattern": "^_"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"@typescript-eslint/explicit-module-boundary-types": "off",
|
|
24
|
+
"@typescript-eslint/no-explicit-any": "warn"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Todos los cambios notables en este proyecto serán documentados en este archivo.
|
|
4
|
+
|
|
5
|
+
El formato está basado en [Keep a Changelog](https://keepachangelog.com/es-ES/1.0.0/),
|
|
6
|
+
y este proyecto adhiere a [Semantic Versioning](https://semver.org/lang/es/).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2024-12-XX
|
|
9
|
+
|
|
10
|
+
### Agregado
|
|
11
|
+
- Framework completo de orquestación de IA
|
|
12
|
+
- Soporte para múltiples proveedores: Groq, OpenRouter, Gemini, Local
|
|
13
|
+
- 5 estrategias de selección: Round-Robin, Priority, Fallback, Weighted, Health-Aware
|
|
14
|
+
- Streaming nativo con ReadableStream
|
|
15
|
+
- Health checks automáticos con métricas de latencia
|
|
16
|
+
- Factory para creación declarativa
|
|
17
|
+
- Manejo de errores personalizado
|
|
18
|
+
- Validación de configuración
|
|
19
|
+
- Documentación completa (README, ARCHITECTURE, USAGE, CONTRIBUTING)
|
|
20
|
+
- Ejemplos de uso
|
|
21
|
+
- Tests básicos
|
|
22
|
+
- Compatibilidad con Node.js y Bun
|
|
23
|
+
|
|
24
|
+
### Características Principales
|
|
25
|
+
- Arquitectura basada en plugins (extensible sin modificar el core)
|
|
26
|
+
- API declarativa y programática
|
|
27
|
+
- Type-safe con TypeScript
|
|
28
|
+
- Fallback automático entre proveedores
|
|
29
|
+
- Soporte para cost-aware selection
|
|
30
|
+
- Health-aware selection con métricas en tiempo real
|
|
31
|
+
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Guía de Contribución
|
|
2
|
+
|
|
3
|
+
## Estructura del Proyecto
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
src/
|
|
7
|
+
├── core/ # Núcleo del framework (interfaces, tipos, orchestrator)
|
|
8
|
+
├── providers/ # Implementaciones de proveedores
|
|
9
|
+
├── strategies/ # Estrategias de selección
|
|
10
|
+
├── factory/ # Factory para creación declarativa
|
|
11
|
+
└── index.ts # Punto de entrada principal
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Agregar un Nuevo Proveedor
|
|
15
|
+
|
|
16
|
+
1. **Crear el archivo del proveedor** en `src/providers/`:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { BaseProvider } from './base.js';
|
|
20
|
+
import type {
|
|
21
|
+
ChatMessage,
|
|
22
|
+
ChatOptions,
|
|
23
|
+
ChatResponse,
|
|
24
|
+
ChatChunk,
|
|
25
|
+
ProviderHealth,
|
|
26
|
+
ProviderMetadata,
|
|
27
|
+
} from '../core/types.js';
|
|
28
|
+
|
|
29
|
+
export interface CustomProviderConfig {
|
|
30
|
+
id: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
// ... otros campos específicos
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class CustomProvider extends BaseProvider {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly metadata: ProviderMetadata;
|
|
38
|
+
|
|
39
|
+
constructor(config: CustomProviderConfig) {
|
|
40
|
+
super();
|
|
41
|
+
// Inicialización
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async checkHealth(): Promise<ProviderHealth> {
|
|
45
|
+
// Implementar health check
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async chat(messages: ChatMessage[], options?: ChatOptions): Promise<ChatResponse> {
|
|
49
|
+
// Implementar chat
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async chatStream(messages: ChatMessage[], options?: ChatOptions): Promise<ReadableStream<ChatChunk>> {
|
|
53
|
+
// Implementar streaming
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected formatMessages(messages: ChatMessage[]): unknown {
|
|
57
|
+
// Convertir formato estándar a formato del proveedor
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected parseResponse(response: unknown): ChatResponse {
|
|
61
|
+
// Convertir respuesta a formato estándar
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
protected parseStream(stream: ReadableStream<unknown>): ReadableStream<ChatChunk> {
|
|
65
|
+
// Convertir stream a formato estándar
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
2. **Exportar en `src/providers/index.ts`**:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
export { CustomProvider } from './custom.js';
|
|
74
|
+
export type { CustomProviderConfig } from './custom.js';
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. **Registrar en `src/factory/index.ts`**:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
import { CustomProvider, type CustomProviderConfig } from '../providers/index.js';
|
|
81
|
+
|
|
82
|
+
// En la función createProvider:
|
|
83
|
+
case 'custom':
|
|
84
|
+
return new CustomProvider({
|
|
85
|
+
id,
|
|
86
|
+
...(rest as CustomProviderConfig),
|
|
87
|
+
} as CustomProviderConfig);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Agregar una Nueva Estrategia
|
|
91
|
+
|
|
92
|
+
1. **Crear el archivo de la estrategia** en `src/strategies/`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { BaseStrategy } from './base.js';
|
|
96
|
+
import type { AIService, SelectionContext } from '../core/interfaces.js';
|
|
97
|
+
|
|
98
|
+
export interface CustomStrategyConfig {
|
|
99
|
+
// Configuración específica
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class CustomStrategy extends BaseStrategy {
|
|
103
|
+
constructor(config?: CustomStrategyConfig) {
|
|
104
|
+
super();
|
|
105
|
+
// Inicialización
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async select(
|
|
109
|
+
providers: AIService[],
|
|
110
|
+
context?: SelectionContext
|
|
111
|
+
): Promise<AIService | null> {
|
|
112
|
+
// Implementar lógica de selección
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
update?(provider: AIService, success: boolean, metadata?: unknown): void {
|
|
116
|
+
// Opcional: actualizar estado interno
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
2. **Exportar en `src/strategies/index.ts`**
|
|
122
|
+
|
|
123
|
+
3. **Registrar en `src/factory/index.ts`**
|
|
124
|
+
|
|
125
|
+
## Convenciones de Código
|
|
126
|
+
|
|
127
|
+
- **TypeScript estricto**: Usar tipos explícitos
|
|
128
|
+
- **Nombres descriptivos**: Variables y funciones con nombres claros
|
|
129
|
+
- **Documentación**: JSDoc para funciones públicas
|
|
130
|
+
- **Manejo de errores**: Usar clases de error personalizadas
|
|
131
|
+
- **Tests**: Agregar tests para nuevas funcionalidades
|
|
132
|
+
|
|
133
|
+
## Testing
|
|
134
|
+
|
|
135
|
+
Los tests están en `tests/`. Usar Bun test runner:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
bun test tests/
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Versionado
|
|
142
|
+
|
|
143
|
+
Seguir [Semantic Versioning](https://semver.org/):
|
|
144
|
+
- **MAJOR**: Cambios incompatibles en la API
|
|
145
|
+
- **MINOR**: Nueva funcionalidad compatible hacia atrás
|
|
146
|
+
- **PATCH**: Correcciones de bugs compatibles
|
|
147
|
+
|
|
148
|
+
## Pull Requests
|
|
149
|
+
|
|
150
|
+
1. Crear una rama desde `main`
|
|
151
|
+
2. Implementar cambios
|
|
152
|
+
3. Agregar tests
|
|
153
|
+
4. Actualizar documentación si es necesario
|
|
154
|
+
5. Asegurar que todos los tests pasen
|
|
155
|
+
6. Crear PR con descripción clara
|
|
156
|
+
|