@elevasis/core 0.46.0 → 0.48.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/auth/index.d.ts +132 -0
- package/dist/knowledge/index.d.ts +2 -1
- package/dist/knowledge/index.js +17 -1
- package/dist/test-utils/index.d.ts +132 -0
- package/package.json +4 -3
- package/src/__tests__/observability-exports.test.ts +27 -22
- package/src/__tests__/published-artifact.test.ts +99 -0
- package/src/execution/engine/llm/__tests__/model-info.test.ts +82 -9
- package/src/execution/engine/llm/__tests__/model-validation.test.ts +82 -17
- package/src/execution/engine/llm/adapters/__tests__/anthropic-adapter.test.ts +108 -87
- package/src/execution/engine/llm/adapters/server/anthropic.ts +38 -24
- package/src/execution/engine/llm/model-info.ts +113 -43
- package/src/knowledge/__tests__/queries.test.ts +8 -0
- package/src/knowledge/published.ts +1 -1
- package/src/operations/index.ts +18 -13
- package/src/operations/public-agent-chat/api-schemas.ts +40 -0
- package/src/operations/public-agent-chat/index.ts +12 -0
- package/src/operations/sessions/api-schemas.ts +31 -11
- package/src/operations/sessions/index.ts +14 -12
- package/src/platform/constants/versions.ts +1 -1
- package/src/supabase/database.types.ts +132 -0
|
@@ -71,20 +71,47 @@ describe('validateModelConfig', () => {
|
|
|
71
71
|
}).not.toThrow()
|
|
72
72
|
})
|
|
73
73
|
|
|
74
|
-
it('passes for config without topP', () => {
|
|
75
|
-
expect(() => {
|
|
76
|
-
validateModelConfig({
|
|
77
|
-
model: 'gpt-5',
|
|
78
|
-
provider: 'openai',
|
|
74
|
+
it('passes for config without topP', () => {
|
|
75
|
+
expect(() => {
|
|
76
|
+
validateModelConfig({
|
|
77
|
+
model: 'gpt-5',
|
|
78
|
+
provider: 'openai',
|
|
79
79
|
apiKey: 'test-key',
|
|
80
80
|
temperature: 1,
|
|
81
81
|
maxOutputTokens: 8000
|
|
82
|
-
})
|
|
83
|
-
}).not.toThrow()
|
|
84
|
-
})
|
|
85
|
-
|
|
82
|
+
})
|
|
83
|
+
}).not.toThrow()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it.each(['claude-opus-4-8', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'claude-haiku-4-5'] as const)(
|
|
87
|
+
'passes for Anthropic %s',
|
|
88
|
+
(model) => {
|
|
89
|
+
expect(() => {
|
|
90
|
+
validateModelConfig({
|
|
91
|
+
model,
|
|
92
|
+
provider: 'anthropic',
|
|
93
|
+
apiKey: 'test-key',
|
|
94
|
+
maxOutputTokens: 4000
|
|
95
|
+
})
|
|
96
|
+
}).not.toThrow()
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
it('passes for Sonnet 4.6 with sampling parameters', () => {
|
|
101
|
+
expect(() => {
|
|
102
|
+
validateModelConfig({
|
|
103
|
+
model: 'claude-sonnet-4-6',
|
|
104
|
+
provider: 'anthropic',
|
|
105
|
+
apiKey: 'test-key',
|
|
106
|
+
temperature: 0.7,
|
|
107
|
+
topP: 0.9,
|
|
108
|
+
maxOutputTokens: 4000
|
|
109
|
+
})
|
|
110
|
+
}).not.toThrow()
|
|
111
|
+
})
|
|
112
|
+
})
|
|
86
113
|
|
|
87
|
-
describe('Invalid Temperature', () => {
|
|
114
|
+
describe('Invalid Temperature', () => {
|
|
88
115
|
it('throws for gpt-5 with temperature=0.7', () => {
|
|
89
116
|
expect(() => {
|
|
90
117
|
validateModelConfig({
|
|
@@ -109,9 +136,9 @@ describe('validateModelConfig', () => {
|
|
|
109
136
|
}).toThrow(ModelConfigError)
|
|
110
137
|
})
|
|
111
138
|
|
|
112
|
-
it('provides correct error details for invalid temperature', () => {
|
|
113
|
-
try {
|
|
114
|
-
validateModelConfig({
|
|
139
|
+
it('provides correct error details for invalid temperature', () => {
|
|
140
|
+
try {
|
|
141
|
+
validateModelConfig({
|
|
115
142
|
model: 'gpt-5',
|
|
116
143
|
provider: 'openai',
|
|
117
144
|
apiKey: 'test-key',
|
|
@@ -123,10 +150,48 @@ describe('validateModelConfig', () => {
|
|
|
123
150
|
expect(error).toBeInstanceOf(ModelConfigError)
|
|
124
151
|
const modelError = error as ModelConfigError
|
|
125
152
|
expect(modelError.field).toBe('temperature')
|
|
126
|
-
expect(modelError.model).toBe('gpt-5')
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
|
|
153
|
+
expect(modelError.model).toBe('gpt-5')
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('throws for Opus 4.8 with non-default temperature', () => {
|
|
158
|
+
expect(() => {
|
|
159
|
+
validateModelConfig({
|
|
160
|
+
model: 'claude-opus-4-8',
|
|
161
|
+
provider: 'anthropic',
|
|
162
|
+
apiKey: 'test-key',
|
|
163
|
+
temperature: 0.7,
|
|
164
|
+
maxOutputTokens: 4000
|
|
165
|
+
})
|
|
166
|
+
}).toThrow(ModelConfigError)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe('Invalid Anthropic Sampling Parameters', () => {
|
|
171
|
+
it('throws for Opus 4.8 with non-default topP', () => {
|
|
172
|
+
expect(() => {
|
|
173
|
+
validateModelConfig({
|
|
174
|
+
model: 'claude-opus-4-8',
|
|
175
|
+
provider: 'anthropic',
|
|
176
|
+
apiKey: 'test-key',
|
|
177
|
+
topP: 0.9,
|
|
178
|
+
maxOutputTokens: 4000
|
|
179
|
+
})
|
|
180
|
+
}).toThrow(ModelConfigError)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('throws for Opus 4.8 with top_k', () => {
|
|
184
|
+
expect(() => {
|
|
185
|
+
validateModelConfig({
|
|
186
|
+
model: 'claude-opus-4-8',
|
|
187
|
+
provider: 'anthropic',
|
|
188
|
+
apiKey: 'test-key',
|
|
189
|
+
maxOutputTokens: 4000,
|
|
190
|
+
top_k: 40
|
|
191
|
+
} as unknown as Parameters<typeof validateModelConfig>[0])
|
|
192
|
+
}).toThrow(ModelConfigError)
|
|
193
|
+
})
|
|
194
|
+
})
|
|
130
195
|
|
|
131
196
|
describe('Invalid maxOutputTokens', () => {
|
|
132
197
|
it('throws for maxOutputTokens below minimum', () => {
|
|
@@ -81,17 +81,18 @@ describe('AnthropicAdapter', () => {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
describe('isAnthropicModel', () => {
|
|
85
|
-
it('returns true for claude models with anthropic provider', () => {
|
|
86
|
-
expect(isAnthropicModel('claude-opus-4-
|
|
87
|
-
expect(isAnthropicModel('claude-sonnet-4-
|
|
88
|
-
expect(isAnthropicModel('claude-haiku-4-5', 'anthropic')).toBe(true)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
expect(isAnthropicModel('claude-
|
|
94
|
-
|
|
84
|
+
describe('isAnthropicModel', () => {
|
|
85
|
+
it('returns true for claude models with anthropic provider', () => {
|
|
86
|
+
expect(isAnthropicModel('claude-opus-4-8', 'anthropic')).toBe(true)
|
|
87
|
+
expect(isAnthropicModel('claude-sonnet-4-6', 'anthropic')).toBe(true)
|
|
88
|
+
expect(isAnthropicModel('claude-haiku-4-5-20251001', 'anthropic')).toBe(true)
|
|
89
|
+
expect(isAnthropicModel('claude-haiku-4-5', 'anthropic')).toBe(true)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('returns false for claude models with other providers', () => {
|
|
93
|
+
expect(isAnthropicModel('claude-opus-4-8', 'openrouter')).toBe(false)
|
|
94
|
+
expect(isAnthropicModel('claude-sonnet-4-6', 'google')).toBe(false)
|
|
95
|
+
})
|
|
95
96
|
|
|
96
97
|
it('returns false for non-claude models', () => {
|
|
97
98
|
expect(isAnthropicModel('gpt-5', 'anthropic')).toBe(false)
|
|
@@ -101,10 +102,10 @@ describe('AnthropicAdapter', () => {
|
|
|
101
102
|
|
|
102
103
|
describe('Basic Configuration', () => {
|
|
103
104
|
it('creates adapter without model options', async () => {
|
|
104
|
-
const adapter = new AnthropicAdapter({
|
|
105
|
-
apiKey: 'test-key',
|
|
106
|
-
model: 'claude-sonnet-4-
|
|
107
|
-
})
|
|
105
|
+
const adapter = new AnthropicAdapter({
|
|
106
|
+
apiKey: 'test-key',
|
|
107
|
+
model: 'claude-sonnet-4-6'
|
|
108
|
+
})
|
|
108
109
|
|
|
109
110
|
mockStreamResponse(mockAnthropicResponse)
|
|
110
111
|
|
|
@@ -114,11 +115,11 @@ describe('AnthropicAdapter', () => {
|
|
|
114
115
|
})
|
|
115
116
|
|
|
116
117
|
it('creates adapter with model options', async () => {
|
|
117
|
-
const adapter = new AnthropicAdapter({
|
|
118
|
-
apiKey: 'test-key',
|
|
119
|
-
model: 'claude-opus-4-
|
|
120
|
-
modelOptions: {}
|
|
121
|
-
})
|
|
118
|
+
const adapter = new AnthropicAdapter({
|
|
119
|
+
apiKey: 'test-key',
|
|
120
|
+
model: 'claude-opus-4-8',
|
|
121
|
+
modelOptions: {}
|
|
122
|
+
})
|
|
122
123
|
|
|
123
124
|
mockStreamResponse(mockAnthropicResponse)
|
|
124
125
|
|
|
@@ -130,10 +131,10 @@ describe('AnthropicAdapter', () => {
|
|
|
130
131
|
|
|
131
132
|
describe('Message Translation', () => {
|
|
132
133
|
it('converts system messages to separate system field', async () => {
|
|
133
|
-
const adapter = new AnthropicAdapter({
|
|
134
|
-
apiKey: 'test-key',
|
|
135
|
-
model: 'claude-sonnet-4-
|
|
136
|
-
})
|
|
134
|
+
const adapter = new AnthropicAdapter({
|
|
135
|
+
apiKey: 'test-key',
|
|
136
|
+
model: 'claude-sonnet-4-6'
|
|
137
|
+
})
|
|
137
138
|
|
|
138
139
|
mockStreamResponse(mockAnthropicResponse)
|
|
139
140
|
|
|
@@ -157,10 +158,10 @@ describe('AnthropicAdapter', () => {
|
|
|
157
158
|
})
|
|
158
159
|
|
|
159
160
|
it('combines multiple system messages', async () => {
|
|
160
|
-
const adapter = new AnthropicAdapter({
|
|
161
|
-
apiKey: 'test-key',
|
|
162
|
-
model: 'claude-sonnet-4-
|
|
163
|
-
})
|
|
161
|
+
const adapter = new AnthropicAdapter({
|
|
162
|
+
apiKey: 'test-key',
|
|
163
|
+
model: 'claude-sonnet-4-6'
|
|
164
|
+
})
|
|
164
165
|
|
|
165
166
|
mockStreamResponse(mockAnthropicResponse)
|
|
166
167
|
|
|
@@ -179,10 +180,10 @@ describe('AnthropicAdapter', () => {
|
|
|
179
180
|
})
|
|
180
181
|
|
|
181
182
|
it('handles requests without system messages', async () => {
|
|
182
|
-
const adapter = new AnthropicAdapter({
|
|
183
|
-
apiKey: 'test-key',
|
|
184
|
-
model: 'claude-sonnet-4-
|
|
185
|
-
})
|
|
183
|
+
const adapter = new AnthropicAdapter({
|
|
184
|
+
apiKey: 'test-key',
|
|
185
|
+
model: 'claude-sonnet-4-6'
|
|
186
|
+
})
|
|
186
187
|
|
|
187
188
|
mockStreamResponse(mockAnthropicResponse)
|
|
188
189
|
|
|
@@ -197,10 +198,10 @@ describe('AnthropicAdapter', () => {
|
|
|
197
198
|
})
|
|
198
199
|
|
|
199
200
|
it('preserves user and assistant roles', async () => {
|
|
200
|
-
const adapter = new AnthropicAdapter({
|
|
201
|
-
apiKey: 'test-key',
|
|
202
|
-
model: 'claude-sonnet-4-
|
|
203
|
-
})
|
|
201
|
+
const adapter = new AnthropicAdapter({
|
|
202
|
+
apiKey: 'test-key',
|
|
203
|
+
model: 'claude-sonnet-4-6'
|
|
204
|
+
})
|
|
204
205
|
|
|
205
206
|
mockStreamResponse(mockAnthropicResponse)
|
|
206
207
|
|
|
@@ -223,10 +224,10 @@ describe('AnthropicAdapter', () => {
|
|
|
223
224
|
|
|
224
225
|
describe('Tool-based Structured Output', () => {
|
|
225
226
|
it('configures tool_use for structured output', async () => {
|
|
226
|
-
const adapter = new AnthropicAdapter({
|
|
227
|
-
apiKey: 'test-key',
|
|
228
|
-
model: 'claude-sonnet-4-
|
|
229
|
-
})
|
|
227
|
+
const adapter = new AnthropicAdapter({
|
|
228
|
+
apiKey: 'test-key',
|
|
229
|
+
model: 'claude-sonnet-4-6'
|
|
230
|
+
})
|
|
230
231
|
|
|
231
232
|
mockStreamResponse(mockAnthropicResponse)
|
|
232
233
|
|
|
@@ -244,10 +245,10 @@ describe('AnthropicAdapter', () => {
|
|
|
244
245
|
})
|
|
245
246
|
|
|
246
247
|
it('extracts response from tool_use block', async () => {
|
|
247
|
-
const adapter = new AnthropicAdapter({
|
|
248
|
-
apiKey: 'test-key',
|
|
249
|
-
model: 'claude-sonnet-4-
|
|
250
|
-
})
|
|
248
|
+
const adapter = new AnthropicAdapter({
|
|
249
|
+
apiKey: 'test-key',
|
|
250
|
+
model: 'claude-sonnet-4-6'
|
|
251
|
+
})
|
|
251
252
|
|
|
252
253
|
mockStreamResponse({
|
|
253
254
|
content: [
|
|
@@ -278,10 +279,10 @@ describe('AnthropicAdapter', () => {
|
|
|
278
279
|
})
|
|
279
280
|
|
|
280
281
|
it('throws error when no tool_use block in response', async () => {
|
|
281
|
-
const adapter = new AnthropicAdapter({
|
|
282
|
-
apiKey: 'test-key',
|
|
283
|
-
model: 'claude-sonnet-4-
|
|
284
|
-
})
|
|
282
|
+
const adapter = new AnthropicAdapter({
|
|
283
|
+
apiKey: 'test-key',
|
|
284
|
+
model: 'claude-sonnet-4-6'
|
|
285
|
+
})
|
|
285
286
|
|
|
286
287
|
mockStreamResponse({
|
|
287
288
|
content: [{ type: 'text', text: 'Hello!' }],
|
|
@@ -296,10 +297,10 @@ describe('AnthropicAdapter', () => {
|
|
|
296
297
|
|
|
297
298
|
describe('Usage Metadata', () => {
|
|
298
299
|
it('calculates total tokens from input + output', async () => {
|
|
299
|
-
const adapter = new AnthropicAdapter({
|
|
300
|
-
apiKey: 'test-key',
|
|
301
|
-
model: 'claude-sonnet-4-
|
|
302
|
-
})
|
|
300
|
+
const adapter = new AnthropicAdapter({
|
|
301
|
+
apiKey: 'test-key',
|
|
302
|
+
model: 'claude-sonnet-4-6'
|
|
303
|
+
})
|
|
303
304
|
|
|
304
305
|
mockStreamResponse({
|
|
305
306
|
content: [mockAnthropicResponse.content[0]],
|
|
@@ -319,10 +320,10 @@ describe('AnthropicAdapter', () => {
|
|
|
319
320
|
})
|
|
320
321
|
|
|
321
322
|
it('extracts token usage correctly', async () => {
|
|
322
|
-
const adapter = new AnthropicAdapter({
|
|
323
|
-
apiKey: 'test-key',
|
|
324
|
-
model: 'claude-sonnet-4-
|
|
325
|
-
})
|
|
323
|
+
const adapter = new AnthropicAdapter({
|
|
324
|
+
apiKey: 'test-key',
|
|
325
|
+
model: 'claude-sonnet-4-6'
|
|
326
|
+
})
|
|
326
327
|
|
|
327
328
|
mockStreamResponse(mockAnthropicResponse)
|
|
328
329
|
|
|
@@ -338,10 +339,10 @@ describe('AnthropicAdapter', () => {
|
|
|
338
339
|
|
|
339
340
|
describe('MaxTokens Configuration', () => {
|
|
340
341
|
it('passes maxOutputTokens to API', async () => {
|
|
341
|
-
const adapter = new AnthropicAdapter({
|
|
342
|
-
apiKey: 'test-key',
|
|
343
|
-
model: 'claude-sonnet-4-
|
|
344
|
-
})
|
|
342
|
+
const adapter = new AnthropicAdapter({
|
|
343
|
+
apiKey: 'test-key',
|
|
344
|
+
model: 'claude-sonnet-4-6'
|
|
345
|
+
})
|
|
345
346
|
|
|
346
347
|
mockStreamResponse(mockAnthropicResponse)
|
|
347
348
|
|
|
@@ -355,10 +356,10 @@ describe('AnthropicAdapter', () => {
|
|
|
355
356
|
})
|
|
356
357
|
|
|
357
358
|
it('uses default 4000 when maxOutputTokens not specified', async () => {
|
|
358
|
-
const adapter = new AnthropicAdapter({
|
|
359
|
-
apiKey: 'test-key',
|
|
360
|
-
model: 'claude-sonnet-4-
|
|
361
|
-
})
|
|
359
|
+
const adapter = new AnthropicAdapter({
|
|
360
|
+
apiKey: 'test-key',
|
|
361
|
+
model: 'claude-sonnet-4-6'
|
|
362
|
+
})
|
|
362
363
|
|
|
363
364
|
mockStreamResponse(mockAnthropicResponse)
|
|
364
365
|
|
|
@@ -373,12 +374,12 @@ describe('AnthropicAdapter', () => {
|
|
|
373
374
|
})
|
|
374
375
|
})
|
|
375
376
|
|
|
376
|
-
describe('Temperature and TopP', () => {
|
|
377
|
-
it('passes temperature to API', async () => {
|
|
378
|
-
const adapter = new AnthropicAdapter({
|
|
379
|
-
apiKey: 'test-key',
|
|
380
|
-
model: 'claude-sonnet-4-
|
|
381
|
-
})
|
|
377
|
+
describe('Temperature and TopP', () => {
|
|
378
|
+
it('passes temperature to API', async () => {
|
|
379
|
+
const adapter = new AnthropicAdapter({
|
|
380
|
+
apiKey: 'test-key',
|
|
381
|
+
model: 'claude-sonnet-4-6'
|
|
382
|
+
})
|
|
382
383
|
|
|
383
384
|
mockStreamResponse(mockAnthropicResponse)
|
|
384
385
|
|
|
@@ -392,10 +393,10 @@ describe('AnthropicAdapter', () => {
|
|
|
392
393
|
})
|
|
393
394
|
|
|
394
395
|
it('passes topP to API', async () => {
|
|
395
|
-
const adapter = new AnthropicAdapter({
|
|
396
|
-
apiKey: 'test-key',
|
|
397
|
-
model: 'claude-sonnet-4-
|
|
398
|
-
})
|
|
396
|
+
const adapter = new AnthropicAdapter({
|
|
397
|
+
apiKey: 'test-key',
|
|
398
|
+
model: 'claude-sonnet-4-6'
|
|
399
|
+
})
|
|
399
400
|
|
|
400
401
|
mockStreamResponse(mockAnthropicResponse)
|
|
401
402
|
|
|
@@ -404,17 +405,37 @@ describe('AnthropicAdapter', () => {
|
|
|
404
405
|
topP: 0.9
|
|
405
406
|
})
|
|
406
407
|
|
|
407
|
-
const callParams = mockMessagesStream.mock.calls[0][0]
|
|
408
|
-
expect(callParams.top_p).toBe(0.9)
|
|
409
|
-
})
|
|
410
|
-
|
|
408
|
+
const callParams = mockMessagesStream.mock.calls[0][0]
|
|
409
|
+
expect(callParams.top_p).toBe(0.9)
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it('omits sampling parameters for Opus 4.8', async () => {
|
|
413
|
+
const adapter = new AnthropicAdapter({
|
|
414
|
+
apiKey: 'test-key',
|
|
415
|
+
model: 'claude-opus-4-8'
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
mockStreamResponse(mockAnthropicResponse)
|
|
419
|
+
|
|
420
|
+
await adapter.generate({
|
|
421
|
+
...baseRequest,
|
|
422
|
+
temperature: 0.7,
|
|
423
|
+
topP: 0.9
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
const callParams = mockMessagesStream.mock.calls[0][0]
|
|
427
|
+
expect(callParams).not.toHaveProperty('temperature')
|
|
428
|
+
expect(callParams).not.toHaveProperty('top_p')
|
|
429
|
+
expect(callParams).not.toHaveProperty('top_k')
|
|
430
|
+
})
|
|
431
|
+
})
|
|
411
432
|
|
|
412
433
|
describe('Error Handling', () => {
|
|
413
434
|
it('propagates API errors', async () => {
|
|
414
|
-
const adapter = new AnthropicAdapter({
|
|
415
|
-
apiKey: 'test-key',
|
|
416
|
-
model: 'claude-sonnet-4-
|
|
417
|
-
})
|
|
435
|
+
const adapter = new AnthropicAdapter({
|
|
436
|
+
apiKey: 'test-key',
|
|
437
|
+
model: 'claude-sonnet-4-6'
|
|
438
|
+
})
|
|
418
439
|
|
|
419
440
|
const apiError = new Error('API rate limit exceeded')
|
|
420
441
|
mockStreamError(apiError)
|
|
@@ -425,10 +446,10 @@ describe('AnthropicAdapter', () => {
|
|
|
425
446
|
|
|
426
447
|
describe('Multiple Calls', () => {
|
|
427
448
|
it('reuses client across multiple generate calls', async () => {
|
|
428
|
-
const adapter = new AnthropicAdapter({
|
|
429
|
-
apiKey: 'test-key',
|
|
430
|
-
model: 'claude-
|
|
431
|
-
})
|
|
449
|
+
const adapter = new AnthropicAdapter({
|
|
450
|
+
apiKey: 'test-key',
|
|
451
|
+
model: 'claude-sonnet-4-6'
|
|
452
|
+
})
|
|
432
453
|
|
|
433
454
|
mockStreamResponsePersistent(mockAnthropicResponse)
|
|
434
455
|
|
|
@@ -443,7 +464,7 @@ describe('AnthropicAdapter', () => {
|
|
|
443
464
|
})
|
|
444
465
|
|
|
445
466
|
describe('Model Selection', () => {
|
|
446
|
-
it.each(['claude-opus-4-
|
|
467
|
+
it.each(['claude-opus-4-8', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001', 'claude-haiku-4-5'] as const)(
|
|
447
468
|
'supports %s model',
|
|
448
469
|
async (model) => {
|
|
449
470
|
const adapter = new AnthropicAdapter({
|
|
@@ -25,19 +25,34 @@ export interface AnthropicAdapterConfig {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
* Check if the model and provider combination is for Anthropic
|
|
29
|
-
*
|
|
30
|
-
* @param model - Model identifier (e.g., 'claude-opus-4-
|
|
31
|
-
* @param provider - Provider name
|
|
32
|
-
* @returns True if this is an Anthropic model configuration
|
|
33
|
-
*/
|
|
34
|
-
export function isAnthropicModel(model: LLMModel, provider: string): boolean {
|
|
35
|
-
return model.startsWith('claude-') && provider === 'anthropic'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
* Check if the model and provider combination is for Anthropic
|
|
29
|
+
*
|
|
30
|
+
* @param model - Model identifier (e.g., 'claude-opus-4-8')
|
|
31
|
+
* @param provider - Provider name
|
|
32
|
+
* @returns True if this is an Anthropic model configuration
|
|
33
|
+
*/
|
|
34
|
+
export function isAnthropicModel(model: LLMModel, provider: string): boolean {
|
|
35
|
+
return model.startsWith('claude-') && provider === 'anthropic'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function shouldOmitSamplingParameters(model: LLMModel): boolean {
|
|
39
|
+
return model === 'claude-opus-4-8'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getSamplingParameters(model: LLMModel, request: LLMGenerateRequest): { temperature?: number; top_p?: number } {
|
|
43
|
+
if (shouldOmitSamplingParameters(model)) {
|
|
44
|
+
return {}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
...(request.temperature !== undefined ? { temperature: request.temperature } : {}),
|
|
49
|
+
...(request.topP !== undefined ? { top_p: request.topP } : {})
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Anthropic Claude Adapter - Implements universal protocol
|
|
55
|
+
* Uses tool_use for structured output (Anthropic's recommended approach)
|
|
41
56
|
*
|
|
42
57
|
* Key differences from other adapters:
|
|
43
58
|
* - System messages go in separate `system` field (not in messages array)
|
|
@@ -87,10 +102,10 @@ export class AnthropicAdapter implements LLMAdapter {
|
|
|
87
102
|
// This guarantees schema compliance without requiring JSON mode
|
|
88
103
|
// Stream + finalMessage() to avoid SDK non-streaming timeout rejection
|
|
89
104
|
// at high max_tokens values (SDK calculates estimated time > 10 min → error)
|
|
90
|
-
const stream = client.messages.stream(
|
|
91
|
-
{
|
|
92
|
-
model: this.model,
|
|
93
|
-
max_tokens: request.maxOutputTokens || 4000, // Required for Anthropic
|
|
105
|
+
const stream = client.messages.stream(
|
|
106
|
+
{
|
|
107
|
+
model: this.model,
|
|
108
|
+
max_tokens: request.maxOutputTokens || 4000, // Required for Anthropic
|
|
94
109
|
system: systemContent,
|
|
95
110
|
messages: anthropicMessages,
|
|
96
111
|
tools: [
|
|
@@ -99,13 +114,12 @@ export class AnthropicAdapter implements LLMAdapter {
|
|
|
99
114
|
description: 'Return structured output matching the required schema',
|
|
100
115
|
input_schema: request.responseSchema as import('@anthropic-ai/sdk').Anthropic.Tool['input_schema']
|
|
101
116
|
}
|
|
102
|
-
],
|
|
103
|
-
tool_choice: { type: 'tool', name: 'structured_output' },
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
)
|
|
117
|
+
],
|
|
118
|
+
tool_choice: { type: 'tool', name: 'structured_output' },
|
|
119
|
+
...getSamplingParameters(this.model, request)
|
|
120
|
+
},
|
|
121
|
+
{ signal: composeSignal(DEFAULT_LLM_TIMEOUT, request.signal) }
|
|
122
|
+
)
|
|
109
123
|
const response = await stream.finalMessage()
|
|
110
124
|
|
|
111
125
|
// Extract structured output from tool_use block
|