@layer-ai/core 0.1.10 → 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.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/routes/complete.d.ts.map +1 -1
- package/dist/routes/complete.js +24 -4
- package/dist/routes/gates.d.ts.map +1 -1
- package/dist/routes/gates.js +8 -13
- package/dist/routes/v2/complete.d.ts +4 -0
- package/dist/routes/v2/complete.d.ts.map +1 -0
- package/dist/routes/v2/complete.js +214 -0
- package/dist/routes/v2/tests/test-complete-anthropic.d.ts +2 -0
- package/dist/routes/v2/tests/test-complete-anthropic.d.ts.map +1 -0
- package/dist/routes/v2/tests/test-complete-anthropic.js +132 -0
- package/dist/routes/v2/tests/test-complete-openai.d.ts +2 -0
- package/dist/routes/v2/tests/test-complete-openai.d.ts.map +1 -0
- package/dist/routes/v2/tests/test-complete-openai.js +178 -0
- package/dist/routes/v2/tests/test-complete-routing.d.ts +2 -0
- package/dist/routes/v2/tests/test-complete-routing.d.ts.map +1 -0
- package/dist/routes/v2/tests/test-complete-routing.js +192 -0
- package/dist/services/providers/anthropic-adapter.d.ts +12 -0
- package/dist/services/providers/anthropic-adapter.d.ts.map +1 -0
- package/dist/services/providers/anthropic-adapter.js +203 -0
- package/dist/services/providers/base-adapter.d.ts +1 -1
- package/dist/services/providers/base-adapter.d.ts.map +1 -1
- package/dist/services/providers/base-adapter.js +1 -1
- package/dist/services/providers/openai-adapter.d.ts +2 -2
- package/dist/services/providers/openai-adapter.d.ts.map +1 -1
- package/dist/services/providers/openai-adapter.js +15 -3
- package/dist/services/providers/tests/test-anthropic-adapter.d.ts +2 -0
- package/dist/services/providers/tests/test-anthropic-adapter.d.ts.map +1 -0
- package/dist/services/providers/tests/test-anthropic-adapter.js +104 -0
- package/dist/services/providers/tests/test-openai-adapter.d.ts +2 -0
- package/dist/services/providers/tests/test-openai-adapter.d.ts.map +1 -0
- package/dist/services/providers/tests/test-openai-adapter.js +118 -0
- package/package.json +2 -2
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// Test v2 complete route with OpenAI adapter
|
|
2
|
+
// This demonstrates LayerRequest format with OpenAI features
|
|
3
|
+
async function testBasicChat() {
|
|
4
|
+
console.log('Test 1: Basic chat completion with OpenAI\n');
|
|
5
|
+
const request = {
|
|
6
|
+
gate: 'test-gate',
|
|
7
|
+
model: 'gpt-4o-mini',
|
|
8
|
+
type: 'chat',
|
|
9
|
+
data: {
|
|
10
|
+
messages: [
|
|
11
|
+
{ role: 'user', content: 'Say "Hello from v2 API" and nothing else.' }
|
|
12
|
+
],
|
|
13
|
+
temperature: 0.7,
|
|
14
|
+
maxTokens: 20,
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
console.log('Request:', JSON.stringify(request, null, 2));
|
|
18
|
+
console.log('\nExpected response includes:');
|
|
19
|
+
console.log('- content: string');
|
|
20
|
+
console.log('- model: string');
|
|
21
|
+
console.log('- finishReason: "completed" | "length_limit" | "tool_call" | "filtered" | "error"');
|
|
22
|
+
console.log('- usage: { promptTokens, completionTokens, totalTokens }');
|
|
23
|
+
console.log('- cost: number');
|
|
24
|
+
console.log('- latencyMs: number');
|
|
25
|
+
}
|
|
26
|
+
async function testVision() {
|
|
27
|
+
console.log('\n\nTest 2: Vision with GPT-4o\n');
|
|
28
|
+
const request = {
|
|
29
|
+
gate: 'test-gate',
|
|
30
|
+
model: 'gpt-4o',
|
|
31
|
+
type: 'chat',
|
|
32
|
+
data: {
|
|
33
|
+
messages: [
|
|
34
|
+
{
|
|
35
|
+
role: 'user',
|
|
36
|
+
content: 'What color is the sky in this image?',
|
|
37
|
+
images: [{
|
|
38
|
+
url: 'https://images.unsplash.com/photo-1765202659641-9ad9facfe5cf?q=80&w=1364&auto=format&fit=crop&ixlib=rb-4.1.0',
|
|
39
|
+
detail: 'auto'
|
|
40
|
+
}]
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
maxTokens: 50,
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
console.log('Request includes image with detail level (auto/low/high)');
|
|
47
|
+
console.log('OpenAI supports image detail parameter for cost optimization');
|
|
48
|
+
}
|
|
49
|
+
async function testToolCalls() {
|
|
50
|
+
console.log('\n\nTest 3: Tool calls with GPT-4\n');
|
|
51
|
+
const request = {
|
|
52
|
+
gate: 'test-gate',
|
|
53
|
+
model: 'gpt-4o-mini',
|
|
54
|
+
type: 'chat',
|
|
55
|
+
data: {
|
|
56
|
+
messages: [
|
|
57
|
+
{ role: 'user', content: 'What is the weather in San Francisco?' }
|
|
58
|
+
],
|
|
59
|
+
tools: [
|
|
60
|
+
{
|
|
61
|
+
type: 'function',
|
|
62
|
+
function: {
|
|
63
|
+
name: 'get_weather',
|
|
64
|
+
description: 'Get the current weather for a location',
|
|
65
|
+
parameters: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: {
|
|
68
|
+
location: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: 'The city and state, e.g. San Francisco, CA',
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
required: ['location'],
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
toolChoice: 'auto',
|
|
79
|
+
maxTokens: 100,
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
console.log('Request includes tools array and toolChoice');
|
|
83
|
+
console.log('OpenAI will return toolCalls in response when needed');
|
|
84
|
+
}
|
|
85
|
+
async function testImageGeneration() {
|
|
86
|
+
console.log('\n\nTest 4: Image generation with DALL-E\n');
|
|
87
|
+
const request = {
|
|
88
|
+
gate: 'test-gate',
|
|
89
|
+
model: 'dall-e-3',
|
|
90
|
+
type: 'image',
|
|
91
|
+
data: {
|
|
92
|
+
prompt: 'A cute cat playing with yarn',
|
|
93
|
+
size: '1024x1024',
|
|
94
|
+
quality: 'standard',
|
|
95
|
+
count: 1,
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
console.log('Image generation request format');
|
|
99
|
+
console.log('Response will include images array with URLs');
|
|
100
|
+
console.log('This is a unique feature to v2 - multiple modalities in one endpoint');
|
|
101
|
+
}
|
|
102
|
+
async function testEmbeddings() {
|
|
103
|
+
console.log('\n\nTest 5: Text embeddings\n');
|
|
104
|
+
const request = {
|
|
105
|
+
gate: 'test-gate',
|
|
106
|
+
model: 'text-embedding-3-small',
|
|
107
|
+
type: 'embeddings',
|
|
108
|
+
data: {
|
|
109
|
+
input: 'The quick brown fox jumps over the lazy dog',
|
|
110
|
+
dimensions: 1536,
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
console.log('Embeddings request format');
|
|
114
|
+
console.log('Response will include embeddings array (number[][])');
|
|
115
|
+
console.log('Another unique v2 feature - embeddings through the same endpoint');
|
|
116
|
+
}
|
|
117
|
+
async function testTextToSpeech() {
|
|
118
|
+
console.log('\n\nTest 6: Text-to-speech\n');
|
|
119
|
+
const request = {
|
|
120
|
+
gate: 'test-gate',
|
|
121
|
+
model: 'tts-1',
|
|
122
|
+
type: 'tts',
|
|
123
|
+
data: {
|
|
124
|
+
input: 'Hello, this is a test of the text to speech system.',
|
|
125
|
+
voice: 'alloy',
|
|
126
|
+
responseFormat: 'mp3',
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
console.log('Text-to-speech request format');
|
|
130
|
+
console.log('Response will include audio with base64 data');
|
|
131
|
+
console.log('Yet another v2 exclusive - TTS through unified API');
|
|
132
|
+
}
|
|
133
|
+
async function testResponseFormat() {
|
|
134
|
+
console.log('\n\nTest 7: JSON response format\n');
|
|
135
|
+
const request = {
|
|
136
|
+
gate: 'test-gate',
|
|
137
|
+
model: 'gpt-4o-mini',
|
|
138
|
+
type: 'chat',
|
|
139
|
+
data: {
|
|
140
|
+
messages: [
|
|
141
|
+
{ role: 'user', content: 'Generate a JSON object with name and age fields for a person.' }
|
|
142
|
+
],
|
|
143
|
+
responseFormat: 'json_object',
|
|
144
|
+
maxTokens: 100,
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
console.log('Request with JSON response format');
|
|
148
|
+
console.log('OpenAI will ensure the response is valid JSON');
|
|
149
|
+
console.log('Available in v2 with responseFormat parameter');
|
|
150
|
+
}
|
|
151
|
+
async function runTests() {
|
|
152
|
+
console.log('='.repeat(60));
|
|
153
|
+
console.log('V2 Complete Route - OpenAI Feature Showcase');
|
|
154
|
+
console.log('='.repeat(60));
|
|
155
|
+
console.log('\nTo test manually:');
|
|
156
|
+
console.log('1. Start server: pnpm --filter @layer-ai/api dev');
|
|
157
|
+
console.log('2. Create test gate in database with OpenAI model');
|
|
158
|
+
console.log('3. POST to /v2/complete with LayerRequest format');
|
|
159
|
+
console.log('='.repeat(60));
|
|
160
|
+
await testBasicChat();
|
|
161
|
+
await testVision();
|
|
162
|
+
await testToolCalls();
|
|
163
|
+
await testImageGeneration();
|
|
164
|
+
await testEmbeddings();
|
|
165
|
+
await testTextToSpeech();
|
|
166
|
+
await testResponseFormat();
|
|
167
|
+
console.log('\n' + '='.repeat(60));
|
|
168
|
+
console.log('V2 API supports ALL modalities through one endpoint:');
|
|
169
|
+
console.log('- chat: Text completion and multimodal chat');
|
|
170
|
+
console.log('- image: DALL-E image generation');
|
|
171
|
+
console.log('- embeddings: Text embeddings');
|
|
172
|
+
console.log('- tts: Text-to-speech');
|
|
173
|
+
console.log('- video: (Future) Video generation');
|
|
174
|
+
console.log('\nAll with consistent LayerRequest/LayerResponse format');
|
|
175
|
+
console.log('='.repeat(60));
|
|
176
|
+
}
|
|
177
|
+
runTests();
|
|
178
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-complete-routing.d.ts","sourceRoot":"","sources":["../../../../src/routes/v2/tests/test-complete-routing.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Test v2 complete route with routing strategies
|
|
2
|
+
// Demonstrates fallback and round-robin across different providers
|
|
3
|
+
async function testFallbackRouting() {
|
|
4
|
+
console.log('Test 1: Fallback routing (Anthropic -> OpenAI)\n');
|
|
5
|
+
const request = {
|
|
6
|
+
gate: 'test-gate-with-fallback',
|
|
7
|
+
model: 'claude-sonnet-4-5-20250929', // Primary model
|
|
8
|
+
type: 'chat',
|
|
9
|
+
data: {
|
|
10
|
+
messages: [
|
|
11
|
+
{ role: 'user', content: 'Hello! Please respond.' }
|
|
12
|
+
],
|
|
13
|
+
maxTokens: 50,
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
console.log('Gate configuration should have:');
|
|
17
|
+
console.log('- model: claude-sonnet-4-5-20250929');
|
|
18
|
+
console.log('- routingStrategy: "fallback"');
|
|
19
|
+
console.log('- fallbackModels: ["gpt-4o-mini", "gpt-3.5-turbo"]');
|
|
20
|
+
console.log('\nBehavior:');
|
|
21
|
+
console.log('1. Try primary model (Claude Sonnet)');
|
|
22
|
+
console.log('2. If fails, try first fallback (GPT-4o-mini)');
|
|
23
|
+
console.log('3. If fails, try second fallback (GPT-3.5-turbo)');
|
|
24
|
+
console.log('4. Return first successful response');
|
|
25
|
+
console.log('\nResponse will include "model" field showing which model was used');
|
|
26
|
+
}
|
|
27
|
+
async function testRoundRobinRouting() {
|
|
28
|
+
console.log('\n\nTest 2: Round-robin routing across providers\n');
|
|
29
|
+
const request = {
|
|
30
|
+
gate: 'test-gate-with-round-robin',
|
|
31
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
32
|
+
type: 'chat',
|
|
33
|
+
data: {
|
|
34
|
+
messages: [
|
|
35
|
+
{ role: 'user', content: 'Tell me a fun fact.' }
|
|
36
|
+
],
|
|
37
|
+
maxTokens: 100,
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
console.log('Gate configuration should have:');
|
|
41
|
+
console.log('- model: claude-sonnet-4-5-20250929');
|
|
42
|
+
console.log('- routingStrategy: "round-robin"');
|
|
43
|
+
console.log('- fallbackModels: ["gpt-4o-mini", "gpt-4o"]');
|
|
44
|
+
console.log('\nBehavior:');
|
|
45
|
+
console.log('- Randomly selects one of: [Claude Sonnet, GPT-4o-mini, GPT-4o]');
|
|
46
|
+
console.log('- Distributes load across multiple models/providers');
|
|
47
|
+
console.log('- Useful for cost optimization and rate limit management');
|
|
48
|
+
console.log('\nResponse "model" field shows which model was randomly selected');
|
|
49
|
+
}
|
|
50
|
+
async function testCrossProviderFallback() {
|
|
51
|
+
console.log('\n\nTest 3: Cross-provider fallback with vision\n');
|
|
52
|
+
const request = {
|
|
53
|
+
gate: 'test-gate-vision-fallback',
|
|
54
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
55
|
+
type: 'chat',
|
|
56
|
+
data: {
|
|
57
|
+
messages: [
|
|
58
|
+
{
|
|
59
|
+
role: 'user',
|
|
60
|
+
content: 'What do you see in this image?',
|
|
61
|
+
images: [{
|
|
62
|
+
url: 'https://images.unsplash.com/photo-1765202659641-9ad9facfe5cf?q=80&w=1364&auto=format&fit=crop'
|
|
63
|
+
}]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
maxTokens: 100,
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
console.log('Gate configuration:');
|
|
70
|
+
console.log('- model: claude-sonnet-4-5-20250929 (Anthropic vision)');
|
|
71
|
+
console.log('- routingStrategy: "fallback"');
|
|
72
|
+
console.log('- fallbackModels: ["gpt-4o", "gpt-4o-mini"]');
|
|
73
|
+
console.log('\nUse case:');
|
|
74
|
+
console.log('- Try Anthropic vision first');
|
|
75
|
+
console.log('- If rate limited or error, fallback to OpenAI vision');
|
|
76
|
+
console.log('- Ensures high availability for vision workloads');
|
|
77
|
+
}
|
|
78
|
+
async function testToolCallsFallback() {
|
|
79
|
+
console.log('\n\nTest 4: Fallback routing with tool calls\n');
|
|
80
|
+
const request = {
|
|
81
|
+
gate: 'test-gate-tools-fallback',
|
|
82
|
+
model: 'gpt-4o-mini',
|
|
83
|
+
type: 'chat',
|
|
84
|
+
data: {
|
|
85
|
+
messages: [
|
|
86
|
+
{ role: 'user', content: 'What is the weather in Tokyo?' }
|
|
87
|
+
],
|
|
88
|
+
tools: [
|
|
89
|
+
{
|
|
90
|
+
type: 'function',
|
|
91
|
+
function: {
|
|
92
|
+
name: 'get_weather',
|
|
93
|
+
description: 'Get weather for a location',
|
|
94
|
+
parameters: {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {
|
|
97
|
+
location: { type: 'string', description: 'City name' },
|
|
98
|
+
},
|
|
99
|
+
required: ['location'],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
toolChoice: 'auto',
|
|
105
|
+
maxTokens: 100,
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
console.log('Gate configuration:');
|
|
109
|
+
console.log('- model: gpt-4o-mini');
|
|
110
|
+
console.log('- routingStrategy: "fallback"');
|
|
111
|
+
console.log('- fallbackModels: ["gpt-4o", "claude-sonnet-4-5-20250929"]');
|
|
112
|
+
console.log('\nBehavior:');
|
|
113
|
+
console.log('- All three models support function calling');
|
|
114
|
+
console.log('- Fallback works seamlessly across providers');
|
|
115
|
+
console.log('- Response includes toolCalls array from whichever model succeeded');
|
|
116
|
+
}
|
|
117
|
+
async function testCostOptimization() {
|
|
118
|
+
console.log('\n\nTest 5: Cost optimization with round-robin\n');
|
|
119
|
+
const request = {
|
|
120
|
+
gate: 'test-gate-cost-optimization',
|
|
121
|
+
model: 'gpt-4o-mini',
|
|
122
|
+
type: 'chat',
|
|
123
|
+
data: {
|
|
124
|
+
messages: [
|
|
125
|
+
{ role: 'user', content: 'Write a short poem about the ocean.' }
|
|
126
|
+
],
|
|
127
|
+
maxTokens: 150,
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
console.log('Gate configuration:');
|
|
131
|
+
console.log('- model: gpt-4o-mini ($0.15/$0.60 per 1M tokens)');
|
|
132
|
+
console.log('- routingStrategy: "round-robin"');
|
|
133
|
+
console.log('- fallbackModels: [');
|
|
134
|
+
console.log(' "gpt-3.5-turbo" ($0.50/$1.50 per 1M)');
|
|
135
|
+
console.log(' "claude-haiku-3-5-20241022" ($0.80/$4.00 per 1M)');
|
|
136
|
+
console.log(' ]');
|
|
137
|
+
console.log('\nUse case:');
|
|
138
|
+
console.log('- Distribute load across cheapest available models');
|
|
139
|
+
console.log('- Balance cost vs quality for non-critical workloads');
|
|
140
|
+
console.log('- Response includes "cost" field for tracking');
|
|
141
|
+
}
|
|
142
|
+
async function testProviderSpecificFeatures() {
|
|
143
|
+
console.log('\n\nTest 6: Mixing provider-specific features\n');
|
|
144
|
+
console.log('Scenario A: Start with Claude (better reasoning)');
|
|
145
|
+
console.log('Gate: model=claude-sonnet, fallback=[gpt-4o]');
|
|
146
|
+
console.log('Use for: Complex reasoning, analysis, code generation');
|
|
147
|
+
console.log('\nScenario B: Start with GPT-4o (multimodal)');
|
|
148
|
+
console.log('Gate: model=gpt-4o, fallback=[claude-sonnet]');
|
|
149
|
+
console.log('Use for: Image analysis, audio processing');
|
|
150
|
+
console.log('\nScenario C: Start with cheapest (GPT-3.5)');
|
|
151
|
+
console.log('Gate: model=gpt-3.5-turbo, fallback=[gpt-4o-mini, claude-haiku]');
|
|
152
|
+
console.log('Use for: Simple tasks, high volume workloads');
|
|
153
|
+
console.log('\nThe v2 API makes it easy to switch strategies per gate');
|
|
154
|
+
}
|
|
155
|
+
async function runTests() {
|
|
156
|
+
console.log('='.repeat(70));
|
|
157
|
+
console.log('V2 Complete Route - Routing Strategies with Mixed Models');
|
|
158
|
+
console.log('='.repeat(70));
|
|
159
|
+
console.log('\nRouting enables:');
|
|
160
|
+
console.log('- High availability (fallback to working models)');
|
|
161
|
+
console.log('- Cost optimization (round-robin across cheap models)');
|
|
162
|
+
console.log('- Provider redundancy (mix OpenAI + Anthropic)');
|
|
163
|
+
console.log('- Rate limit handling (switch when rate limited)');
|
|
164
|
+
console.log('='.repeat(70));
|
|
165
|
+
await testFallbackRouting();
|
|
166
|
+
await testRoundRobinRouting();
|
|
167
|
+
await testCrossProviderFallback();
|
|
168
|
+
await testToolCallsFallback();
|
|
169
|
+
await testCostOptimization();
|
|
170
|
+
await testProviderSpecificFeatures();
|
|
171
|
+
console.log('\n' + '='.repeat(70));
|
|
172
|
+
console.log('How to set up routing:');
|
|
173
|
+
console.log('\n1. Create a gate with routing configuration:');
|
|
174
|
+
console.log(' POST /v1/gates');
|
|
175
|
+
console.log(' {');
|
|
176
|
+
console.log(' "name": "my-gate",');
|
|
177
|
+
console.log(' "model": "claude-sonnet-4-5-20250929",');
|
|
178
|
+
console.log(' "routingStrategy": "fallback",');
|
|
179
|
+
console.log(' "fallbackModels": ["gpt-4o-mini", "gpt-3.5-turbo"]');
|
|
180
|
+
console.log(' }');
|
|
181
|
+
console.log('\n2. Use the gate in v2/complete:');
|
|
182
|
+
console.log(' POST /v2/complete');
|
|
183
|
+
console.log(' {');
|
|
184
|
+
console.log(' "gate": "my-gate",');
|
|
185
|
+
console.log(' "type": "chat",');
|
|
186
|
+
console.log(' "data": { "messages": [...] }');
|
|
187
|
+
console.log(' }');
|
|
188
|
+
console.log('\n3. Check response.model to see which model was used');
|
|
189
|
+
console.log('='.repeat(70));
|
|
190
|
+
}
|
|
191
|
+
runTests();
|
|
192
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseProviderAdapter } from './base-adapter.js';
|
|
2
|
+
import { LayerRequest, LayerResponse, Role, FinishReason, ToolChoice } from '@layer-ai/sdk';
|
|
3
|
+
export declare class AnthropicAdapter extends BaseProviderAdapter {
|
|
4
|
+
protected provider: string;
|
|
5
|
+
protected roleMappings: Record<Role, string>;
|
|
6
|
+
protected toolChoiceMappings: Record<string, string | object>;
|
|
7
|
+
protected finishReasonMappings: Record<string, FinishReason>;
|
|
8
|
+
protected mapToolChoice(choice: ToolChoice): string | object | undefined;
|
|
9
|
+
call(request: LayerRequest): Promise<LayerResponse>;
|
|
10
|
+
private handleChat;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=anthropic-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/anthropic-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAC;AAavB,qBAAa,gBAAiB,SAAQ,mBAAmB;IACvD,SAAS,CAAC,QAAQ,SAAe;IAEjC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAI3D;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM1D;IAEF,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS;IAalE,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;YAiB3C,UAAU;CA2JzB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { BaseProviderAdapter, ADAPTER_HANDLED } from './base-adapter.js';
|
|
3
|
+
let anthropic = null;
|
|
4
|
+
function getAnthropicClient() {
|
|
5
|
+
if (!anthropic) {
|
|
6
|
+
anthropic = new Anthropic({
|
|
7
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
return anthropic;
|
|
11
|
+
}
|
|
12
|
+
export class AnthropicAdapter extends BaseProviderAdapter {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.provider = 'anthropic';
|
|
16
|
+
this.roleMappings = {
|
|
17
|
+
system: ADAPTER_HANDLED, // Handled via system parameter
|
|
18
|
+
user: 'user',
|
|
19
|
+
assistant: 'assistant',
|
|
20
|
+
tool: 'user', // Tool results are user messages in Anthropic
|
|
21
|
+
function: 'user', // Function results are user messages in Anthropic
|
|
22
|
+
model: 'assistant',
|
|
23
|
+
developer: 'user',
|
|
24
|
+
};
|
|
25
|
+
this.toolChoiceMappings = {
|
|
26
|
+
auto: { type: 'auto' },
|
|
27
|
+
required: { type: 'any' },
|
|
28
|
+
none: { type: 'none' },
|
|
29
|
+
};
|
|
30
|
+
this.finishReasonMappings = {
|
|
31
|
+
end_turn: 'completed',
|
|
32
|
+
max_tokens: 'length_limit',
|
|
33
|
+
tool_use: 'tool_call',
|
|
34
|
+
stop_sequence: 'completed',
|
|
35
|
+
error_output: 'error',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
mapToolChoice(choice) {
|
|
39
|
+
// Handle object format: { type: 'function', function: { name: 'foo' } } -> { type: 'tool', name: 'foo' }
|
|
40
|
+
if (typeof choice === 'object' && choice.type === 'function') {
|
|
41
|
+
return {
|
|
42
|
+
type: 'tool',
|
|
43
|
+
name: choice.function.name,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// Handle string format using base mappings
|
|
47
|
+
return super.mapToolChoice(choice);
|
|
48
|
+
}
|
|
49
|
+
async call(request) {
|
|
50
|
+
switch (request.type) {
|
|
51
|
+
case 'chat':
|
|
52
|
+
return this.handleChat(request);
|
|
53
|
+
case 'image':
|
|
54
|
+
throw new Error('image generation not yet supported by LayerAI');
|
|
55
|
+
case 'embeddings':
|
|
56
|
+
throw new Error('embeddings not yet supported by LayerAI');
|
|
57
|
+
case 'tts':
|
|
58
|
+
throw new Error('tts generation not yet supported by LayerAI');
|
|
59
|
+
case 'video':
|
|
60
|
+
throw new Error('Video generation not yet supported by LayerAI');
|
|
61
|
+
default:
|
|
62
|
+
throw new Error(`Unknown modality: ${request.type}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async handleChat(request) {
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
const client = getAnthropicClient();
|
|
68
|
+
const { data: chat, model } = request;
|
|
69
|
+
if (!model) {
|
|
70
|
+
throw new Error('Model is required for chat completions');
|
|
71
|
+
}
|
|
72
|
+
const systemPrompt = chat.systemPrompt || undefined;
|
|
73
|
+
const messages = [];
|
|
74
|
+
for (const msg of chat.messages) {
|
|
75
|
+
// Skip system messages - they're handled via the system parameter
|
|
76
|
+
if (msg.role === 'system')
|
|
77
|
+
continue;
|
|
78
|
+
const role = this.mapRole(msg.role);
|
|
79
|
+
if (msg.images && msg.images.length > 0) {
|
|
80
|
+
const content = [];
|
|
81
|
+
if (msg.content) {
|
|
82
|
+
content.push({ type: 'text', text: msg.content });
|
|
83
|
+
}
|
|
84
|
+
for (const image of msg.images) {
|
|
85
|
+
if (image.url) {
|
|
86
|
+
content.push({
|
|
87
|
+
type: 'image',
|
|
88
|
+
source: {
|
|
89
|
+
type: 'url',
|
|
90
|
+
url: image.url,
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else if (image.base64) {
|
|
95
|
+
content.push({
|
|
96
|
+
type: 'image',
|
|
97
|
+
source: {
|
|
98
|
+
type: 'base64',
|
|
99
|
+
media_type: image.mimeType || 'image/jpeg',
|
|
100
|
+
data: image.base64
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
messages.push({ role: role, content });
|
|
106
|
+
}
|
|
107
|
+
else if (msg.toolCalls) {
|
|
108
|
+
const content = [];
|
|
109
|
+
if (msg.content) {
|
|
110
|
+
content.push({ type: 'text', text: msg.content });
|
|
111
|
+
}
|
|
112
|
+
for (const toolCall of msg.toolCalls) {
|
|
113
|
+
content.push({
|
|
114
|
+
type: 'tool_use',
|
|
115
|
+
id: toolCall.id,
|
|
116
|
+
name: toolCall.function.name,
|
|
117
|
+
input: JSON.parse(toolCall.function.arguments),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
messages.push({ role: 'assistant', content });
|
|
121
|
+
}
|
|
122
|
+
else if (msg.toolCallId) {
|
|
123
|
+
messages.push({
|
|
124
|
+
role: 'user',
|
|
125
|
+
content: [{
|
|
126
|
+
type: 'tool_result',
|
|
127
|
+
tool_use_id: msg.toolCallId,
|
|
128
|
+
content: msg.content || '',
|
|
129
|
+
}],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
messages.push({
|
|
134
|
+
role: role,
|
|
135
|
+
content: msg.content || '',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const anthropicRequest = {
|
|
140
|
+
model: model,
|
|
141
|
+
messages,
|
|
142
|
+
max_tokens: chat.maxTokens || 4096,
|
|
143
|
+
...(systemPrompt && { system: systemPrompt }),
|
|
144
|
+
...(chat.temperature !== undefined && { temperature: chat.temperature }),
|
|
145
|
+
...(chat.temperature === undefined && chat.topP !== undefined && { top_p: chat.topP }),
|
|
146
|
+
...(chat.stopSequences && { stop_sequences: chat.stopSequences }),
|
|
147
|
+
...(chat.tools && {
|
|
148
|
+
tools: chat.tools.map(tool => ({
|
|
149
|
+
name: tool.function.name,
|
|
150
|
+
description: tool.function.description,
|
|
151
|
+
input_schema: tool.function.parameters || { type: 'object', properties: {} },
|
|
152
|
+
})),
|
|
153
|
+
...(chat.toolChoice && { tool_choice: this.mapToolChoice(chat.toolChoice) }),
|
|
154
|
+
}),
|
|
155
|
+
};
|
|
156
|
+
const response = await client.messages.create(anthropicRequest);
|
|
157
|
+
// Extract text content
|
|
158
|
+
let textContent;
|
|
159
|
+
const textBlock = response.content.find(block => block.type === 'text');
|
|
160
|
+
if (textBlock && textBlock.type === 'text') {
|
|
161
|
+
textContent = textBlock.text;
|
|
162
|
+
}
|
|
163
|
+
// Extract tool calls
|
|
164
|
+
let toolCalls;
|
|
165
|
+
const toolUseBlocks = response.content.filter(block => block.type === 'tool_use');
|
|
166
|
+
if (toolUseBlocks.length > 0) {
|
|
167
|
+
toolCalls = toolUseBlocks
|
|
168
|
+
.map(block => {
|
|
169
|
+
if (block.type === 'tool_use') {
|
|
170
|
+
return {
|
|
171
|
+
id: block.id,
|
|
172
|
+
type: 'function',
|
|
173
|
+
function: {
|
|
174
|
+
name: block.name,
|
|
175
|
+
arguments: JSON.stringify(block.input),
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
})
|
|
181
|
+
.filter((call) => call !== undefined);
|
|
182
|
+
}
|
|
183
|
+
const promptTokens = response.usage.input_tokens;
|
|
184
|
+
const completionTokens = response.usage.output_tokens;
|
|
185
|
+
const totalTokens = promptTokens + completionTokens;
|
|
186
|
+
const cost = this.calculateCost(model, promptTokens, completionTokens);
|
|
187
|
+
return {
|
|
188
|
+
content: textContent,
|
|
189
|
+
toolCalls,
|
|
190
|
+
model: response.model,
|
|
191
|
+
finishReason: this.mapFinishReason(response.stop_reason || 'end_turn'),
|
|
192
|
+
rawFinishReason: response.stop_reason || undefined,
|
|
193
|
+
usage: {
|
|
194
|
+
promptTokens,
|
|
195
|
+
completionTokens,
|
|
196
|
+
totalTokens,
|
|
197
|
+
},
|
|
198
|
+
cost,
|
|
199
|
+
latencyMs: Date.now() - startTime,
|
|
200
|
+
raw: response,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LayerRequest, LayerResponse, Role, ImageDetail, ImageSize, ImageQuality, ImageStyle, VideoSize, AudioFormat, AudioMimeType, ImageMimeType, FinishReason, ToolChoice, EncodingFormat, ADAPTER_HANDLED } from '@layer-ai/sdk';
|
|
2
2
|
export { ADAPTER_HANDLED };
|
|
3
|
-
export declare abstract class
|
|
3
|
+
export declare abstract class BaseProviderAdapter {
|
|
4
4
|
protected abstract provider: string;
|
|
5
5
|
protected roleMappings?: Record<Role, string>;
|
|
6
6
|
protected imageDetailMappings?: Record<ImageDetail, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/base-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,eAAe,EAGhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,8BAAsB,
|
|
1
|
+
{"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/base-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,aAAa,EACb,aAAa,EACb,YAAY,EACZ,UAAU,EACV,cAAc,EACd,eAAe,EAGhB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,8BAAsB,mBAAmB;IACvC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAEpC,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,SAAS,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC5D,SAAS,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC;IAC/D,SAAS,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC9D,SAAS,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxD,SAAS,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC9D,SAAS,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAC1D,SAAS,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACxD,SAAS,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC5D,SAAS,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAChE,SAAS,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAChE,SAAS,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAElE,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAE5D,SAAS,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM;IAcrC,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS;IAQjE,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS;IAQ3D,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS;IAQpE,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS;IAQ9D,SAAS,CAAC,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS;IAQ3D,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS;IAQjE,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS;IAQvE,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS;IAQvE,SAAS,CAAC,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS;IAQvE,SAAS,CAAC,eAAe,CAAC,oBAAoB,EAAE,MAAM,GAAG,YAAY;IAQrE,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS;IAYxE,SAAS,CAAC,aAAa,CACrB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,GACvB,MAAM;CAMV"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BaseProviderAdapter } from './base-adapter.js';
|
|
2
2
|
import { LayerRequest, LayerResponse, Role, ImageDetail, ImageSize, ImageQuality, ImageStyle, VideoSize, AudioFormat, FinishReason } from '@layer-ai/sdk';
|
|
3
|
-
export declare class OpenAIAdapter extends
|
|
3
|
+
export declare class OpenAIAdapter extends BaseProviderAdapter {
|
|
4
4
|
protected provider: string;
|
|
5
5
|
protected roleMappings: Record<Role, string>;
|
|
6
6
|
protected imageDetailMappings: Record<ImageDetail, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/openai-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"openai-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/openai-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACb,MAAM,eAAe,CAAC;AAavB,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,SAAS,CAAC,QAAQ,SAAY;IAE9B,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAEF,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAIxD;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAK1D;IAEF,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAQpD;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAG1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAGtD;IAEF,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAKpD;IAEF,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAOxD;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;YAiB3C,UAAU;YAoGV,qBAAqB;YA6BrB,gBAAgB;YAiChB,kBAAkB;CA8BjC"}
|