@assemble-dev/providers 0.1.0 → 0.3.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.cjs +1751 -302
- package/dist/index.d.cts +252 -20
- package/dist/index.d.ts +252 -20
- package/dist/index.js +1707 -291
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -22,21 +22,41 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AnthropicMessagesResponseSchema: () => AnthropicMessagesResponseSchema,
|
|
24
24
|
AnthropicStreamEventSchema: () => AnthropicStreamEventSchema,
|
|
25
|
+
AzureOpenAiChatCompletionResponseSchema: () => AzureOpenAiChatCompletionResponseSchema,
|
|
26
|
+
ChatMessageCacheControlSchema: () => ChatMessageCacheControlSchema,
|
|
25
27
|
ChatMessageRoleSchema: () => ChatMessageRoleSchema,
|
|
26
28
|
ChatMessageSchema: () => ChatMessageSchema,
|
|
27
29
|
ChatStopReasonSchema: () => ChatStopReasonSchema,
|
|
28
30
|
MessageContentBlockSchema: () => MessageContentBlockSchema,
|
|
29
31
|
ModelToolCallSchema: () => ModelToolCallSchema,
|
|
30
32
|
anthropic: () => anthropic,
|
|
33
|
+
anthropicBatchJsonOnlySystemInstruction: () => anthropicBatchJsonOnlySystemInstruction,
|
|
34
|
+
anthropicBatchTraceWorkflowName: () => anthropicBatchTraceWorkflowName,
|
|
35
|
+
applyCacheControlToLastContentBlock: () => applyCacheControlToLastContentBlock,
|
|
36
|
+
awsSigV4Algorithm: () => awsSigV4Algorithm,
|
|
37
|
+
azureOpenai: () => azureOpenai,
|
|
38
|
+
bedrock: () => bedrock,
|
|
39
|
+
bedrockAnthropicVersion: () => bedrockAnthropicVersion,
|
|
40
|
+
buildAnthropicCacheControl: () => buildAnthropicCacheControl,
|
|
31
41
|
buildAnthropicRequestBody: () => buildAnthropicRequestBody,
|
|
42
|
+
buildAnthropicStructuredOutputJsonSchema: () => buildAnthropicStructuredOutputJsonSchema,
|
|
43
|
+
buildAzureOpenAiChatCompletionsUrl: () => buildAzureOpenAiChatCompletionsUrl,
|
|
44
|
+
buildAzureOpenAiChatRequestBody: () => buildAzureOpenAiChatRequestBody,
|
|
45
|
+
buildBedrockInvokeModelRequestBody: () => buildBedrockInvokeModelRequestBody,
|
|
46
|
+
buildBedrockInvokeModelUrl: () => buildBedrockInvokeModelUrl,
|
|
47
|
+
buildGeminiResponseSchema: () => buildGeminiResponseSchema,
|
|
32
48
|
buildOpenAiRequestBody: () => buildOpenAiRequestBody,
|
|
49
|
+
buildOpenAiStructuredOutputJsonSchema: () => buildOpenAiStructuredOutputJsonSchema,
|
|
33
50
|
buildProviderCanceledError: () => buildProviderCanceledError,
|
|
34
51
|
calculateProviderRetryDelayMs: () => calculateProviderRetryDelayMs,
|
|
52
|
+
convertZodSchemaToJsonSchema: () => convertZodSchemaToJsonSchema,
|
|
35
53
|
createAnthropicBatchClient: () => createAnthropicBatchClient,
|
|
36
54
|
defaultAnthropicBaseUrl: () => defaultAnthropicBaseUrl,
|
|
37
55
|
defaultAnthropicBatchPollIntervalMs: () => defaultAnthropicBatchPollIntervalMs,
|
|
38
56
|
defaultAnthropicBatchTimeoutMs: () => defaultAnthropicBatchTimeoutMs,
|
|
39
57
|
defaultAnthropicMaxOutputTokens: () => defaultAnthropicMaxOutputTokens,
|
|
58
|
+
defaultAzureOpenAiApiVersion: () => defaultAzureOpenAiApiVersion,
|
|
59
|
+
defaultBedrockMaxOutputTokens: () => defaultBedrockMaxOutputTokens,
|
|
40
60
|
defaultGeminiBaseUrl: () => defaultGeminiBaseUrl,
|
|
41
61
|
defaultOpenAiBaseUrl: () => defaultOpenAiBaseUrl,
|
|
42
62
|
defaultProviderRetryInitialDelayMs: () => defaultProviderRetryInitialDelayMs,
|
|
@@ -47,6 +67,7 @@ __export(index_exports, {
|
|
|
47
67
|
executeProviderCallWithModelFallback: () => executeProviderCallWithModelFallback,
|
|
48
68
|
executeProviderCallWithRetry: () => executeProviderCallWithRetry,
|
|
49
69
|
extractAnthropicTextContent: () => extractAnthropicTextContent,
|
|
70
|
+
extractAnthropicThinkingText: () => extractAnthropicThinkingText,
|
|
50
71
|
extractAnthropicToolCalls: () => extractAnthropicToolCalls,
|
|
51
72
|
extractMessageText: () => extractMessageText,
|
|
52
73
|
gemini: () => gemini,
|
|
@@ -55,19 +76,34 @@ __export(index_exports, {
|
|
|
55
76
|
joinSystemMessageText: () => joinSystemMessageText,
|
|
56
77
|
mapAnthropicResponseToChatCompletionResult: () => mapAnthropicResponseToChatCompletionResult,
|
|
57
78
|
mapAnthropicUsageToTokenUsage: () => mapAnthropicUsageToTokenUsage,
|
|
79
|
+
mapAzureOpenAiResponseToChatCompletionResult: () => mapAzureOpenAiResponseToChatCompletionResult,
|
|
80
|
+
mapAzureOpenAiUsageToTokenUsage: () => mapAzureOpenAiUsageToTokenUsage,
|
|
58
81
|
mapChatMessageToAnthropicRequestMessage: () => mapChatMessageToAnthropicRequestMessage,
|
|
59
82
|
mapChatMessageToOpenAiRequestMessage: () => mapChatMessageToOpenAiRequestMessage,
|
|
60
83
|
mapModelToolChoiceToAnthropicToolChoice: () => mapModelToolChoiceToAnthropicToolChoice,
|
|
61
84
|
mapModelToolChoiceToOpenAiToolChoice: () => mapModelToolChoiceToOpenAiToolChoice,
|
|
62
85
|
mapModelToolDefinitionToOpenAiTool: () => mapModelToolDefinitionToOpenAiTool,
|
|
63
86
|
mapModelToolDefinitionsToAnthropicTools: () => mapModelToolDefinitionsToAnthropicTools,
|
|
87
|
+
mergeProviderRequestExtraBody: () => mergeProviderRequestExtraBody,
|
|
64
88
|
mockModel: () => mockModel,
|
|
65
89
|
normalizeAnthropicStopReason: () => normalizeAnthropicStopReason,
|
|
90
|
+
normalizeAzureOpenAiFinishReason: () => normalizeAzureOpenAiFinishReason,
|
|
91
|
+
openAiStructuredOutputSchemaName: () => openAiStructuredOutputSchemaName,
|
|
66
92
|
openai: () => openai,
|
|
93
|
+
parseAnthropicBatchStructuredContent: () => parseAnthropicBatchStructuredContent,
|
|
67
94
|
parseAnthropicStreamEvents: () => parseAnthropicStreamEvents,
|
|
68
95
|
parseAnthropicToolInputJson: () => parseAnthropicToolInputJson,
|
|
96
|
+
parseAzureOpenAiStreamEvents: () => parseAzureOpenAiStreamEvents,
|
|
97
|
+
parseAzureOpenAiToolArgumentsJson: () => parseAzureOpenAiToolArgumentsJson,
|
|
98
|
+
resolveAnthropicPromptCachingSettings: () => resolveAnthropicPromptCachingSettings,
|
|
99
|
+
resolveBedrockCredentials: () => resolveBedrockCredentials,
|
|
69
100
|
resolveProviderApiKey: () => resolveProviderApiKey,
|
|
70
|
-
|
|
101
|
+
sanitizeJsonSchemaForAnthropicStructuredOutput: () => sanitizeJsonSchemaForAnthropicStructuredOutput,
|
|
102
|
+
sanitizeJsonSchemaForGeminiResponseSchema: () => sanitizeJsonSchemaForGeminiResponseSchema,
|
|
103
|
+
sanitizeJsonSchemaForOpenAiStructuredOutput: () => sanitizeJsonSchemaForOpenAiStructuredOutput,
|
|
104
|
+
signAwsRequestWithSigV4: () => signAwsRequestWithSigV4,
|
|
105
|
+
streamAnthropicChatCompletion: () => streamAnthropicChatCompletion,
|
|
106
|
+
streamAzureOpenAiChatCompletion: () => streamAzureOpenAiChatCompletion
|
|
71
107
|
});
|
|
72
108
|
module.exports = __toCommonJS(index_exports);
|
|
73
109
|
|
|
@@ -101,12 +137,16 @@ var ModelToolCallSchema = import_zod.z.object({
|
|
|
101
137
|
toolName: import_zod.z.string(),
|
|
102
138
|
input: import_json.JsonValueSchema
|
|
103
139
|
});
|
|
140
|
+
var ChatMessageCacheControlSchema = import_zod.z.object({
|
|
141
|
+
ttl: import_zod.z.enum(["5m", "1h"]).optional()
|
|
142
|
+
});
|
|
104
143
|
var ChatMessageSchema = import_zod.z.object({
|
|
105
144
|
role: ChatMessageRoleSchema,
|
|
106
145
|
content: import_zod.z.union([import_zod.z.string(), import_zod.z.array(MessageContentBlockSchema)]),
|
|
107
146
|
toolName: import_zod.z.string().optional(),
|
|
108
147
|
toolCallId: import_zod.z.string().optional(),
|
|
109
|
-
toolCalls: import_zod.z.array(ModelToolCallSchema).optional()
|
|
148
|
+
toolCalls: import_zod.z.array(ModelToolCallSchema).optional(),
|
|
149
|
+
cacheControl: ChatMessageCacheControlSchema.optional()
|
|
110
150
|
});
|
|
111
151
|
var ChatStopReasonSchema = import_zod.z.enum([
|
|
112
152
|
"end_turn",
|
|
@@ -122,14 +162,222 @@ function extractMessageText(message) {
|
|
|
122
162
|
}
|
|
123
163
|
|
|
124
164
|
// src/openai-adapter.ts
|
|
125
|
-
var
|
|
126
|
-
var
|
|
127
|
-
var
|
|
128
|
-
var
|
|
165
|
+
var import_zod4 = require("zod");
|
|
166
|
+
var import_errors16 = require("@assemble-dev/shared-types/errors");
|
|
167
|
+
var import_json4 = require("@assemble-dev/shared-types/json");
|
|
168
|
+
var import_errors17 = require("@assemble-dev/shared-utils/errors");
|
|
129
169
|
|
|
130
170
|
// src/openai-request-mapping.ts
|
|
171
|
+
var import_errors3 = require("@assemble-dev/shared-types/errors");
|
|
172
|
+
var import_errors4 = require("@assemble-dev/shared-utils/errors");
|
|
173
|
+
|
|
174
|
+
// src/structured-output-json-schema.ts
|
|
175
|
+
var import_zod2 = require("zod");
|
|
131
176
|
var import_errors = require("@assemble-dev/shared-types/errors");
|
|
177
|
+
var import_json2 = require("@assemble-dev/shared-types/json");
|
|
132
178
|
var import_errors2 = require("@assemble-dev/shared-utils/errors");
|
|
179
|
+
|
|
180
|
+
// src/json-schema-tree.ts
|
|
181
|
+
var schemaMapKeywords = ["properties", "$defs"];
|
|
182
|
+
var schemaListKeywords = ["anyOf", "oneOf", "allOf", "prefixItems"];
|
|
183
|
+
var schemaValueKeywords = [
|
|
184
|
+
"items",
|
|
185
|
+
"additionalProperties",
|
|
186
|
+
"not",
|
|
187
|
+
"if",
|
|
188
|
+
"then",
|
|
189
|
+
"else",
|
|
190
|
+
"contains",
|
|
191
|
+
"propertyNames"
|
|
192
|
+
];
|
|
193
|
+
function isJsonObject(value) {
|
|
194
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
195
|
+
}
|
|
196
|
+
function transformJsonSchemaNode(node, transformNode) {
|
|
197
|
+
const result = { ...transformNode({ ...node }) };
|
|
198
|
+
for (const keyword of schemaMapKeywords) {
|
|
199
|
+
const mapValue = result[keyword];
|
|
200
|
+
if (mapValue !== void 0 && isJsonObject(mapValue)) {
|
|
201
|
+
result[keyword] = transformJsonSchemaMap(mapValue, transformNode);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (const keyword of schemaListKeywords) {
|
|
205
|
+
const listValue = result[keyword];
|
|
206
|
+
if (Array.isArray(listValue)) {
|
|
207
|
+
result[keyword] = listValue.map(
|
|
208
|
+
(entry) => isJsonObject(entry) ? transformJsonSchemaNode(entry, transformNode) : entry
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
for (const keyword of schemaValueKeywords) {
|
|
213
|
+
const schemaValue = result[keyword];
|
|
214
|
+
if (schemaValue !== void 0 && isJsonObject(schemaValue)) {
|
|
215
|
+
result[keyword] = transformJsonSchemaNode(schemaValue, transformNode);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
function transformJsonSchemaMap(schemaMap, transformNode) {
|
|
221
|
+
const result = {};
|
|
222
|
+
for (const [schemaName, schemaValue] of Object.entries(schemaMap)) {
|
|
223
|
+
result[schemaName] = isJsonObject(schemaValue) ? transformJsonSchemaNode(schemaValue, transformNode) : schemaValue;
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
function inlineJsonSchemaReferences(root) {
|
|
228
|
+
const rootDefinitions = root["$defs"];
|
|
229
|
+
const definitions = rootDefinitions !== void 0 && isJsonObject(rootDefinitions) ? rootDefinitions : {};
|
|
230
|
+
const resolvedRoot = resolveJsonSchemaReferences(
|
|
231
|
+
root,
|
|
232
|
+
definitions,
|
|
233
|
+
/* @__PURE__ */ new Set()
|
|
234
|
+
);
|
|
235
|
+
return isJsonObject(resolvedRoot) ? resolvedRoot : {};
|
|
236
|
+
}
|
|
237
|
+
function resolveJsonSchemaReferences(value, definitions, activeReferences) {
|
|
238
|
+
if (Array.isArray(value)) {
|
|
239
|
+
return value.map(
|
|
240
|
+
(entry) => resolveJsonSchemaReferences(entry, definitions, activeReferences)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (!isJsonObject(value)) {
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
const referenceTarget = value["$ref"];
|
|
247
|
+
if (typeof referenceTarget === "string") {
|
|
248
|
+
return resolveJsonSchemaReferenceTarget(
|
|
249
|
+
referenceTarget,
|
|
250
|
+
definitions,
|
|
251
|
+
activeReferences
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
const result = {};
|
|
255
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
256
|
+
if (key !== "$defs") {
|
|
257
|
+
result[key] = resolveJsonSchemaReferences(
|
|
258
|
+
entryValue,
|
|
259
|
+
definitions,
|
|
260
|
+
activeReferences
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
function resolveJsonSchemaReferenceTarget(referenceTarget, definitions, activeReferences) {
|
|
267
|
+
if (activeReferences.has(referenceTarget)) {
|
|
268
|
+
return {};
|
|
269
|
+
}
|
|
270
|
+
const definitionsPrefix = "#/$defs/";
|
|
271
|
+
if (!referenceTarget.startsWith(definitionsPrefix)) {
|
|
272
|
+
return {};
|
|
273
|
+
}
|
|
274
|
+
const definition = definitions[referenceTarget.slice(definitionsPrefix.length)];
|
|
275
|
+
if (definition === void 0 || !isJsonObject(definition)) {
|
|
276
|
+
return {};
|
|
277
|
+
}
|
|
278
|
+
return resolveJsonSchemaReferences(
|
|
279
|
+
definition,
|
|
280
|
+
definitions,
|
|
281
|
+
/* @__PURE__ */ new Set([...activeReferences, referenceTarget])
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
function removeJsonSchemaKeywords(node, keywords) {
|
|
285
|
+
const result = {};
|
|
286
|
+
for (const [key, value] of Object.entries(node)) {
|
|
287
|
+
if (!keywords.includes(key)) {
|
|
288
|
+
result[key] = value;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return result;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/structured-output-json-schema.ts
|
|
295
|
+
var openAiStructuredOutputSchemaName = "structured_output";
|
|
296
|
+
var schemaMetadataKeywords = ["$schema", "$id"];
|
|
297
|
+
var anthropicUnsupportedSchemaKeywords = [
|
|
298
|
+
...schemaMetadataKeywords,
|
|
299
|
+
"minimum",
|
|
300
|
+
"maximum",
|
|
301
|
+
"exclusiveMinimum",
|
|
302
|
+
"exclusiveMaximum",
|
|
303
|
+
"minLength",
|
|
304
|
+
"maxLength",
|
|
305
|
+
"multipleOf"
|
|
306
|
+
];
|
|
307
|
+
var openAiUnsupportedSchemaKeywords = [
|
|
308
|
+
...anthropicUnsupportedSchemaKeywords,
|
|
309
|
+
"pattern",
|
|
310
|
+
"format",
|
|
311
|
+
"minItems",
|
|
312
|
+
"maxItems",
|
|
313
|
+
"uniqueItems",
|
|
314
|
+
"default"
|
|
315
|
+
];
|
|
316
|
+
function convertZodSchemaToJsonSchema(schema) {
|
|
317
|
+
try {
|
|
318
|
+
return import_json2.JsonObjectSchema.parse(import_zod2.z.toJSONSchema(schema, { io: "input" }));
|
|
319
|
+
} catch (error) {
|
|
320
|
+
throw new import_errors2.AppError({
|
|
321
|
+
code: import_errors.AppErrorCode.BadRequest,
|
|
322
|
+
message: "Structured output schema could not be converted to JSON Schema",
|
|
323
|
+
statusCode: 400,
|
|
324
|
+
cause: error instanceof Error ? error : void 0
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function buildAnthropicStructuredOutputJsonSchema(schema) {
|
|
329
|
+
return sanitizeJsonSchemaForAnthropicStructuredOutput(
|
|
330
|
+
convertZodSchemaToJsonSchema(schema)
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
function sanitizeJsonSchemaForAnthropicStructuredOutput(jsonSchema) {
|
|
334
|
+
return transformJsonSchemaNode(
|
|
335
|
+
inlineJsonSchemaReferences(jsonSchema),
|
|
336
|
+
sanitizeAnthropicSchemaNode
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
function buildOpenAiStructuredOutputJsonSchema(schema) {
|
|
340
|
+
return sanitizeJsonSchemaForOpenAiStructuredOutput(
|
|
341
|
+
convertZodSchemaToJsonSchema(schema)
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
function sanitizeJsonSchemaForOpenAiStructuredOutput(jsonSchema) {
|
|
345
|
+
return transformJsonSchemaNode(jsonSchema, sanitizeOpenAiSchemaNode);
|
|
346
|
+
}
|
|
347
|
+
function sanitizeAnthropicSchemaNode(node) {
|
|
348
|
+
const sanitized = removeJsonSchemaKeywords(
|
|
349
|
+
node,
|
|
350
|
+
anthropicUnsupportedSchemaKeywords
|
|
351
|
+
);
|
|
352
|
+
if (sanitized["type"] === "object") {
|
|
353
|
+
sanitized["additionalProperties"] = false;
|
|
354
|
+
}
|
|
355
|
+
return sanitized;
|
|
356
|
+
}
|
|
357
|
+
function sanitizeOpenAiSchemaNode(node) {
|
|
358
|
+
const sanitized = removeJsonSchemaKeywords(
|
|
359
|
+
node,
|
|
360
|
+
openAiUnsupportedSchemaKeywords
|
|
361
|
+
);
|
|
362
|
+
if (sanitized["type"] !== "object") {
|
|
363
|
+
return sanitized;
|
|
364
|
+
}
|
|
365
|
+
sanitized["additionalProperties"] = false;
|
|
366
|
+
const propertyNames = listSchemaPropertyNames(sanitized);
|
|
367
|
+
if (propertyNames.length > 0) {
|
|
368
|
+
sanitized["required"] = propertyNames;
|
|
369
|
+
}
|
|
370
|
+
return sanitized;
|
|
371
|
+
}
|
|
372
|
+
function listSchemaPropertyNames(node) {
|
|
373
|
+
const properties = node["properties"];
|
|
374
|
+
if (properties === void 0 || !isJsonObject(properties)) {
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
return Object.keys(properties);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// src/openai-request-mapping.ts
|
|
133
381
|
function buildOpenAiRequestBody(model, input) {
|
|
134
382
|
const requestBody = {
|
|
135
383
|
model,
|
|
@@ -141,7 +389,16 @@ function buildOpenAiRequestBody(model, input) {
|
|
|
141
389
|
if (input.maxOutputTokens !== void 0) {
|
|
142
390
|
requestBody.max_tokens = input.maxOutputTokens;
|
|
143
391
|
}
|
|
144
|
-
if (input.
|
|
392
|
+
if (input.structuredOutputJsonSchema !== void 0) {
|
|
393
|
+
requestBody.response_format = {
|
|
394
|
+
type: "json_schema",
|
|
395
|
+
json_schema: {
|
|
396
|
+
name: openAiStructuredOutputSchemaName,
|
|
397
|
+
strict: true,
|
|
398
|
+
schema: input.structuredOutputJsonSchema
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
} else if (input.requireJsonObjectResponse === true) {
|
|
145
402
|
requestBody.response_format = { type: "json_object" };
|
|
146
403
|
}
|
|
147
404
|
if (input.tools !== void 0 && input.tools.length > 0) {
|
|
@@ -227,17 +484,17 @@ function mapMessageContentBlockToOpenAiContentPart(contentBlock) {
|
|
|
227
484
|
}
|
|
228
485
|
};
|
|
229
486
|
}
|
|
230
|
-
throw new
|
|
231
|
-
code:
|
|
487
|
+
throw new import_errors4.AppError({
|
|
488
|
+
code: import_errors3.AppErrorCode.ValidationFailed,
|
|
232
489
|
message: "OpenAI adapter does not support document content blocks in chat messages",
|
|
233
490
|
statusCode: 400
|
|
234
491
|
});
|
|
235
492
|
}
|
|
236
493
|
|
|
237
494
|
// src/parse-structured-json.ts
|
|
238
|
-
var
|
|
239
|
-
var
|
|
240
|
-
var
|
|
495
|
+
var import_errors5 = require("@assemble-dev/shared-types/errors");
|
|
496
|
+
var import_json3 = require("@assemble-dev/shared-types/json");
|
|
497
|
+
var import_errors6 = require("@assemble-dev/shared-utils/errors");
|
|
241
498
|
var markdownCodeFencePattern = /^```[A-Za-z]*\s*\n?([\s\S]*?)\n?```$/;
|
|
242
499
|
function parseStructuredJsonText(text, schema) {
|
|
243
500
|
return schema.parse(parseJsonText(stripMarkdownCodeFences(text)));
|
|
@@ -252,10 +509,10 @@ function stripMarkdownCodeFences(text) {
|
|
|
252
509
|
}
|
|
253
510
|
function parseJsonText(text) {
|
|
254
511
|
try {
|
|
255
|
-
return
|
|
512
|
+
return import_json3.JsonValueSchema.parse(JSON.parse(text));
|
|
256
513
|
} catch (error) {
|
|
257
|
-
throw new
|
|
258
|
-
code:
|
|
514
|
+
throw new import_errors6.AppError({
|
|
515
|
+
code: import_errors5.AppErrorCode.ValidationFailed,
|
|
259
516
|
message: "Structured output was not valid JSON",
|
|
260
517
|
statusCode: 422,
|
|
261
518
|
cause: error instanceof Error ? error : void 0
|
|
@@ -264,9 +521,9 @@ function parseJsonText(text) {
|
|
|
264
521
|
}
|
|
265
522
|
|
|
266
523
|
// src/provider-api-key.ts
|
|
267
|
-
var
|
|
268
|
-
var
|
|
269
|
-
var
|
|
524
|
+
var import_zod3 = require("zod");
|
|
525
|
+
var import_errors7 = require("@assemble-dev/shared-types/errors");
|
|
526
|
+
var import_errors8 = require("@assemble-dev/shared-utils/errors");
|
|
270
527
|
var import_provider_keys = require("@assemble-dev/shared-utils/provider-keys");
|
|
271
528
|
function resolveProviderApiKey(input) {
|
|
272
529
|
if (input.apiKey !== void 0 && input.apiKey.length > 0) {
|
|
@@ -279,8 +536,8 @@ function resolveProviderApiKey(input) {
|
|
|
279
536
|
}
|
|
280
537
|
function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
281
538
|
if (!(0, import_provider_keys.validateProviderApiKeyName)(apiKeyName)) {
|
|
282
|
-
throw new
|
|
283
|
-
code:
|
|
539
|
+
throw new import_errors8.AppError({
|
|
540
|
+
code: import_errors7.AppErrorCode.BadRequest,
|
|
284
541
|
message: `Invalid ${input.providerLabel} apiKeyName "${apiKeyName}". Expected ${import_provider_keys.expectedProviderApiKeyNameFormat}.`,
|
|
285
542
|
statusCode: 400
|
|
286
543
|
});
|
|
@@ -288,8 +545,8 @@ function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
|
288
545
|
const envVarName = (0, import_provider_keys.buildNamedApiKeyEnvVarName)(input.baseEnvVarName, apiKeyName);
|
|
289
546
|
const envApiKey = readEnvironmentVariable(envVarName);
|
|
290
547
|
if (envApiKey === void 0 || envApiKey.length === 0) {
|
|
291
|
-
throw new
|
|
292
|
-
code:
|
|
548
|
+
throw new import_errors8.AppError({
|
|
549
|
+
code: import_errors7.AppErrorCode.Unauthorized,
|
|
293
550
|
message: `${input.providerLabel} API key named "${apiKeyName}" is missing. Set the ${envVarName} environment variable or pass apiKey to ${input.adapterFunctionName}.`,
|
|
294
551
|
statusCode: 401
|
|
295
552
|
});
|
|
@@ -299,8 +556,8 @@ function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
|
299
556
|
function resolveBaseProviderApiKeyFromEnv(input) {
|
|
300
557
|
const envApiKey = readEnvironmentVariable(input.baseEnvVarName);
|
|
301
558
|
if (envApiKey === void 0 || envApiKey.length === 0) {
|
|
302
|
-
throw new
|
|
303
|
-
code:
|
|
559
|
+
throw new import_errors8.AppError({
|
|
560
|
+
code: import_errors7.AppErrorCode.Unauthorized,
|
|
304
561
|
message: `${input.providerLabel} API key is missing. Pass apiKey to ${input.adapterFunctionName} or set the ${input.baseEnvVarName} environment variable.`,
|
|
305
562
|
statusCode: 401
|
|
306
563
|
});
|
|
@@ -308,19 +565,19 @@ function resolveBaseProviderApiKeyFromEnv(input) {
|
|
|
308
565
|
return envApiKey;
|
|
309
566
|
}
|
|
310
567
|
function readEnvironmentVariable(envVarName) {
|
|
311
|
-
const parsedEnv =
|
|
568
|
+
const parsedEnv = import_zod3.z.object({ [envVarName]: import_zod3.z.string().optional() }).parse(process.env);
|
|
312
569
|
return parsedEnv[envVarName];
|
|
313
570
|
}
|
|
314
571
|
|
|
315
572
|
// src/provider-cancellation.ts
|
|
316
|
-
var
|
|
317
|
-
var
|
|
573
|
+
var import_errors9 = require("@assemble-dev/shared-types/errors");
|
|
574
|
+
var import_errors10 = require("@assemble-dev/shared-utils/errors");
|
|
318
575
|
function isAbortError(error) {
|
|
319
576
|
return error.name === "AbortError";
|
|
320
577
|
}
|
|
321
578
|
function buildProviderCanceledError(providerLabel) {
|
|
322
|
-
return new
|
|
323
|
-
code:
|
|
579
|
+
return new import_errors10.AppError({
|
|
580
|
+
code: import_errors9.AppErrorCode.Canceled,
|
|
324
581
|
message: `${providerLabel} request was canceled`,
|
|
325
582
|
statusCode: 499
|
|
326
583
|
});
|
|
@@ -336,19 +593,28 @@ async function executeAbortableProviderFetch(providerLabel, performFetch) {
|
|
|
336
593
|
}
|
|
337
594
|
}
|
|
338
595
|
|
|
596
|
+
// src/provider-extra-body.ts
|
|
597
|
+
function mergeProviderRequestExtraBody(requestBody, adapterExtraBody, requestExtraBody) {
|
|
598
|
+
return {
|
|
599
|
+
...requestBody,
|
|
600
|
+
...adapterExtraBody ?? {},
|
|
601
|
+
...requestExtraBody ?? {}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
339
605
|
// src/provider-fallback.ts
|
|
340
|
-
var
|
|
606
|
+
var import_errors13 = require("@assemble-dev/shared-utils/errors");
|
|
341
607
|
|
|
342
608
|
// src/provider-retry.ts
|
|
343
|
-
var
|
|
344
|
-
var
|
|
609
|
+
var import_errors11 = require("@assemble-dev/shared-types/errors");
|
|
610
|
+
var import_errors12 = require("@assemble-dev/shared-utils/errors");
|
|
345
611
|
var defaultProviderRetryMaxAttempts = 3;
|
|
346
612
|
var defaultProviderRetryInitialDelayMs = 500;
|
|
347
613
|
var defaultProviderRetryMaxDelayMs = 8e3;
|
|
348
614
|
var retryableAppErrorCodes = /* @__PURE__ */ new Set([
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
615
|
+
import_errors11.AppErrorCode.RateLimited,
|
|
616
|
+
import_errors11.AppErrorCode.ServiceUnavailable,
|
|
617
|
+
import_errors11.AppErrorCode.InternalServerError
|
|
352
618
|
]);
|
|
353
619
|
function isRetryableProviderAppError(appError) {
|
|
354
620
|
return retryableAppErrorCodes.has(appError.code);
|
|
@@ -363,7 +629,7 @@ async function executeProviderCallWithRetry(retryOptions, executeCall, waitFor)
|
|
|
363
629
|
try {
|
|
364
630
|
return await executeCall();
|
|
365
631
|
} catch (error) {
|
|
366
|
-
if (error instanceof
|
|
632
|
+
if (error instanceof import_errors12.AppError && !isRetryableProviderAppError(error)) {
|
|
367
633
|
throw error;
|
|
368
634
|
}
|
|
369
635
|
if (error instanceof Error && isAbortError(error)) {
|
|
@@ -397,7 +663,7 @@ async function executeProviderCallWithModelFallback(fallbackModel, executePrimar
|
|
|
397
663
|
if (fallbackModel === void 0) {
|
|
398
664
|
throw error;
|
|
399
665
|
}
|
|
400
|
-
if (error instanceof
|
|
666
|
+
if (error instanceof import_errors13.AppError && !isRetryableProviderAppError(error)) {
|
|
401
667
|
throw error;
|
|
402
668
|
}
|
|
403
669
|
if (error instanceof Error && isAbortError(error)) {
|
|
@@ -408,11 +674,11 @@ async function executeProviderCallWithModelFallback(fallbackModel, executePrimar
|
|
|
408
674
|
}
|
|
409
675
|
|
|
410
676
|
// src/provider-status-error.ts
|
|
411
|
-
var
|
|
412
|
-
var
|
|
677
|
+
var import_errors14 = require("@assemble-dev/shared-types/errors");
|
|
678
|
+
var import_errors15 = require("@assemble-dev/shared-utils/errors");
|
|
413
679
|
function buildProviderStatusError(providerLabel, providerStatus) {
|
|
414
680
|
const code = mapProviderStatusToAppErrorCode(providerStatus);
|
|
415
|
-
return new
|
|
681
|
+
return new import_errors15.AppError({
|
|
416
682
|
code,
|
|
417
683
|
message: `${providerLabel} request failed with status ${String(providerStatus)}`,
|
|
418
684
|
statusCode: mapAppErrorCodeToStatusCode(code),
|
|
@@ -420,44 +686,44 @@ function buildProviderStatusError(providerLabel, providerStatus) {
|
|
|
420
686
|
});
|
|
421
687
|
}
|
|
422
688
|
function buildProviderInvalidResponseError(providerLabel, expectedShapeDescription) {
|
|
423
|
-
return new
|
|
424
|
-
code:
|
|
689
|
+
return new import_errors15.AppError({
|
|
690
|
+
code: import_errors14.AppErrorCode.ValidationFailed,
|
|
425
691
|
message: `${providerLabel} response did not match the expected ${expectedShapeDescription} shape`,
|
|
426
692
|
statusCode: 502
|
|
427
693
|
});
|
|
428
694
|
}
|
|
429
695
|
function mapProviderStatusToAppErrorCode(providerStatus) {
|
|
430
696
|
if (providerStatus === 400) {
|
|
431
|
-
return
|
|
697
|
+
return import_errors14.AppErrorCode.BadRequest;
|
|
432
698
|
}
|
|
433
699
|
if (providerStatus === 401) {
|
|
434
|
-
return
|
|
700
|
+
return import_errors14.AppErrorCode.Unauthorized;
|
|
435
701
|
}
|
|
436
702
|
if (providerStatus === 403) {
|
|
437
|
-
return
|
|
703
|
+
return import_errors14.AppErrorCode.Forbidden;
|
|
438
704
|
}
|
|
439
705
|
if (providerStatus === 404) {
|
|
440
|
-
return
|
|
706
|
+
return import_errors14.AppErrorCode.NotFound;
|
|
441
707
|
}
|
|
442
708
|
if (providerStatus === 429) {
|
|
443
|
-
return
|
|
709
|
+
return import_errors14.AppErrorCode.RateLimited;
|
|
444
710
|
}
|
|
445
|
-
return
|
|
711
|
+
return import_errors14.AppErrorCode.InternalServerError;
|
|
446
712
|
}
|
|
447
713
|
function mapAppErrorCodeToStatusCode(code) {
|
|
448
|
-
if (code ===
|
|
714
|
+
if (code === import_errors14.AppErrorCode.BadRequest) {
|
|
449
715
|
return 400;
|
|
450
716
|
}
|
|
451
|
-
if (code ===
|
|
717
|
+
if (code === import_errors14.AppErrorCode.Unauthorized) {
|
|
452
718
|
return 401;
|
|
453
719
|
}
|
|
454
|
-
if (code ===
|
|
720
|
+
if (code === import_errors14.AppErrorCode.Forbidden) {
|
|
455
721
|
return 403;
|
|
456
722
|
}
|
|
457
|
-
if (code ===
|
|
723
|
+
if (code === import_errors14.AppErrorCode.NotFound) {
|
|
458
724
|
return 404;
|
|
459
725
|
}
|
|
460
|
-
if (code ===
|
|
726
|
+
if (code === import_errors14.AppErrorCode.RateLimited) {
|
|
461
727
|
return 429;
|
|
462
728
|
}
|
|
463
729
|
return 500;
|
|
@@ -465,33 +731,29 @@ function mapAppErrorCodeToStatusCode(code) {
|
|
|
465
731
|
|
|
466
732
|
// src/openai-adapter.ts
|
|
467
733
|
var defaultOpenAiBaseUrl = "https://api.openai.com/v1";
|
|
468
|
-
var OpenAiResponseToolCallSchema =
|
|
469
|
-
id:
|
|
470
|
-
function:
|
|
471
|
-
name:
|
|
472
|
-
arguments:
|
|
734
|
+
var OpenAiResponseToolCallSchema = import_zod4.z.object({
|
|
735
|
+
id: import_zod4.z.string(),
|
|
736
|
+
function: import_zod4.z.object({
|
|
737
|
+
name: import_zod4.z.string(),
|
|
738
|
+
arguments: import_zod4.z.string()
|
|
473
739
|
})
|
|
474
740
|
});
|
|
475
|
-
var OpenAiChatCompletionResponseSchema =
|
|
476
|
-
choices:
|
|
477
|
-
|
|
478
|
-
message:
|
|
479
|
-
content:
|
|
480
|
-
tool_calls:
|
|
741
|
+
var OpenAiChatCompletionResponseSchema = import_zod4.z.object({
|
|
742
|
+
choices: import_zod4.z.array(
|
|
743
|
+
import_zod4.z.object({
|
|
744
|
+
message: import_zod4.z.object({
|
|
745
|
+
content: import_zod4.z.string().nullable(),
|
|
746
|
+
tool_calls: import_zod4.z.array(OpenAiResponseToolCallSchema).optional()
|
|
481
747
|
}),
|
|
482
|
-
finish_reason:
|
|
748
|
+
finish_reason: import_zod4.z.string().nullable().optional()
|
|
483
749
|
})
|
|
484
750
|
).min(1),
|
|
485
|
-
usage:
|
|
486
|
-
prompt_tokens:
|
|
487
|
-
completion_tokens:
|
|
488
|
-
total_tokens:
|
|
751
|
+
usage: import_zod4.z.object({
|
|
752
|
+
prompt_tokens: import_zod4.z.number().int().nonnegative(),
|
|
753
|
+
completion_tokens: import_zod4.z.number().int().nonnegative(),
|
|
754
|
+
total_tokens: import_zod4.z.number().int().nonnegative()
|
|
489
755
|
})
|
|
490
756
|
});
|
|
491
|
-
var structuredOutputSystemMessage = {
|
|
492
|
-
role: "system",
|
|
493
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
494
|
-
};
|
|
495
757
|
function openai(options) {
|
|
496
758
|
return {
|
|
497
759
|
name: `openai:${options.model}`,
|
|
@@ -500,18 +762,20 @@ function openai(options) {
|
|
|
500
762
|
messages: request.messages,
|
|
501
763
|
temperature: request.temperature ?? options.temperature,
|
|
502
764
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
503
|
-
requireJsonObjectResponse: false,
|
|
504
765
|
tools: request.tools,
|
|
505
766
|
toolChoice: request.toolChoice,
|
|
506
|
-
abortSignal: request.abortSignal
|
|
767
|
+
abortSignal: request.abortSignal,
|
|
768
|
+
extraBody: request.extraBody
|
|
507
769
|
});
|
|
508
770
|
},
|
|
509
771
|
async generateStructuredOutput(request) {
|
|
510
772
|
const completion = await executeOpenAiAdapterCall(options, {
|
|
511
|
-
messages:
|
|
773
|
+
messages: request.messages,
|
|
512
774
|
temperature: request.temperature ?? options.temperature,
|
|
513
775
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
514
|
-
|
|
776
|
+
structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
|
|
777
|
+
request.schema
|
|
778
|
+
),
|
|
515
779
|
abortSignal: request.abortSignal
|
|
516
780
|
});
|
|
517
781
|
return {
|
|
@@ -545,7 +809,11 @@ async function requestOpenAiChatCompletion(options, input) {
|
|
|
545
809
|
});
|
|
546
810
|
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
547
811
|
const baseUrl = options.baseURL ?? defaultOpenAiBaseUrl;
|
|
548
|
-
const requestBody =
|
|
812
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
813
|
+
buildOpenAiRequestBody(options.model, input),
|
|
814
|
+
options.extraBody,
|
|
815
|
+
input.extraBody
|
|
816
|
+
);
|
|
549
817
|
const startedAt = Date.now();
|
|
550
818
|
const response = await executeAbortableProviderFetch(
|
|
551
819
|
"OpenAI",
|
|
@@ -600,10 +868,10 @@ function parseOpenAiToolArgumentsJson(argumentsJsonText) {
|
|
|
600
868
|
return {};
|
|
601
869
|
}
|
|
602
870
|
try {
|
|
603
|
-
return
|
|
871
|
+
return import_json4.JsonValueSchema.parse(JSON.parse(argumentsJsonText));
|
|
604
872
|
} catch (error) {
|
|
605
|
-
throw new
|
|
606
|
-
code:
|
|
873
|
+
throw new import_errors17.AppError({
|
|
874
|
+
code: import_errors16.AppErrorCode.ValidationFailed,
|
|
607
875
|
message: "OpenAI tool call arguments were not valid JSON",
|
|
608
876
|
statusCode: 502,
|
|
609
877
|
cause: error instanceof Error ? error : void 0
|
|
@@ -625,8 +893,8 @@ function extractOpenAiMessageContent(response, allowEmptyContent) {
|
|
|
625
893
|
if (allowEmptyContent) {
|
|
626
894
|
return "";
|
|
627
895
|
}
|
|
628
|
-
throw new
|
|
629
|
-
code:
|
|
896
|
+
throw new import_errors17.AppError({
|
|
897
|
+
code: import_errors16.AppErrorCode.ValidationFailed,
|
|
630
898
|
message: "OpenAI response contained no message content",
|
|
631
899
|
statusCode: 502
|
|
632
900
|
});
|
|
@@ -641,6 +909,36 @@ function mapOpenAiUsageToTokenUsage(response) {
|
|
|
641
909
|
};
|
|
642
910
|
}
|
|
643
911
|
|
|
912
|
+
// src/anthropic-cache-control.ts
|
|
913
|
+
function resolveAnthropicPromptCachingSettings(promptCaching) {
|
|
914
|
+
if (promptCaching === void 0) {
|
|
915
|
+
return { mode: "off" };
|
|
916
|
+
}
|
|
917
|
+
if (typeof promptCaching === "string") {
|
|
918
|
+
return { mode: promptCaching };
|
|
919
|
+
}
|
|
920
|
+
return promptCaching;
|
|
921
|
+
}
|
|
922
|
+
function buildAnthropicCacheControl(ttl) {
|
|
923
|
+
if (ttl === void 0) {
|
|
924
|
+
return { type: "ephemeral" };
|
|
925
|
+
}
|
|
926
|
+
return { type: "ephemeral", ttl };
|
|
927
|
+
}
|
|
928
|
+
function applyCacheControlToLastContentBlock(requestMessage, cacheControl) {
|
|
929
|
+
if (typeof requestMessage.content === "string") {
|
|
930
|
+
if (requestMessage.content.length === 0) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
requestMessage.content = [{ type: "text", text: requestMessage.content }];
|
|
934
|
+
}
|
|
935
|
+
const lastContentBlock = requestMessage.content[requestMessage.content.length - 1];
|
|
936
|
+
if (lastContentBlock === void 0) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
lastContentBlock.cache_control = cacheControl;
|
|
940
|
+
}
|
|
941
|
+
|
|
644
942
|
// src/anthropic-request-mapping.ts
|
|
645
943
|
function buildAnthropicRequestBody(settings, input) {
|
|
646
944
|
const requestBody = {
|
|
@@ -650,6 +948,7 @@ function buildAnthropicRequestBody(settings, input) {
|
|
|
650
948
|
};
|
|
651
949
|
applyAnthropicSystemField(requestBody, settings, input);
|
|
652
950
|
applyAnthropicToolFields(requestBody, settings, input);
|
|
951
|
+
applyAnthropicMessageCacheBreakpoints(requestBody, settings, input);
|
|
653
952
|
if (input.temperature !== void 0) {
|
|
654
953
|
requestBody.temperature = input.temperature;
|
|
655
954
|
}
|
|
@@ -659,21 +958,54 @@ function buildAnthropicRequestBody(settings, input) {
|
|
|
659
958
|
budget_tokens: settings.thinking.budgetTokens
|
|
660
959
|
};
|
|
661
960
|
}
|
|
961
|
+
if (input.structuredOutputJsonSchema !== void 0) {
|
|
962
|
+
requestBody.output_config = {
|
|
963
|
+
format: { type: "json_schema", schema: input.structuredOutputJsonSchema }
|
|
964
|
+
};
|
|
965
|
+
}
|
|
662
966
|
return requestBody;
|
|
663
967
|
}
|
|
664
968
|
function applyAnthropicSystemField(requestBody, settings, input) {
|
|
969
|
+
const caching = resolveAnthropicPromptCachingSettings(settings.promptCaching);
|
|
665
970
|
const systemText = joinSystemMessageText(input.messages);
|
|
666
971
|
if (systemText.length === 0) {
|
|
667
972
|
return;
|
|
668
973
|
}
|
|
669
|
-
if (
|
|
974
|
+
if (caching.mode === "off") {
|
|
670
975
|
requestBody.system = systemText;
|
|
671
976
|
return;
|
|
672
977
|
}
|
|
673
978
|
requestBody.system = [
|
|
674
|
-
{
|
|
979
|
+
{
|
|
980
|
+
type: "text",
|
|
981
|
+
text: systemText,
|
|
982
|
+
cache_control: buildAnthropicCacheControl(caching.ttl)
|
|
983
|
+
}
|
|
675
984
|
];
|
|
676
985
|
}
|
|
986
|
+
function applyAnthropicMessageCacheBreakpoints(requestBody, settings, input) {
|
|
987
|
+
const caching = resolveAnthropicPromptCachingSettings(settings.promptCaching);
|
|
988
|
+
const finalRequestMessage = requestBody.messages[requestBody.messages.length - 1];
|
|
989
|
+
if (caching.mode === "messages" && finalRequestMessage !== void 0) {
|
|
990
|
+
applyCacheControlToLastContentBlock(
|
|
991
|
+
finalRequestMessage,
|
|
992
|
+
buildAnthropicCacheControl(caching.ttl)
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
const nonSystemMessages = input.messages.filter(
|
|
996
|
+
(message) => message.role !== "system"
|
|
997
|
+
);
|
|
998
|
+
nonSystemMessages.forEach((message, messageIndex) => {
|
|
999
|
+
const requestMessage = requestBody.messages[messageIndex];
|
|
1000
|
+
if (message.cacheControl === void 0 || requestMessage === void 0) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
applyCacheControlToLastContentBlock(
|
|
1004
|
+
requestMessage,
|
|
1005
|
+
buildAnthropicCacheControl(message.cacheControl.ttl ?? caching.ttl)
|
|
1006
|
+
);
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
677
1009
|
function applyAnthropicToolFields(requestBody, settings, input) {
|
|
678
1010
|
if (input.tools !== void 0 && input.tools.length > 0) {
|
|
679
1011
|
requestBody.tools = mapModelToolDefinitionsToAnthropicTools(
|
|
@@ -688,14 +1020,15 @@ function applyAnthropicToolFields(requestBody, settings, input) {
|
|
|
688
1020
|
}
|
|
689
1021
|
}
|
|
690
1022
|
function mapModelToolDefinitionsToAnthropicTools(tools, promptCaching) {
|
|
1023
|
+
const caching = resolveAnthropicPromptCachingSettings(promptCaching);
|
|
691
1024
|
return tools.map((tool, toolIndex) => {
|
|
692
1025
|
const anthropicTool = {
|
|
693
1026
|
name: tool.name,
|
|
694
1027
|
description: tool.description,
|
|
695
1028
|
input_schema: tool.inputJsonSchema
|
|
696
1029
|
};
|
|
697
|
-
if (
|
|
698
|
-
anthropicTool.cache_control =
|
|
1030
|
+
if (caching.mode !== "off" && toolIndex === tools.length - 1) {
|
|
1031
|
+
anthropicTool.cache_control = buildAnthropicCacheControl(caching.ttl);
|
|
699
1032
|
}
|
|
700
1033
|
return anthropicTool;
|
|
701
1034
|
});
|
|
@@ -786,44 +1119,45 @@ function mapMessageContentBlockToAnthropic(contentBlock) {
|
|
|
786
1119
|
}
|
|
787
1120
|
|
|
788
1121
|
// src/anthropic-response-parsing.ts
|
|
789
|
-
var
|
|
790
|
-
var
|
|
791
|
-
var
|
|
792
|
-
var
|
|
793
|
-
var AnthropicUsageSchema =
|
|
794
|
-
input_tokens:
|
|
795
|
-
output_tokens:
|
|
796
|
-
cache_creation_input_tokens:
|
|
797
|
-
cache_read_input_tokens:
|
|
1122
|
+
var import_zod5 = require("zod");
|
|
1123
|
+
var import_errors18 = require("@assemble-dev/shared-types/errors");
|
|
1124
|
+
var import_json5 = require("@assemble-dev/shared-types/json");
|
|
1125
|
+
var import_errors19 = require("@assemble-dev/shared-utils/errors");
|
|
1126
|
+
var AnthropicUsageSchema = import_zod5.z.object({
|
|
1127
|
+
input_tokens: import_zod5.z.number().int().nonnegative(),
|
|
1128
|
+
output_tokens: import_zod5.z.number().int().nonnegative(),
|
|
1129
|
+
cache_creation_input_tokens: import_zod5.z.number().int().nonnegative().optional(),
|
|
1130
|
+
cache_read_input_tokens: import_zod5.z.number().int().nonnegative().optional()
|
|
798
1131
|
});
|
|
799
|
-
var AnthropicContentBlockSchema =
|
|
800
|
-
|
|
801
|
-
type:
|
|
802
|
-
text:
|
|
1132
|
+
var AnthropicContentBlockSchema = import_zod5.z.discriminatedUnion("type", [
|
|
1133
|
+
import_zod5.z.object({
|
|
1134
|
+
type: import_zod5.z.literal("text"),
|
|
1135
|
+
text: import_zod5.z.string()
|
|
803
1136
|
}),
|
|
804
|
-
|
|
805
|
-
type:
|
|
806
|
-
id:
|
|
807
|
-
name:
|
|
808
|
-
input:
|
|
1137
|
+
import_zod5.z.object({
|
|
1138
|
+
type: import_zod5.z.literal("tool_use"),
|
|
1139
|
+
id: import_zod5.z.string(),
|
|
1140
|
+
name: import_zod5.z.string(),
|
|
1141
|
+
input: import_json5.JsonValueSchema
|
|
809
1142
|
}),
|
|
810
|
-
|
|
811
|
-
type:
|
|
812
|
-
thinking:
|
|
813
|
-
signature:
|
|
1143
|
+
import_zod5.z.object({
|
|
1144
|
+
type: import_zod5.z.literal("thinking"),
|
|
1145
|
+
thinking: import_zod5.z.string().optional(),
|
|
1146
|
+
signature: import_zod5.z.string().optional()
|
|
814
1147
|
}),
|
|
815
|
-
|
|
816
|
-
type:
|
|
817
|
-
data:
|
|
1148
|
+
import_zod5.z.object({
|
|
1149
|
+
type: import_zod5.z.literal("redacted_thinking"),
|
|
1150
|
+
data: import_zod5.z.string().optional()
|
|
818
1151
|
})
|
|
819
1152
|
]);
|
|
820
|
-
var AnthropicMessagesResponseSchema =
|
|
821
|
-
content:
|
|
822
|
-
stop_reason:
|
|
1153
|
+
var AnthropicMessagesResponseSchema = import_zod5.z.object({
|
|
1154
|
+
content: import_zod5.z.array(AnthropicContentBlockSchema),
|
|
1155
|
+
stop_reason: import_zod5.z.string().nullable().optional(),
|
|
823
1156
|
usage: AnthropicUsageSchema
|
|
824
1157
|
});
|
|
825
1158
|
function mapAnthropicResponseToChatCompletionResult(response, latencyMs) {
|
|
826
1159
|
const toolCalls = extractAnthropicToolCalls(response);
|
|
1160
|
+
const thinkingText = extractAnthropicThinkingText(response);
|
|
827
1161
|
const result = {
|
|
828
1162
|
content: extractAnthropicTextContent(response, toolCalls.length > 0),
|
|
829
1163
|
tokenUsage: mapAnthropicUsageToTokenUsage(response.usage),
|
|
@@ -832,6 +1166,9 @@ function mapAnthropicResponseToChatCompletionResult(response, latencyMs) {
|
|
|
832
1166
|
if (toolCalls.length > 0) {
|
|
833
1167
|
result.toolCalls = toolCalls;
|
|
834
1168
|
}
|
|
1169
|
+
if (thinkingText.length > 0) {
|
|
1170
|
+
result.thinkingText = thinkingText;
|
|
1171
|
+
}
|
|
835
1172
|
if (response.stop_reason !== null && response.stop_reason !== void 0) {
|
|
836
1173
|
result.stopReason = normalizeAnthropicStopReason(response.stop_reason);
|
|
837
1174
|
}
|
|
@@ -844,13 +1181,16 @@ function extractAnthropicToolCalls(response) {
|
|
|
844
1181
|
input: contentBlock.input
|
|
845
1182
|
}));
|
|
846
1183
|
}
|
|
1184
|
+
function extractAnthropicThinkingText(response) {
|
|
1185
|
+
return response.content.filter((contentBlock) => contentBlock.type === "thinking").map((contentBlock) => contentBlock.thinking ?? "").join("");
|
|
1186
|
+
}
|
|
847
1187
|
function extractAnthropicTextContent(response, allowEmptyText) {
|
|
848
1188
|
const textBlocks = response.content.filter(
|
|
849
1189
|
(contentBlock) => contentBlock.type === "text"
|
|
850
1190
|
);
|
|
851
1191
|
if (textBlocks.length === 0 && !allowEmptyText) {
|
|
852
|
-
throw new
|
|
853
|
-
code:
|
|
1192
|
+
throw new import_errors19.AppError({
|
|
1193
|
+
code: import_errors18.AppErrorCode.ValidationFailed,
|
|
854
1194
|
message: "Anthropic response contained no text content",
|
|
855
1195
|
statusCode: 502
|
|
856
1196
|
});
|
|
@@ -887,58 +1227,68 @@ function mapAnthropicUsageToTokenUsage(usage) {
|
|
|
887
1227
|
}
|
|
888
1228
|
|
|
889
1229
|
// src/anthropic-stream.ts
|
|
890
|
-
var
|
|
891
|
-
var
|
|
892
|
-
var
|
|
1230
|
+
var import_errors20 = require("@assemble-dev/shared-types/errors");
|
|
1231
|
+
var import_json6 = require("@assemble-dev/shared-types/json");
|
|
1232
|
+
var import_errors21 = require("@assemble-dev/shared-utils/errors");
|
|
893
1233
|
|
|
894
1234
|
// src/anthropic-stream-events.ts
|
|
895
|
-
var
|
|
896
|
-
var AnthropicStreamEventSchema =
|
|
897
|
-
|
|
898
|
-
type:
|
|
899
|
-
message:
|
|
900
|
-
usage:
|
|
901
|
-
input_tokens:
|
|
902
|
-
output_tokens:
|
|
903
|
-
cache_creation_input_tokens:
|
|
904
|
-
cache_read_input_tokens:
|
|
1235
|
+
var import_zod6 = require("zod");
|
|
1236
|
+
var AnthropicStreamEventSchema = import_zod6.z.discriminatedUnion("type", [
|
|
1237
|
+
import_zod6.z.object({
|
|
1238
|
+
type: import_zod6.z.literal("message_start"),
|
|
1239
|
+
message: import_zod6.z.object({
|
|
1240
|
+
usage: import_zod6.z.object({
|
|
1241
|
+
input_tokens: import_zod6.z.number().int().nonnegative(),
|
|
1242
|
+
output_tokens: import_zod6.z.number().int().nonnegative().optional(),
|
|
1243
|
+
cache_creation_input_tokens: import_zod6.z.number().int().nonnegative().optional(),
|
|
1244
|
+
cache_read_input_tokens: import_zod6.z.number().int().nonnegative().optional()
|
|
905
1245
|
})
|
|
906
1246
|
})
|
|
907
1247
|
}),
|
|
908
|
-
|
|
909
|
-
type:
|
|
910
|
-
index:
|
|
911
|
-
content_block:
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
type:
|
|
915
|
-
|
|
916
|
-
|
|
1248
|
+
import_zod6.z.object({
|
|
1249
|
+
type: import_zod6.z.literal("content_block_start"),
|
|
1250
|
+
index: import_zod6.z.number().int().nonnegative(),
|
|
1251
|
+
content_block: import_zod6.z.discriminatedUnion("type", [
|
|
1252
|
+
import_zod6.z.object({ type: import_zod6.z.literal("text"), text: import_zod6.z.string().optional() }),
|
|
1253
|
+
import_zod6.z.object({
|
|
1254
|
+
type: import_zod6.z.literal("thinking"),
|
|
1255
|
+
thinking: import_zod6.z.string().optional()
|
|
1256
|
+
}),
|
|
1257
|
+
import_zod6.z.object({
|
|
1258
|
+
type: import_zod6.z.literal("redacted_thinking"),
|
|
1259
|
+
data: import_zod6.z.string().optional()
|
|
1260
|
+
}),
|
|
1261
|
+
import_zod6.z.object({
|
|
1262
|
+
type: import_zod6.z.literal("tool_use"),
|
|
1263
|
+
id: import_zod6.z.string(),
|
|
1264
|
+
name: import_zod6.z.string()
|
|
917
1265
|
})
|
|
918
1266
|
])
|
|
919
1267
|
}),
|
|
920
|
-
|
|
921
|
-
type:
|
|
922
|
-
index:
|
|
923
|
-
delta:
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1268
|
+
import_zod6.z.object({
|
|
1269
|
+
type: import_zod6.z.literal("content_block_delta"),
|
|
1270
|
+
index: import_zod6.z.number().int().nonnegative(),
|
|
1271
|
+
delta: import_zod6.z.discriminatedUnion("type", [
|
|
1272
|
+
import_zod6.z.object({ type: import_zod6.z.literal("text_delta"), text: import_zod6.z.string() }),
|
|
1273
|
+
import_zod6.z.object({ type: import_zod6.z.literal("thinking_delta"), thinking: import_zod6.z.string() }),
|
|
1274
|
+
import_zod6.z.object({ type: import_zod6.z.literal("signature_delta"), signature: import_zod6.z.string() }),
|
|
1275
|
+
import_zod6.z.object({
|
|
1276
|
+
type: import_zod6.z.literal("input_json_delta"),
|
|
1277
|
+
partial_json: import_zod6.z.string()
|
|
928
1278
|
})
|
|
929
1279
|
])
|
|
930
1280
|
}),
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
type:
|
|
934
|
-
delta:
|
|
935
|
-
usage:
|
|
1281
|
+
import_zod6.z.object({ type: import_zod6.z.literal("content_block_stop") }),
|
|
1282
|
+
import_zod6.z.object({
|
|
1283
|
+
type: import_zod6.z.literal("message_delta"),
|
|
1284
|
+
delta: import_zod6.z.object({ stop_reason: import_zod6.z.string().nullable().optional() }),
|
|
1285
|
+
usage: import_zod6.z.object({ output_tokens: import_zod6.z.number().int().nonnegative() }).optional()
|
|
936
1286
|
}),
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
type:
|
|
941
|
-
error:
|
|
1287
|
+
import_zod6.z.object({ type: import_zod6.z.literal("message_stop") }),
|
|
1288
|
+
import_zod6.z.object({ type: import_zod6.z.literal("ping") }),
|
|
1289
|
+
import_zod6.z.object({
|
|
1290
|
+
type: import_zod6.z.literal("error"),
|
|
1291
|
+
error: import_zod6.z.object({ message: import_zod6.z.string() })
|
|
942
1292
|
})
|
|
943
1293
|
]);
|
|
944
1294
|
|
|
@@ -1024,6 +1374,7 @@ async function* readServerSentEventDataLines(body, abortSignal) {
|
|
|
1024
1374
|
function createAnthropicStreamAccumulator() {
|
|
1025
1375
|
return {
|
|
1026
1376
|
contentText: "",
|
|
1377
|
+
thinkingText: "",
|
|
1027
1378
|
toolCallsByBlockIndex: /* @__PURE__ */ new Map(),
|
|
1028
1379
|
inputTokens: 0,
|
|
1029
1380
|
outputTokens: 0
|
|
@@ -1031,8 +1382,8 @@ function createAnthropicStreamAccumulator() {
|
|
|
1031
1382
|
}
|
|
1032
1383
|
function* applyAnthropicStreamEvent(event, accumulator) {
|
|
1033
1384
|
if (event.type === "error") {
|
|
1034
|
-
throw new
|
|
1035
|
-
code:
|
|
1385
|
+
throw new import_errors21.AppError({
|
|
1386
|
+
code: import_errors20.AppErrorCode.InternalServerError,
|
|
1036
1387
|
message: `Anthropic stream reported an error: ${event.error.message}`,
|
|
1037
1388
|
statusCode: 502
|
|
1038
1389
|
});
|
|
@@ -1068,6 +1419,17 @@ function* applyAnthropicContentBlockStart(event, accumulator) {
|
|
|
1068
1419
|
}
|
|
1069
1420
|
return;
|
|
1070
1421
|
}
|
|
1422
|
+
if (event.content_block.type === "thinking") {
|
|
1423
|
+
const initialThinkingText = event.content_block.thinking ?? "";
|
|
1424
|
+
if (initialThinkingText.length > 0) {
|
|
1425
|
+
accumulator.thinkingText += initialThinkingText;
|
|
1426
|
+
yield { type: "thinking_delta", text: initialThinkingText };
|
|
1427
|
+
}
|
|
1428
|
+
return;
|
|
1429
|
+
}
|
|
1430
|
+
if (event.content_block.type === "redacted_thinking") {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1071
1433
|
accumulator.toolCallsByBlockIndex.set(event.index, {
|
|
1072
1434
|
id: event.content_block.id,
|
|
1073
1435
|
toolName: event.content_block.name,
|
|
@@ -1085,6 +1447,14 @@ function* applyAnthropicContentBlockDelta(event, accumulator) {
|
|
|
1085
1447
|
yield { type: "text_delta", text: event.delta.text };
|
|
1086
1448
|
return;
|
|
1087
1449
|
}
|
|
1450
|
+
if (event.delta.type === "thinking_delta") {
|
|
1451
|
+
accumulator.thinkingText += event.delta.thinking;
|
|
1452
|
+
yield { type: "thinking_delta", text: event.delta.thinking };
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
if (event.delta.type === "signature_delta") {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1088
1458
|
const toolCallAccumulation = accumulator.toolCallsByBlockIndex.get(
|
|
1089
1459
|
event.index
|
|
1090
1460
|
);
|
|
@@ -1131,6 +1501,9 @@ function buildAnthropicStreamResult(accumulator, latencyMs) {
|
|
|
1131
1501
|
if (accumulator.stopReason !== void 0) {
|
|
1132
1502
|
result.stopReason = accumulator.stopReason;
|
|
1133
1503
|
}
|
|
1504
|
+
if (accumulator.thinkingText.length > 0) {
|
|
1505
|
+
result.thinkingText = accumulator.thinkingText;
|
|
1506
|
+
}
|
|
1134
1507
|
return result;
|
|
1135
1508
|
}
|
|
1136
1509
|
function mapAccumulatedToolCallToModelToolCall(toolCallAccumulation) {
|
|
@@ -1145,10 +1518,10 @@ function parseAnthropicToolInputJson(inputJsonText) {
|
|
|
1145
1518
|
return {};
|
|
1146
1519
|
}
|
|
1147
1520
|
try {
|
|
1148
|
-
return
|
|
1521
|
+
return import_json6.JsonValueSchema.parse(JSON.parse(inputJsonText));
|
|
1149
1522
|
} catch (error) {
|
|
1150
|
-
throw new
|
|
1151
|
-
code:
|
|
1523
|
+
throw new import_errors21.AppError({
|
|
1524
|
+
code: import_errors20.AppErrorCode.ValidationFailed,
|
|
1152
1525
|
message: "Anthropic tool input was not valid JSON",
|
|
1153
1526
|
statusCode: 502,
|
|
1154
1527
|
cause: error instanceof Error ? error : void 0
|
|
@@ -1160,10 +1533,6 @@ function parseAnthropicToolInputJson(inputJsonText) {
|
|
|
1160
1533
|
var defaultAnthropicBaseUrl = "https://api.anthropic.com";
|
|
1161
1534
|
var defaultAnthropicMaxOutputTokens = 1024;
|
|
1162
1535
|
var anthropicApiVersion = "2023-06-01";
|
|
1163
|
-
var structuredOutputSystemMessage2 = {
|
|
1164
|
-
role: "system",
|
|
1165
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
1166
|
-
};
|
|
1167
1536
|
function anthropic(options) {
|
|
1168
1537
|
return {
|
|
1169
1538
|
name: `anthropic:${options.model}`,
|
|
@@ -1175,9 +1544,12 @@ function anthropic(options) {
|
|
|
1175
1544
|
},
|
|
1176
1545
|
async generateStructuredOutput(request) {
|
|
1177
1546
|
const completion = await executeAnthropicAdapterCall(options, {
|
|
1178
|
-
messages:
|
|
1547
|
+
messages: request.messages,
|
|
1179
1548
|
temperature: request.temperature ?? options.temperature,
|
|
1180
1549
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1550
|
+
structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
|
|
1551
|
+
request.schema
|
|
1552
|
+
),
|
|
1181
1553
|
abortSignal: request.abortSignal
|
|
1182
1554
|
});
|
|
1183
1555
|
return {
|
|
@@ -1200,7 +1572,8 @@ function buildAnthropicCallInput(options, request) {
|
|
|
1200
1572
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1201
1573
|
tools: request.tools,
|
|
1202
1574
|
toolChoice: request.toolChoice,
|
|
1203
|
-
abortSignal: request.abortSignal
|
|
1575
|
+
abortSignal: request.abortSignal,
|
|
1576
|
+
extraBody: request.extraBody
|
|
1204
1577
|
};
|
|
1205
1578
|
}
|
|
1206
1579
|
async function executeAnthropicAdapterCall(options, input) {
|
|
@@ -1217,9 +1590,10 @@ async function executeAnthropicAdapterCall(options, input) {
|
|
|
1217
1590
|
);
|
|
1218
1591
|
}
|
|
1219
1592
|
async function requestAnthropicMessages(options, input) {
|
|
1220
|
-
const requestBody =
|
|
1221
|
-
buildAnthropicRequestSettings(options),
|
|
1222
|
-
|
|
1593
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
1594
|
+
buildAnthropicRequestBody(buildAnthropicRequestSettings(options), input),
|
|
1595
|
+
options.extraBody,
|
|
1596
|
+
input.extraBody
|
|
1223
1597
|
);
|
|
1224
1598
|
const startedAt = Date.now();
|
|
1225
1599
|
const response = await executeAbortableProviderFetch(
|
|
@@ -1248,9 +1622,10 @@ async function requestAnthropicMessages(options, input) {
|
|
|
1248
1622
|
}
|
|
1249
1623
|
function buildAnthropicStreamInput(options, request) {
|
|
1250
1624
|
const input = buildAnthropicCallInput(options, request);
|
|
1251
|
-
const requestBody =
|
|
1252
|
-
buildAnthropicRequestSettings(options),
|
|
1253
|
-
|
|
1625
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
1626
|
+
buildAnthropicRequestBody(buildAnthropicRequestSettings(options), input),
|
|
1627
|
+
options.extraBody,
|
|
1628
|
+
input.extraBody
|
|
1254
1629
|
);
|
|
1255
1630
|
return {
|
|
1256
1631
|
url: `${resolveAnthropicBaseUrl(options)}/v1/messages`,
|
|
@@ -1290,28 +1665,116 @@ function resolveAnthropicFetch(options) {
|
|
|
1290
1665
|
}
|
|
1291
1666
|
|
|
1292
1667
|
// src/gemini-adapter.ts
|
|
1293
|
-
var
|
|
1294
|
-
var
|
|
1295
|
-
var
|
|
1668
|
+
var import_zod7 = require("zod");
|
|
1669
|
+
var import_errors22 = require("@assemble-dev/shared-types/errors");
|
|
1670
|
+
var import_errors23 = require("@assemble-dev/shared-utils/errors");
|
|
1671
|
+
|
|
1672
|
+
// src/gemini-response-schema.ts
|
|
1673
|
+
var geminiAllowedSchemaKeywords = [
|
|
1674
|
+
"type",
|
|
1675
|
+
"format",
|
|
1676
|
+
"description",
|
|
1677
|
+
"nullable",
|
|
1678
|
+
"enum",
|
|
1679
|
+
"properties",
|
|
1680
|
+
"required",
|
|
1681
|
+
"items",
|
|
1682
|
+
"minItems",
|
|
1683
|
+
"maxItems",
|
|
1684
|
+
"anyOf"
|
|
1685
|
+
];
|
|
1686
|
+
var geminiAllowedFormats = [
|
|
1687
|
+
"date-time",
|
|
1688
|
+
"int32",
|
|
1689
|
+
"int64",
|
|
1690
|
+
"float",
|
|
1691
|
+
"double"
|
|
1692
|
+
];
|
|
1693
|
+
function buildGeminiResponseSchema(schema) {
|
|
1694
|
+
return sanitizeJsonSchemaForGeminiResponseSchema(
|
|
1695
|
+
convertZodSchemaToJsonSchema(schema)
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
function sanitizeJsonSchemaForGeminiResponseSchema(jsonSchema) {
|
|
1699
|
+
return transformJsonSchemaNode(
|
|
1700
|
+
inlineJsonSchemaReferences(jsonSchema),
|
|
1701
|
+
sanitizeGeminiSchemaNode
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
function sanitizeGeminiSchemaNode(node) {
|
|
1705
|
+
const normalized = normalizeGeminiNullableType(
|
|
1706
|
+
mergeNullUnionIntoNullableNode(node)
|
|
1707
|
+
);
|
|
1708
|
+
const sanitized = {};
|
|
1709
|
+
for (const [key, value] of Object.entries(normalized)) {
|
|
1710
|
+
if (geminiAllowedSchemaKeywords.includes(key)) {
|
|
1711
|
+
sanitized[key] = value;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
return removeUnsupportedGeminiFormat(sanitized);
|
|
1715
|
+
}
|
|
1716
|
+
function mergeNullUnionIntoNullableNode(node) {
|
|
1717
|
+
const unionKeyword = Array.isArray(node["anyOf"]) ? "anyOf" : "oneOf";
|
|
1718
|
+
const unionMembers = node[unionKeyword];
|
|
1719
|
+
if (!Array.isArray(unionMembers)) {
|
|
1720
|
+
return node;
|
|
1721
|
+
}
|
|
1722
|
+
const nonNullMembers = unionMembers.filter(
|
|
1723
|
+
(member) => !isNullTypeSchemaNode(member)
|
|
1724
|
+
);
|
|
1725
|
+
if (nonNullMembers.length === unionMembers.length) {
|
|
1726
|
+
return node;
|
|
1727
|
+
}
|
|
1728
|
+
const merged = { ...node };
|
|
1729
|
+
delete merged[unionKeyword];
|
|
1730
|
+
const singleMember = nonNullMembers.length === 1 ? nonNullMembers[0] : void 0;
|
|
1731
|
+
if (singleMember !== void 0 && isJsonObject(singleMember)) {
|
|
1732
|
+
return { ...merged, ...singleMember, nullable: true };
|
|
1733
|
+
}
|
|
1734
|
+
return { ...merged, anyOf: nonNullMembers, nullable: true };
|
|
1735
|
+
}
|
|
1736
|
+
function normalizeGeminiNullableType(node) {
|
|
1737
|
+
const typeValue = node["type"];
|
|
1738
|
+
if (!Array.isArray(typeValue)) {
|
|
1739
|
+
return node;
|
|
1740
|
+
}
|
|
1741
|
+
const nonNullTypes = typeValue.filter((typeName) => typeName !== "null");
|
|
1742
|
+
const normalized = { ...node };
|
|
1743
|
+
if (nonNullTypes.length < typeValue.length) {
|
|
1744
|
+
normalized["nullable"] = true;
|
|
1745
|
+
}
|
|
1746
|
+
normalized["type"] = nonNullTypes[0] ?? "string";
|
|
1747
|
+
return normalized;
|
|
1748
|
+
}
|
|
1749
|
+
function removeUnsupportedGeminiFormat(node) {
|
|
1750
|
+
const formatValue = node["format"];
|
|
1751
|
+
if (typeof formatValue === "string" && !geminiAllowedFormats.includes(formatValue)) {
|
|
1752
|
+
const withoutFormat = { ...node };
|
|
1753
|
+
delete withoutFormat["format"];
|
|
1754
|
+
return withoutFormat;
|
|
1755
|
+
}
|
|
1756
|
+
return node;
|
|
1757
|
+
}
|
|
1758
|
+
function isNullTypeSchemaNode(value) {
|
|
1759
|
+
return isJsonObject(value) && value["type"] === "null";
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
// src/gemini-adapter.ts
|
|
1296
1763
|
var defaultGeminiBaseUrl = "https://generativelanguage.googleapis.com";
|
|
1297
|
-
var GeminiGenerateContentResponseSchema =
|
|
1298
|
-
candidates:
|
|
1299
|
-
|
|
1300
|
-
content:
|
|
1301
|
-
parts:
|
|
1764
|
+
var GeminiGenerateContentResponseSchema = import_zod7.z.object({
|
|
1765
|
+
candidates: import_zod7.z.array(
|
|
1766
|
+
import_zod7.z.object({
|
|
1767
|
+
content: import_zod7.z.object({
|
|
1768
|
+
parts: import_zod7.z.array(import_zod7.z.object({ text: import_zod7.z.string() }))
|
|
1302
1769
|
})
|
|
1303
1770
|
})
|
|
1304
1771
|
).optional(),
|
|
1305
|
-
usageMetadata:
|
|
1306
|
-
promptTokenCount:
|
|
1307
|
-
candidatesTokenCount:
|
|
1308
|
-
totalTokenCount:
|
|
1772
|
+
usageMetadata: import_zod7.z.object({
|
|
1773
|
+
promptTokenCount: import_zod7.z.number().int().nonnegative().optional(),
|
|
1774
|
+
candidatesTokenCount: import_zod7.z.number().int().nonnegative().optional(),
|
|
1775
|
+
totalTokenCount: import_zod7.z.number().int().nonnegative().optional()
|
|
1309
1776
|
}).optional()
|
|
1310
1777
|
});
|
|
1311
|
-
var structuredOutputSystemMessage3 = {
|
|
1312
|
-
role: "system",
|
|
1313
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
1314
|
-
};
|
|
1315
1778
|
function gemini(options) {
|
|
1316
1779
|
return {
|
|
1317
1780
|
name: `gemini:${options.model}`,
|
|
@@ -1321,16 +1784,16 @@ function gemini(options) {
|
|
|
1321
1784
|
messages: request.messages,
|
|
1322
1785
|
temperature: request.temperature ?? options.temperature,
|
|
1323
1786
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1324
|
-
|
|
1325
|
-
|
|
1787
|
+
abortSignal: request.abortSignal,
|
|
1788
|
+
extraBody: request.extraBody
|
|
1326
1789
|
});
|
|
1327
1790
|
},
|
|
1328
1791
|
async generateStructuredOutput(request) {
|
|
1329
1792
|
const completion = await executeGeminiAdapterCall(options, {
|
|
1330
|
-
messages:
|
|
1793
|
+
messages: request.messages,
|
|
1331
1794
|
temperature: request.temperature ?? options.temperature,
|
|
1332
1795
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1333
|
-
|
|
1796
|
+
responseSchema: buildGeminiResponseSchema(request.schema),
|
|
1334
1797
|
abortSignal: request.abortSignal
|
|
1335
1798
|
});
|
|
1336
1799
|
return {
|
|
@@ -1364,7 +1827,11 @@ async function requestGeminiGenerateContent(options, input) {
|
|
|
1364
1827
|
});
|
|
1365
1828
|
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
1366
1829
|
const baseUrl = options.baseURL ?? defaultGeminiBaseUrl;
|
|
1367
|
-
const requestBody =
|
|
1830
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
1831
|
+
buildGeminiRequestBody(input),
|
|
1832
|
+
options.extraBody,
|
|
1833
|
+
input.extraBody
|
|
1834
|
+
);
|
|
1368
1835
|
const requestUrl = `${baseUrl}/v1beta/models/${options.model}:generateContent`;
|
|
1369
1836
|
const startedAt = Date.now();
|
|
1370
1837
|
const response = await executeAbortableProviderFetch(
|
|
@@ -1413,8 +1880,8 @@ function ensureGeminiRequestHasNoTools(request) {
|
|
|
1413
1880
|
if (request.tools === void 0 || request.tools.length === 0) {
|
|
1414
1881
|
return;
|
|
1415
1882
|
}
|
|
1416
|
-
throw new
|
|
1417
|
-
code:
|
|
1883
|
+
throw new import_errors23.AppError({
|
|
1884
|
+
code: import_errors22.AppErrorCode.ValidationFailed,
|
|
1418
1885
|
message: "gemini adapter does not support model-driven tool use; remove tools from the request or use another adapter",
|
|
1419
1886
|
statusCode: 400
|
|
1420
1887
|
});
|
|
@@ -1451,8 +1918,9 @@ function buildGeminiGenerationConfig(input) {
|
|
|
1451
1918
|
if (input.maxOutputTokens !== void 0) {
|
|
1452
1919
|
generationConfig.maxOutputTokens = input.maxOutputTokens;
|
|
1453
1920
|
}
|
|
1454
|
-
if (input.
|
|
1921
|
+
if (input.responseSchema !== void 0) {
|
|
1455
1922
|
generationConfig.responseMimeType = "application/json";
|
|
1923
|
+
generationConfig.responseSchema = input.responseSchema;
|
|
1456
1924
|
}
|
|
1457
1925
|
if (Object.keys(generationConfig).length === 0) {
|
|
1458
1926
|
return void 0;
|
|
@@ -1462,8 +1930,8 @@ function buildGeminiGenerationConfig(input) {
|
|
|
1462
1930
|
function extractGeminiCandidateText(response) {
|
|
1463
1931
|
const firstCandidate = response.candidates?.[0];
|
|
1464
1932
|
if (firstCandidate === void 0) {
|
|
1465
|
-
throw new
|
|
1466
|
-
code:
|
|
1933
|
+
throw new import_errors23.AppError({
|
|
1934
|
+
code: import_errors22.AppErrorCode.ValidationFailed,
|
|
1467
1935
|
message: "Gemini response contained no candidates",
|
|
1468
1936
|
statusCode: 502
|
|
1469
1937
|
});
|
|
@@ -1482,8 +1950,8 @@ function mapGeminiUsageToTokenUsage(response) {
|
|
|
1482
1950
|
}
|
|
1483
1951
|
|
|
1484
1952
|
// src/mock-adapter.ts
|
|
1485
|
-
var
|
|
1486
|
-
var
|
|
1953
|
+
var import_errors24 = require("@assemble-dev/shared-types/errors");
|
|
1954
|
+
var import_errors25 = require("@assemble-dev/shared-utils/errors");
|
|
1487
1955
|
var mockStreamChunkCount = 3;
|
|
1488
1956
|
function mockModel(options = {}) {
|
|
1489
1957
|
const remainingScriptedResponses = [...options.scriptedResponses ?? []];
|
|
@@ -1563,8 +2031,8 @@ function resolveNextMockResponseText(request, remainingScriptedResponses, respon
|
|
|
1563
2031
|
if (respond !== void 0) {
|
|
1564
2032
|
return respond(request);
|
|
1565
2033
|
}
|
|
1566
|
-
throw new
|
|
1567
|
-
code:
|
|
2034
|
+
throw new import_errors25.AppError({
|
|
2035
|
+
code: import_errors24.AppErrorCode.BadRequest,
|
|
1568
2036
|
message: "Mock model has no scripted responses left and no respond function was provided",
|
|
1569
2037
|
statusCode: 400
|
|
1570
2038
|
});
|
|
@@ -1652,60 +2120,274 @@ function calculateModelCallCostUsd(pricing, tokenUsage) {
|
|
|
1652
2120
|
}
|
|
1653
2121
|
|
|
1654
2122
|
// src/anthropic-batch.ts
|
|
1655
|
-
var
|
|
1656
|
-
var
|
|
1657
|
-
var
|
|
2123
|
+
var import_errors28 = require("@assemble-dev/shared-types/errors");
|
|
2124
|
+
var import_json8 = require("@assemble-dev/shared-types/json");
|
|
2125
|
+
var import_errors29 = require("@assemble-dev/shared-utils/errors");
|
|
1658
2126
|
|
|
1659
|
-
// src/anthropic-batch-
|
|
1660
|
-
var
|
|
1661
|
-
var
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
type:
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
2127
|
+
// src/anthropic-batch-structured.ts
|
|
2128
|
+
var import_errors26 = require("@assemble-dev/shared-types/errors");
|
|
2129
|
+
var import_json7 = require("@assemble-dev/shared-types/json");
|
|
2130
|
+
var import_errors27 = require("@assemble-dev/shared-utils/errors");
|
|
2131
|
+
var anthropicBatchJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
|
|
2132
|
+
function parseAnthropicBatchStructuredContent(content, schema) {
|
|
2133
|
+
const jsonValue = parseStructuredJsonText(
|
|
2134
|
+
content,
|
|
2135
|
+
import_json7.JsonValueSchema
|
|
2136
|
+
);
|
|
2137
|
+
const validationResult = schema.safeParse(jsonValue);
|
|
2138
|
+
if (!validationResult.success) {
|
|
2139
|
+
throw new import_errors27.AppError({
|
|
2140
|
+
code: import_errors26.AppErrorCode.ValidationFailed,
|
|
2141
|
+
message: "Anthropic batch structured output did not match the expected schema",
|
|
2142
|
+
statusCode: 422,
|
|
2143
|
+
details: { validationErrorMessage: validationResult.error.message }
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
return validationResult.data;
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// src/anthropic-batch-request.ts
|
|
2150
|
+
function mapBatchMessageRequestToApiBatchRequest(request) {
|
|
2151
|
+
const params = {
|
|
2152
|
+
model: request.model,
|
|
2153
|
+
max_tokens: request.maxOutputTokens ?? defaultAnthropicMaxOutputTokens,
|
|
2154
|
+
messages: request.messages.filter((message) => message.role !== "system").map(mapChatMessageToBatchApiRequestMessage)
|
|
2155
|
+
};
|
|
2156
|
+
applyBatchSystemField(params, request);
|
|
2157
|
+
applyBatchStructuredOutputConfig(params, request);
|
|
2158
|
+
if (request.temperature !== void 0) {
|
|
2159
|
+
params.temperature = request.temperature;
|
|
2160
|
+
}
|
|
2161
|
+
return { custom_id: request.customId, params };
|
|
2162
|
+
}
|
|
2163
|
+
function applyBatchStructuredOutputConfig(params, request) {
|
|
2164
|
+
if (request.structuredOutputJsonSchema === void 0) {
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
params.output_config = {
|
|
2168
|
+
format: {
|
|
2169
|
+
type: "json_schema",
|
|
2170
|
+
schema: sanitizeJsonSchemaForAnthropicStructuredOutput(
|
|
2171
|
+
request.structuredOutputJsonSchema
|
|
2172
|
+
)
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
function applyBatchSystemField(params, request) {
|
|
2177
|
+
const systemText = buildBatchSystemText(request);
|
|
2178
|
+
if (systemText.length === 0) {
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
if (request.promptCaching !== "auto") {
|
|
2182
|
+
params.system = systemText;
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
params.system = [
|
|
2186
|
+
{ type: "text", text: systemText, cache_control: { type: "ephemeral" } }
|
|
2187
|
+
];
|
|
2188
|
+
}
|
|
2189
|
+
function buildBatchSystemText(request) {
|
|
2190
|
+
const joinedSystemText = joinBatchSystemMessageText(request.messages);
|
|
2191
|
+
if (request.requireJsonResponse !== true || request.structuredOutputJsonSchema !== void 0) {
|
|
2192
|
+
return joinedSystemText;
|
|
2193
|
+
}
|
|
2194
|
+
if (joinedSystemText.length === 0) {
|
|
2195
|
+
return anthropicBatchJsonOnlySystemInstruction;
|
|
2196
|
+
}
|
|
2197
|
+
return `${anthropicBatchJsonOnlySystemInstruction}
|
|
2198
|
+
|
|
2199
|
+
${joinedSystemText}`;
|
|
2200
|
+
}
|
|
2201
|
+
function joinBatchSystemMessageText(messages) {
|
|
2202
|
+
return messages.filter((message) => message.role === "system").map(extractMessageText).join("\n\n");
|
|
2203
|
+
}
|
|
2204
|
+
function mapChatMessageToBatchApiRequestMessage(message) {
|
|
2205
|
+
if (message.role === "assistant") {
|
|
2206
|
+
return { role: "assistant", content: extractMessageText(message) };
|
|
2207
|
+
}
|
|
2208
|
+
if (message.role === "tool") {
|
|
2209
|
+
return { role: "user", content: buildBatchToolResultText(message) };
|
|
2210
|
+
}
|
|
2211
|
+
return { role: "user", content: extractMessageText(message) };
|
|
2212
|
+
}
|
|
2213
|
+
function buildBatchToolResultText(message) {
|
|
2214
|
+
if (message.toolName === void 0) {
|
|
2215
|
+
return `Tool result: ${extractMessageText(message)}`;
|
|
2216
|
+
}
|
|
2217
|
+
return `Tool result (${message.toolName}): ${extractMessageText(message)}`;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
// src/anthropic-batch-trace-events.ts
|
|
2221
|
+
var import_ids = require("@assemble-dev/shared-utils/ids");
|
|
2222
|
+
var anthropicBatchTraceWorkflowName = "anthropic-message-batch";
|
|
2223
|
+
function createAnthropicBatchTraceEmitter(input) {
|
|
2224
|
+
if (input.onTraceEvent === void 0) {
|
|
2225
|
+
return createNoopAnthropicBatchTraceEmitter();
|
|
2226
|
+
}
|
|
2227
|
+
const onTraceEvent = input.onTraceEvent;
|
|
2228
|
+
const runId = (0, import_ids.generateRunId)();
|
|
2229
|
+
const completedBatchIds = /* @__PURE__ */ new Set();
|
|
2230
|
+
const submittedEventIdByBatchId = /* @__PURE__ */ new Map();
|
|
2231
|
+
const modelByBatchScopedCustomId = /* @__PURE__ */ new Map();
|
|
2232
|
+
function buildBatchTraceEventBase(parentEventId) {
|
|
2233
|
+
return {
|
|
2234
|
+
id: (0, import_ids.generateTraceEventId)(),
|
|
2235
|
+
runId,
|
|
2236
|
+
workflowName: anthropicBatchTraceWorkflowName,
|
|
2237
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2238
|
+
parentEventId,
|
|
2239
|
+
redacted: false
|
|
2240
|
+
};
|
|
2241
|
+
}
|
|
2242
|
+
function stampMetadataOntoEvent(event) {
|
|
2243
|
+
if (input.metadata === void 0) {
|
|
2244
|
+
return event;
|
|
2245
|
+
}
|
|
2246
|
+
return { ...event, metadata: input.metadata };
|
|
2247
|
+
}
|
|
2248
|
+
function emitBatchSubmittedEvent(batch, requestSummaries) {
|
|
2249
|
+
const submittedEvent = {
|
|
2250
|
+
...buildBatchTraceEventBase(null),
|
|
2251
|
+
type: "batch.submitted",
|
|
2252
|
+
batchId: batch.id,
|
|
2253
|
+
requestCount: requestSummaries.length,
|
|
2254
|
+
models: requestSummaries.map((requestSummary) => requestSummary.model)
|
|
2255
|
+
};
|
|
2256
|
+
submittedEventIdByBatchId.set(batch.id, submittedEvent.id);
|
|
2257
|
+
for (const requestSummary of requestSummaries) {
|
|
2258
|
+
modelByBatchScopedCustomId.set(
|
|
2259
|
+
buildBatchScopedCustomIdKey(batch.id, requestSummary.customId),
|
|
2260
|
+
requestSummary.model
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
onTraceEvent(stampMetadataOntoEvent(submittedEvent));
|
|
2264
|
+
}
|
|
2265
|
+
function emitBatchCompletedEventOnce(batch) {
|
|
2266
|
+
if (batch.processingStatus !== "ended" || completedBatchIds.has(batch.id)) {
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
completedBatchIds.add(batch.id);
|
|
2270
|
+
const completedEvent = {
|
|
2271
|
+
...buildBatchTraceEventBase(
|
|
2272
|
+
submittedEventIdByBatchId.get(batch.id) ?? null
|
|
2273
|
+
),
|
|
2274
|
+
type: "batch.completed",
|
|
2275
|
+
batchId: batch.id,
|
|
2276
|
+
requestCounts: batch.requestCounts,
|
|
2277
|
+
latencyMs: calculateBatchLatencyMs(batch)
|
|
2278
|
+
};
|
|
2279
|
+
onTraceEvent(stampMetadataOntoEvent(completedEvent));
|
|
2280
|
+
}
|
|
2281
|
+
function emitBatchResultEvent(batchId, result) {
|
|
2282
|
+
const resultEvent = buildBatchResultEvent(
|
|
2283
|
+
buildBatchTraceEventBase(submittedEventIdByBatchId.get(batchId) ?? null),
|
|
2284
|
+
batchId,
|
|
2285
|
+
result,
|
|
2286
|
+
modelByBatchScopedCustomId.get(
|
|
2287
|
+
buildBatchScopedCustomIdKey(batchId, result.customId)
|
|
2288
|
+
)
|
|
2289
|
+
);
|
|
2290
|
+
onTraceEvent(stampMetadataOntoEvent(resultEvent));
|
|
2291
|
+
}
|
|
2292
|
+
return {
|
|
2293
|
+
emitBatchSubmittedEvent,
|
|
2294
|
+
emitBatchCompletedEventOnce,
|
|
2295
|
+
emitBatchResultEvent
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
function createNoopAnthropicBatchTraceEmitter() {
|
|
2299
|
+
return {
|
|
2300
|
+
emitBatchSubmittedEvent() {
|
|
2301
|
+
},
|
|
2302
|
+
emitBatchCompletedEventOnce() {
|
|
2303
|
+
},
|
|
2304
|
+
emitBatchResultEvent() {
|
|
2305
|
+
}
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
function buildBatchScopedCustomIdKey(batchId, customId) {
|
|
2309
|
+
return `${batchId}:${customId}`;
|
|
2310
|
+
}
|
|
2311
|
+
function calculateBatchLatencyMs(batch) {
|
|
2312
|
+
const createdAtMs = Date.parse(batch.createdAt);
|
|
2313
|
+
const endedAtMs = batch.endedAt === null ? Date.now() : Date.parse(batch.endedAt);
|
|
2314
|
+
if (Number.isNaN(createdAtMs) || Number.isNaN(endedAtMs)) {
|
|
2315
|
+
return 0;
|
|
2316
|
+
}
|
|
2317
|
+
return Math.max(0, endedAtMs - createdAtMs);
|
|
2318
|
+
}
|
|
2319
|
+
function buildBatchResultEvent(eventBase, batchId, result, modelName) {
|
|
2320
|
+
if (result.outcome.type !== "succeeded") {
|
|
2321
|
+
return {
|
|
2322
|
+
...eventBase,
|
|
2323
|
+
type: "batch.result",
|
|
2324
|
+
batchId,
|
|
2325
|
+
customId: result.customId,
|
|
2326
|
+
outcomeType: result.outcome.type,
|
|
2327
|
+
costUsd: null
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
return {
|
|
2331
|
+
...eventBase,
|
|
2332
|
+
type: "batch.result",
|
|
2333
|
+
batchId,
|
|
2334
|
+
customId: result.customId,
|
|
2335
|
+
outcomeType: "succeeded",
|
|
2336
|
+
tokenUsage: result.outcome.tokenUsage,
|
|
2337
|
+
costUsd: modelName === void 0 ? null : estimateModelCallCostUsd(modelName, result.outcome.tokenUsage)
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
// src/anthropic-batch-types.ts
|
|
2342
|
+
var import_zod8 = require("zod");
|
|
2343
|
+
var AnthropicMessageBatchResponseSchema = import_zod8.z.object({
|
|
2344
|
+
id: import_zod8.z.string(),
|
|
2345
|
+
processing_status: import_zod8.z.enum(["in_progress", "canceling", "ended"]),
|
|
2346
|
+
request_counts: import_zod8.z.object({
|
|
2347
|
+
processing: import_zod8.z.number().int().nonnegative(),
|
|
2348
|
+
succeeded: import_zod8.z.number().int().nonnegative(),
|
|
2349
|
+
errored: import_zod8.z.number().int().nonnegative(),
|
|
2350
|
+
canceled: import_zod8.z.number().int().nonnegative(),
|
|
2351
|
+
expired: import_zod8.z.number().int().nonnegative()
|
|
2352
|
+
}),
|
|
2353
|
+
results_url: import_zod8.z.string().nullable(),
|
|
2354
|
+
created_at: import_zod8.z.string(),
|
|
2355
|
+
ended_at: import_zod8.z.string().nullable()
|
|
2356
|
+
});
|
|
2357
|
+
var AnthropicBatchContentBlockSchema = import_zod8.z.discriminatedUnion("type", [
|
|
2358
|
+
import_zod8.z.object({
|
|
2359
|
+
type: import_zod8.z.literal("text"),
|
|
2360
|
+
text: import_zod8.z.string()
|
|
2361
|
+
}),
|
|
2362
|
+
import_zod8.z.object({
|
|
2363
|
+
type: import_zod8.z.literal("thinking"),
|
|
2364
|
+
thinking: import_zod8.z.string().optional(),
|
|
2365
|
+
signature: import_zod8.z.string().optional()
|
|
2366
|
+
}),
|
|
2367
|
+
import_zod8.z.object({
|
|
2368
|
+
type: import_zod8.z.literal("redacted_thinking"),
|
|
2369
|
+
data: import_zod8.z.string().optional()
|
|
2370
|
+
})
|
|
2371
|
+
]);
|
|
2372
|
+
var AnthropicBatchResultLineSchema = import_zod8.z.object({
|
|
2373
|
+
custom_id: import_zod8.z.string(),
|
|
2374
|
+
result: import_zod8.z.discriminatedUnion("type", [
|
|
2375
|
+
import_zod8.z.object({
|
|
2376
|
+
type: import_zod8.z.literal("succeeded"),
|
|
2377
|
+
message: import_zod8.z.object({
|
|
2378
|
+
content: import_zod8.z.array(AnthropicBatchContentBlockSchema),
|
|
2379
|
+
usage: AnthropicUsageSchema
|
|
2380
|
+
})
|
|
1706
2381
|
}),
|
|
1707
|
-
|
|
1708
|
-
|
|
2382
|
+
import_zod8.z.object({
|
|
2383
|
+
type: import_zod8.z.literal("errored"),
|
|
2384
|
+
error: import_zod8.z.object({
|
|
2385
|
+
type: import_zod8.z.string(),
|
|
2386
|
+
message: import_zod8.z.string()
|
|
2387
|
+
})
|
|
2388
|
+
}),
|
|
2389
|
+
import_zod8.z.object({ type: import_zod8.z.literal("canceled") }),
|
|
2390
|
+
import_zod8.z.object({ type: import_zod8.z.literal("expired") })
|
|
1709
2391
|
])
|
|
1710
2392
|
});
|
|
1711
2393
|
function mapAnthropicMessageBatchResponseToMessageBatch(response) {
|
|
@@ -1759,19 +2441,26 @@ var defaultAnthropicBatchPollIntervalMs = 5e3;
|
|
|
1759
2441
|
var defaultAnthropicBatchTimeoutMs = 36e5;
|
|
1760
2442
|
var anthropicBatchApiVersion = "2023-06-01";
|
|
1761
2443
|
function createAnthropicBatchClient(options) {
|
|
2444
|
+
const clientContext = {
|
|
2445
|
+
options,
|
|
2446
|
+
traceEmitter: createAnthropicBatchTraceEmitter({
|
|
2447
|
+
onTraceEvent: options.onTraceEvent,
|
|
2448
|
+
metadata: options.metadata
|
|
2449
|
+
})
|
|
2450
|
+
};
|
|
1762
2451
|
return {
|
|
1763
2452
|
async submitMessageBatch(requests) {
|
|
1764
|
-
return await submitAnthropicMessageBatch(
|
|
2453
|
+
return await submitAnthropicMessageBatch(clientContext, requests);
|
|
1765
2454
|
},
|
|
1766
2455
|
async fetchMessageBatch(batchId) {
|
|
1767
|
-
return await fetchAnthropicMessageBatch(
|
|
2456
|
+
return await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1768
2457
|
},
|
|
1769
2458
|
async listMessageBatchResults(batchId) {
|
|
1770
|
-
return await listAnthropicMessageBatchResults(
|
|
2459
|
+
return await listAnthropicMessageBatchResults(clientContext, batchId);
|
|
1771
2460
|
},
|
|
1772
2461
|
async waitForMessageBatchCompletion(batchId, waitOptions) {
|
|
1773
2462
|
return await waitForAnthropicMessageBatchCompletion(
|
|
1774
|
-
|
|
2463
|
+
clientContext,
|
|
1775
2464
|
batchId,
|
|
1776
2465
|
waitOptions
|
|
1777
2466
|
);
|
|
@@ -1798,8 +2487,8 @@ function buildAnthropicBatchRequestHeaders(apiKey) {
|
|
|
1798
2487
|
"anthropic-version": anthropicBatchApiVersion
|
|
1799
2488
|
};
|
|
1800
2489
|
}
|
|
1801
|
-
async function submitAnthropicMessageBatch(
|
|
1802
|
-
const context = resolveAnthropicBatchRequestContext(options);
|
|
2490
|
+
async function submitAnthropicMessageBatch(clientContext, requests) {
|
|
2491
|
+
const context = resolveAnthropicBatchRequestContext(clientContext.options);
|
|
1803
2492
|
const response = await context.fetchImplementation(
|
|
1804
2493
|
`${context.baseUrl}/v1/messages/batches`,
|
|
1805
2494
|
{
|
|
@@ -1810,10 +2499,18 @@ async function submitAnthropicMessageBatch(options, requests) {
|
|
|
1810
2499
|
})
|
|
1811
2500
|
}
|
|
1812
2501
|
);
|
|
1813
|
-
|
|
2502
|
+
const batch = await parseAnthropicMessageBatchHttpResponse(response);
|
|
2503
|
+
clientContext.traceEmitter.emitBatchSubmittedEvent(
|
|
2504
|
+
batch,
|
|
2505
|
+
requests.map((request) => ({
|
|
2506
|
+
customId: request.customId,
|
|
2507
|
+
model: request.model
|
|
2508
|
+
}))
|
|
2509
|
+
);
|
|
2510
|
+
return batch;
|
|
1814
2511
|
}
|
|
1815
|
-
async function fetchAnthropicMessageBatch(
|
|
1816
|
-
const context = resolveAnthropicBatchRequestContext(options);
|
|
2512
|
+
async function fetchAnthropicMessageBatch(clientContext, batchId) {
|
|
2513
|
+
const context = resolveAnthropicBatchRequestContext(clientContext.options);
|
|
1817
2514
|
const response = await context.fetchImplementation(
|
|
1818
2515
|
`${context.baseUrl}/v1/messages/batches/${batchId}`,
|
|
1819
2516
|
{
|
|
@@ -1821,7 +2518,9 @@ async function fetchAnthropicMessageBatch(options, batchId) {
|
|
|
1821
2518
|
headers: buildAnthropicBatchRequestHeaders(context.apiKey)
|
|
1822
2519
|
}
|
|
1823
2520
|
);
|
|
1824
|
-
|
|
2521
|
+
const batch = await parseAnthropicMessageBatchHttpResponse(response);
|
|
2522
|
+
clientContext.traceEmitter.emitBatchCompletedEventOnce(batch);
|
|
2523
|
+
return batch;
|
|
1825
2524
|
}
|
|
1826
2525
|
async function parseAnthropicMessageBatchHttpResponse(response) {
|
|
1827
2526
|
if (!response.ok) {
|
|
@@ -1835,17 +2534,17 @@ async function parseAnthropicMessageBatchHttpResponse(response) {
|
|
|
1835
2534
|
}
|
|
1836
2535
|
return mapAnthropicMessageBatchResponseToMessageBatch(parseResult.data);
|
|
1837
2536
|
}
|
|
1838
|
-
async function listAnthropicMessageBatchResults(
|
|
1839
|
-
const batch = await fetchAnthropicMessageBatch(
|
|
2537
|
+
async function listAnthropicMessageBatchResults(clientContext, batchId) {
|
|
2538
|
+
const batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1840
2539
|
if (batch.resultsUrl === null) {
|
|
1841
|
-
throw new
|
|
1842
|
-
code:
|
|
2540
|
+
throw new import_errors29.AppError({
|
|
2541
|
+
code: import_errors28.AppErrorCode.Conflict,
|
|
1843
2542
|
message: `Anthropic message batch ${batchId} has no results because processing has not ended`,
|
|
1844
2543
|
statusCode: 409,
|
|
1845
2544
|
details: { batchId, processingStatus: batch.processingStatus }
|
|
1846
2545
|
});
|
|
1847
2546
|
}
|
|
1848
|
-
const context = resolveAnthropicBatchRequestContext(options);
|
|
2547
|
+
const context = resolveAnthropicBatchRequestContext(clientContext.options);
|
|
1849
2548
|
const response = await context.fetchImplementation(batch.resultsUrl, {
|
|
1850
2549
|
method: "GET",
|
|
1851
2550
|
headers: buildAnthropicBatchRequestHeaders(context.apiKey)
|
|
@@ -1853,7 +2552,11 @@ async function listAnthropicMessageBatchResults(options, batchId) {
|
|
|
1853
2552
|
if (!response.ok) {
|
|
1854
2553
|
throw buildProviderStatusError("Anthropic", response.status);
|
|
1855
2554
|
}
|
|
1856
|
-
|
|
2555
|
+
const results = parseAnthropicBatchResultsJsonl(await response.text());
|
|
2556
|
+
for (const result of results) {
|
|
2557
|
+
clientContext.traceEmitter.emitBatchResultEvent(batchId, result);
|
|
2558
|
+
}
|
|
2559
|
+
return results;
|
|
1857
2560
|
}
|
|
1858
2561
|
function parseAnthropicBatchResultsJsonl(jsonlText) {
|
|
1859
2562
|
return jsonlText.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map(parseAnthropicBatchResultLine);
|
|
@@ -1872,7 +2575,7 @@ function parseAnthropicBatchResultLine(line) {
|
|
|
1872
2575
|
}
|
|
1873
2576
|
function parseAnthropicBatchResultLineJson(line) {
|
|
1874
2577
|
try {
|
|
1875
|
-
return
|
|
2578
|
+
return import_json8.JsonValueSchema.parse(JSON.parse(line));
|
|
1876
2579
|
} catch {
|
|
1877
2580
|
throw buildProviderInvalidResponseError(
|
|
1878
2581
|
"Anthropic",
|
|
@@ -1880,81 +2583,811 @@ function parseAnthropicBatchResultLineJson(line) {
|
|
|
1880
2583
|
);
|
|
1881
2584
|
}
|
|
1882
2585
|
}
|
|
1883
|
-
async function waitForAnthropicMessageBatchCompletion(
|
|
2586
|
+
async function waitForAnthropicMessageBatchCompletion(clientContext, batchId, waitOptions) {
|
|
1884
2587
|
const pollIntervalMs = waitOptions?.pollIntervalMs ?? defaultAnthropicBatchPollIntervalMs;
|
|
1885
2588
|
const timeoutMs = waitOptions?.timeoutMs ?? defaultAnthropicBatchTimeoutMs;
|
|
1886
2589
|
const waitFor = waitOptions?.waitFor ?? waitForMilliseconds;
|
|
1887
2590
|
const startedAt = Date.now();
|
|
1888
|
-
let batch = await fetchAnthropicMessageBatch(
|
|
2591
|
+
let batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1889
2592
|
while (batch.processingStatus !== "ended") {
|
|
1890
2593
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
1891
2594
|
throw buildAnthropicBatchTimeoutError(batchId, timeoutMs);
|
|
1892
2595
|
}
|
|
1893
2596
|
await waitFor(pollIntervalMs);
|
|
1894
|
-
batch = await fetchAnthropicMessageBatch(
|
|
2597
|
+
batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1895
2598
|
}
|
|
1896
2599
|
return batch;
|
|
1897
2600
|
}
|
|
1898
2601
|
function buildAnthropicBatchTimeoutError(batchId, timeoutMs) {
|
|
1899
|
-
return new
|
|
1900
|
-
code:
|
|
2602
|
+
return new import_errors29.AppError({
|
|
2603
|
+
code: import_errors28.AppErrorCode.ServiceUnavailable,
|
|
1901
2604
|
message: `Anthropic message batch ${batchId} did not complete within ${String(timeoutMs)}ms`,
|
|
1902
2605
|
statusCode: 504,
|
|
1903
2606
|
details: { batchId, timeoutMs }
|
|
1904
2607
|
});
|
|
1905
2608
|
}
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2609
|
+
|
|
2610
|
+
// src/azure-openai-adapter.ts
|
|
2611
|
+
var import_errors32 = require("@assemble-dev/shared-types/errors");
|
|
2612
|
+
var import_errors33 = require("@assemble-dev/shared-utils/errors");
|
|
2613
|
+
|
|
2614
|
+
// src/azure-openai-response-parsing.ts
|
|
2615
|
+
var import_zod9 = require("zod");
|
|
2616
|
+
var import_errors30 = require("@assemble-dev/shared-types/errors");
|
|
2617
|
+
var import_json9 = require("@assemble-dev/shared-types/json");
|
|
2618
|
+
var import_errors31 = require("@assemble-dev/shared-utils/errors");
|
|
2619
|
+
var AzureOpenAiUsageSchema = import_zod9.z.object({
|
|
2620
|
+
prompt_tokens: import_zod9.z.number().int().nonnegative(),
|
|
2621
|
+
completion_tokens: import_zod9.z.number().int().nonnegative(),
|
|
2622
|
+
total_tokens: import_zod9.z.number().int().nonnegative()
|
|
2623
|
+
});
|
|
2624
|
+
var AzureOpenAiResponseToolCallSchema = import_zod9.z.object({
|
|
2625
|
+
id: import_zod9.z.string(),
|
|
2626
|
+
function: import_zod9.z.object({
|
|
2627
|
+
name: import_zod9.z.string(),
|
|
2628
|
+
arguments: import_zod9.z.string()
|
|
2629
|
+
})
|
|
2630
|
+
});
|
|
2631
|
+
var AzureOpenAiChatCompletionResponseSchema = import_zod9.z.object({
|
|
2632
|
+
choices: import_zod9.z.array(
|
|
2633
|
+
import_zod9.z.object({
|
|
2634
|
+
message: import_zod9.z.object({
|
|
2635
|
+
content: import_zod9.z.string().nullable(),
|
|
2636
|
+
tool_calls: import_zod9.z.array(AzureOpenAiResponseToolCallSchema).optional()
|
|
2637
|
+
}),
|
|
2638
|
+
finish_reason: import_zod9.z.string().nullable().optional()
|
|
2639
|
+
})
|
|
2640
|
+
).min(1),
|
|
2641
|
+
usage: AzureOpenAiUsageSchema
|
|
2642
|
+
});
|
|
2643
|
+
function mapAzureOpenAiResponseToChatCompletionResult(response, latencyMs) {
|
|
2644
|
+
const toolCalls = extractAzureOpenAiToolCalls(response);
|
|
2645
|
+
const result = {
|
|
2646
|
+
content: extractAzureOpenAiMessageContent(response, toolCalls.length > 0),
|
|
2647
|
+
tokenUsage: mapAzureOpenAiUsageToTokenUsage(response.usage),
|
|
2648
|
+
latencyMs
|
|
1911
2649
|
};
|
|
1912
|
-
const
|
|
1913
|
-
if (
|
|
1914
|
-
|
|
2650
|
+
const finishReason = response.choices[0]?.finish_reason;
|
|
2651
|
+
if (toolCalls.length > 0) {
|
|
2652
|
+
result.toolCalls = toolCalls;
|
|
1915
2653
|
}
|
|
1916
|
-
if (
|
|
1917
|
-
|
|
2654
|
+
if (finishReason !== null && finishReason !== void 0) {
|
|
2655
|
+
result.stopReason = normalizeAzureOpenAiFinishReason(finishReason);
|
|
1918
2656
|
}
|
|
1919
|
-
return
|
|
2657
|
+
return result;
|
|
1920
2658
|
}
|
|
1921
|
-
function
|
|
1922
|
-
|
|
2659
|
+
function extractAzureOpenAiToolCalls(response) {
|
|
2660
|
+
const responseToolCalls = response.choices[0]?.message.tool_calls ?? [];
|
|
2661
|
+
return responseToolCalls.map((responseToolCall) => ({
|
|
2662
|
+
id: responseToolCall.id,
|
|
2663
|
+
toolName: responseToolCall.function.name,
|
|
2664
|
+
input: parseAzureOpenAiToolArgumentsJson(responseToolCall.function.arguments)
|
|
2665
|
+
}));
|
|
1923
2666
|
}
|
|
1924
|
-
function
|
|
1925
|
-
if (
|
|
1926
|
-
return {
|
|
2667
|
+
function parseAzureOpenAiToolArgumentsJson(argumentsJsonText) {
|
|
2668
|
+
if (argumentsJsonText.trim().length === 0) {
|
|
2669
|
+
return {};
|
|
1927
2670
|
}
|
|
1928
|
-
|
|
1929
|
-
return
|
|
2671
|
+
try {
|
|
2672
|
+
return import_json9.JsonValueSchema.parse(JSON.parse(argumentsJsonText));
|
|
2673
|
+
} catch (error) {
|
|
2674
|
+
throw new import_errors31.AppError({
|
|
2675
|
+
code: import_errors30.AppErrorCode.ValidationFailed,
|
|
2676
|
+
message: "Azure OpenAI tool call arguments were not valid JSON",
|
|
2677
|
+
statusCode: 502,
|
|
2678
|
+
cause: error instanceof Error ? error : void 0
|
|
2679
|
+
});
|
|
1930
2680
|
}
|
|
1931
|
-
return { role: "user", content: extractMessageText(message) };
|
|
1932
2681
|
}
|
|
1933
|
-
function
|
|
1934
|
-
if (
|
|
1935
|
-
return
|
|
2682
|
+
function normalizeAzureOpenAiFinishReason(finishReason) {
|
|
2683
|
+
if (finishReason === "tool_calls") {
|
|
2684
|
+
return "tool_use";
|
|
1936
2685
|
}
|
|
1937
|
-
|
|
2686
|
+
if (finishReason === "length") {
|
|
2687
|
+
return "max_tokens";
|
|
2688
|
+
}
|
|
2689
|
+
return "end_turn";
|
|
2690
|
+
}
|
|
2691
|
+
function extractAzureOpenAiMessageContent(response, allowEmptyContent) {
|
|
2692
|
+
const content = response.choices[0]?.message.content;
|
|
2693
|
+
if (content === void 0 || content === null) {
|
|
2694
|
+
if (allowEmptyContent) {
|
|
2695
|
+
return "";
|
|
2696
|
+
}
|
|
2697
|
+
throw new import_errors31.AppError({
|
|
2698
|
+
code: import_errors30.AppErrorCode.ValidationFailed,
|
|
2699
|
+
message: "Azure OpenAI response contained no message content",
|
|
2700
|
+
statusCode: 502
|
|
2701
|
+
});
|
|
2702
|
+
}
|
|
2703
|
+
return content;
|
|
2704
|
+
}
|
|
2705
|
+
function mapAzureOpenAiUsageToTokenUsage(usage) {
|
|
2706
|
+
return {
|
|
2707
|
+
inputTokens: usage.prompt_tokens,
|
|
2708
|
+
outputTokens: usage.completion_tokens,
|
|
2709
|
+
totalTokens: usage.total_tokens
|
|
2710
|
+
};
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// src/azure-openai-stream.ts
|
|
2714
|
+
var import_zod10 = require("zod");
|
|
2715
|
+
var AzureOpenAiStreamToolCallDeltaSchema = import_zod10.z.object({
|
|
2716
|
+
index: import_zod10.z.number().int().nonnegative(),
|
|
2717
|
+
id: import_zod10.z.string().optional(),
|
|
2718
|
+
function: import_zod10.z.object({
|
|
2719
|
+
name: import_zod10.z.string().optional(),
|
|
2720
|
+
arguments: import_zod10.z.string().optional()
|
|
2721
|
+
}).optional()
|
|
2722
|
+
});
|
|
2723
|
+
var AzureOpenAiStreamChunkSchema = import_zod10.z.object({
|
|
2724
|
+
choices: import_zod10.z.array(
|
|
2725
|
+
import_zod10.z.object({
|
|
2726
|
+
delta: import_zod10.z.object({
|
|
2727
|
+
content: import_zod10.z.string().nullable().optional(),
|
|
2728
|
+
tool_calls: import_zod10.z.array(AzureOpenAiStreamToolCallDeltaSchema).optional()
|
|
2729
|
+
}).optional(),
|
|
2730
|
+
finish_reason: import_zod10.z.string().nullable().optional()
|
|
2731
|
+
})
|
|
2732
|
+
).optional(),
|
|
2733
|
+
usage: AzureOpenAiUsageSchema.nullable().optional()
|
|
2734
|
+
});
|
|
2735
|
+
async function* streamAzureOpenAiChatCompletion(input) {
|
|
2736
|
+
const startedAt = Date.now();
|
|
2737
|
+
const response = await establishAzureOpenAiStreamResponse(input);
|
|
2738
|
+
if (response.body === null) {
|
|
2739
|
+
throw buildProviderInvalidResponseError("Azure OpenAI", "stream body");
|
|
2740
|
+
}
|
|
2741
|
+
yield* parseAzureOpenAiStreamEvents(
|
|
2742
|
+
response.body,
|
|
2743
|
+
startedAt,
|
|
2744
|
+
input.abortSignal
|
|
2745
|
+
);
|
|
2746
|
+
}
|
|
2747
|
+
async function establishAzureOpenAiStreamResponse(input) {
|
|
2748
|
+
return await executeProviderCallWithRetry(input.retry, async () => {
|
|
2749
|
+
const response = await executeAbortableProviderFetch(
|
|
2750
|
+
"Azure OpenAI",
|
|
2751
|
+
async () => await input.fetchImplementation(input.url, {
|
|
2752
|
+
method: "POST",
|
|
2753
|
+
headers: input.headers,
|
|
2754
|
+
body: JSON.stringify(input.body),
|
|
2755
|
+
signal: input.abortSignal
|
|
2756
|
+
})
|
|
2757
|
+
);
|
|
2758
|
+
if (!response.ok) {
|
|
2759
|
+
throw buildProviderStatusError("Azure OpenAI", response.status);
|
|
2760
|
+
}
|
|
2761
|
+
return response;
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
async function* parseAzureOpenAiStreamEvents(body, startedAt, abortSignal) {
|
|
2765
|
+
const accumulator = createAzureOpenAiStreamAccumulator();
|
|
2766
|
+
for await (const dataText of readServerSentEventDataLines2(body, abortSignal)) {
|
|
2767
|
+
if (dataText === "[DONE]") {
|
|
2768
|
+
yield {
|
|
2769
|
+
type: "completed",
|
|
2770
|
+
result: buildAzureOpenAiStreamResult(
|
|
2771
|
+
accumulator,
|
|
2772
|
+
Date.now() - startedAt
|
|
2773
|
+
)
|
|
2774
|
+
};
|
|
2775
|
+
return;
|
|
2776
|
+
}
|
|
2777
|
+
const parsedChunk = AzureOpenAiStreamChunkSchema.safeParse(
|
|
2778
|
+
JSON.parse(dataText)
|
|
2779
|
+
);
|
|
2780
|
+
if (!parsedChunk.success) {
|
|
2781
|
+
continue;
|
|
2782
|
+
}
|
|
2783
|
+
yield* applyAzureOpenAiStreamChunk(parsedChunk.data, accumulator);
|
|
2784
|
+
}
|
|
2785
|
+
throw buildProviderInvalidResponseError("Azure OpenAI", "stream termination");
|
|
2786
|
+
}
|
|
2787
|
+
async function* readServerSentEventDataLines2(body, abortSignal) {
|
|
2788
|
+
const reader = body.getReader();
|
|
2789
|
+
const decoder = new TextDecoder();
|
|
2790
|
+
let bufferedText = "";
|
|
2791
|
+
try {
|
|
2792
|
+
for (; ; ) {
|
|
2793
|
+
if (abortSignal?.aborted === true) {
|
|
2794
|
+
await reader.cancel();
|
|
2795
|
+
throw buildProviderCanceledError("Azure OpenAI");
|
|
2796
|
+
}
|
|
2797
|
+
const readResult = await reader.read();
|
|
2798
|
+
if (readResult.done) {
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
bufferedText += decoder.decode(readResult.value, { stream: true });
|
|
2802
|
+
const lines = bufferedText.split("\n");
|
|
2803
|
+
bufferedText = lines.pop() ?? "";
|
|
2804
|
+
for (const line of lines) {
|
|
2805
|
+
if (line.startsWith("data:")) {
|
|
2806
|
+
const dataText = line.slice("data:".length).trim();
|
|
2807
|
+
if (dataText.length > 0) {
|
|
2808
|
+
yield dataText;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
} catch (error) {
|
|
2814
|
+
if (error instanceof Error && isAbortError(error)) {
|
|
2815
|
+
throw buildProviderCanceledError("Azure OpenAI");
|
|
2816
|
+
}
|
|
2817
|
+
throw error;
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
function createAzureOpenAiStreamAccumulator() {
|
|
2821
|
+
return {
|
|
2822
|
+
contentText: "",
|
|
2823
|
+
toolCallsByIndex: /* @__PURE__ */ new Map()
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
function* applyAzureOpenAiStreamChunk(chunk, accumulator) {
|
|
2827
|
+
if (chunk.usage !== null && chunk.usage !== void 0) {
|
|
2828
|
+
accumulator.usage = chunk.usage;
|
|
2829
|
+
}
|
|
2830
|
+
const choice = chunk.choices?.[0];
|
|
2831
|
+
if (choice === void 0) {
|
|
2832
|
+
return;
|
|
2833
|
+
}
|
|
2834
|
+
if (choice.finish_reason !== null && choice.finish_reason !== void 0) {
|
|
2835
|
+
accumulator.finishReason = choice.finish_reason;
|
|
2836
|
+
}
|
|
2837
|
+
if (choice.delta === void 0) {
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
const contentDelta = choice.delta.content;
|
|
2841
|
+
if (contentDelta !== null && contentDelta !== void 0 && contentDelta.length > 0) {
|
|
2842
|
+
accumulator.contentText += contentDelta;
|
|
2843
|
+
yield { type: "text_delta", text: contentDelta };
|
|
2844
|
+
}
|
|
2845
|
+
for (const toolCallDelta of choice.delta.tool_calls ?? []) {
|
|
2846
|
+
yield* applyAzureOpenAiToolCallDelta(toolCallDelta, accumulator);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
function* applyAzureOpenAiToolCallDelta(toolCallDelta, accumulator) {
|
|
2850
|
+
const existingToolCall = accumulator.toolCallsByIndex.get(toolCallDelta.index);
|
|
2851
|
+
if (existingToolCall === void 0) {
|
|
2852
|
+
yield* startAzureOpenAiToolCallAccumulation(toolCallDelta, accumulator);
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
const argumentsDelta = toolCallDelta.function?.arguments;
|
|
2856
|
+
if (argumentsDelta !== void 0 && argumentsDelta.length > 0) {
|
|
2857
|
+
existingToolCall.argumentsJsonText += argumentsDelta;
|
|
2858
|
+
yield {
|
|
2859
|
+
type: "tool_call_input_delta",
|
|
2860
|
+
id: existingToolCall.id,
|
|
2861
|
+
inputJsonText: argumentsDelta
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
function* startAzureOpenAiToolCallAccumulation(toolCallDelta, accumulator) {
|
|
2866
|
+
const toolName = toolCallDelta.function?.name;
|
|
2867
|
+
if (toolCallDelta.id === void 0 || toolName === void 0) {
|
|
2868
|
+
throw buildProviderInvalidResponseError(
|
|
2869
|
+
"Azure OpenAI",
|
|
2870
|
+
"stream tool call start"
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
const startedToolCall = {
|
|
2874
|
+
id: toolCallDelta.id,
|
|
2875
|
+
toolName,
|
|
2876
|
+
argumentsJsonText: toolCallDelta.function?.arguments ?? ""
|
|
2877
|
+
};
|
|
2878
|
+
accumulator.toolCallsByIndex.set(toolCallDelta.index, startedToolCall);
|
|
2879
|
+
yield {
|
|
2880
|
+
type: "tool_call_started",
|
|
2881
|
+
id: startedToolCall.id,
|
|
2882
|
+
toolName: startedToolCall.toolName
|
|
2883
|
+
};
|
|
2884
|
+
if (startedToolCall.argumentsJsonText.length > 0) {
|
|
2885
|
+
yield {
|
|
2886
|
+
type: "tool_call_input_delta",
|
|
2887
|
+
id: startedToolCall.id,
|
|
2888
|
+
inputJsonText: startedToolCall.argumentsJsonText
|
|
2889
|
+
};
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
function buildAzureOpenAiStreamResult(accumulator, latencyMs) {
|
|
2893
|
+
const result = {
|
|
2894
|
+
content: accumulator.contentText,
|
|
2895
|
+
tokenUsage: mapAzureOpenAiUsageToTokenUsage(
|
|
2896
|
+
accumulator.usage ?? {
|
|
2897
|
+
prompt_tokens: 0,
|
|
2898
|
+
completion_tokens: 0,
|
|
2899
|
+
total_tokens: 0
|
|
2900
|
+
}
|
|
2901
|
+
),
|
|
2902
|
+
latencyMs
|
|
2903
|
+
};
|
|
2904
|
+
const toolCalls = [...accumulator.toolCallsByIndex.values()].map(
|
|
2905
|
+
mapAccumulatedToolCallToModelToolCall2
|
|
2906
|
+
);
|
|
2907
|
+
if (toolCalls.length > 0) {
|
|
2908
|
+
result.toolCalls = toolCalls;
|
|
2909
|
+
}
|
|
2910
|
+
if (accumulator.finishReason !== void 0) {
|
|
2911
|
+
result.stopReason = normalizeAzureOpenAiFinishReason(
|
|
2912
|
+
accumulator.finishReason
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
return result;
|
|
2916
|
+
}
|
|
2917
|
+
function mapAccumulatedToolCallToModelToolCall2(toolCallAccumulation) {
|
|
2918
|
+
return {
|
|
2919
|
+
id: toolCallAccumulation.id,
|
|
2920
|
+
toolName: toolCallAccumulation.toolName,
|
|
2921
|
+
input: parseAzureOpenAiToolArgumentsJson(
|
|
2922
|
+
toolCallAccumulation.argumentsJsonText
|
|
2923
|
+
)
|
|
2924
|
+
};
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
// src/azure-openai-adapter.ts
|
|
2928
|
+
var defaultAzureOpenAiApiVersion = "2024-10-21";
|
|
2929
|
+
var azureDeploymentModelPlaceholder = "azure-deployment";
|
|
2930
|
+
function azureOpenai(options) {
|
|
2931
|
+
return {
|
|
2932
|
+
name: `azure-openai:${options.deployment}`,
|
|
2933
|
+
async generateChatCompletion(request) {
|
|
2934
|
+
return await executeAzureOpenAiAdapterCall(options, {
|
|
2935
|
+
messages: request.messages,
|
|
2936
|
+
temperature: request.temperature ?? options.temperature,
|
|
2937
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
2938
|
+
tools: request.tools,
|
|
2939
|
+
toolChoice: request.toolChoice,
|
|
2940
|
+
abortSignal: request.abortSignal,
|
|
2941
|
+
extraBody: request.extraBody
|
|
2942
|
+
});
|
|
2943
|
+
},
|
|
2944
|
+
async generateStructuredOutput(request) {
|
|
2945
|
+
const completion = await executeAzureOpenAiAdapterCall(options, {
|
|
2946
|
+
messages: request.messages,
|
|
2947
|
+
temperature: request.temperature ?? options.temperature,
|
|
2948
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
2949
|
+
structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
|
|
2950
|
+
request.schema
|
|
2951
|
+
),
|
|
2952
|
+
abortSignal: request.abortSignal
|
|
2953
|
+
});
|
|
2954
|
+
return {
|
|
2955
|
+
value: parseStructuredJsonText(completion.content, request.schema),
|
|
2956
|
+
tokenUsage: completion.tokenUsage,
|
|
2957
|
+
latencyMs: completion.latencyMs
|
|
2958
|
+
};
|
|
2959
|
+
},
|
|
2960
|
+
streamChatCompletion(request) {
|
|
2961
|
+
return streamAzureOpenAiChatCompletion(
|
|
2962
|
+
buildAzureOpenAiStreamInput(options, request)
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
function buildAzureOpenAiChatCompletionsUrl(options) {
|
|
2968
|
+
const endpoint = resolveAzureOpenAiEndpoint(options);
|
|
2969
|
+
const apiVersion = options.apiVersion ?? defaultAzureOpenAiApiVersion;
|
|
2970
|
+
return `${endpoint}/openai/deployments/${encodeURIComponent(options.deployment)}/chat/completions?api-version=${encodeURIComponent(apiVersion)}`;
|
|
2971
|
+
}
|
|
2972
|
+
function resolveAzureOpenAiEndpoint(options) {
|
|
2973
|
+
if (options.baseURL !== void 0 && options.baseURL.length > 0) {
|
|
2974
|
+
return options.baseURL.replace(/\/+$/, "");
|
|
2975
|
+
}
|
|
2976
|
+
if (options.resourceName !== void 0 && options.resourceName.length > 0) {
|
|
2977
|
+
return `https://${options.resourceName}.openai.azure.com`;
|
|
2978
|
+
}
|
|
2979
|
+
throw new import_errors33.AppError({
|
|
2980
|
+
code: import_errors32.AppErrorCode.BadRequest,
|
|
2981
|
+
message: "Azure OpenAI endpoint is missing. Pass resourceName or baseURL to azureOpenai().",
|
|
2982
|
+
statusCode: 400
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
2985
|
+
function buildAzureOpenAiRequestHeaders(options) {
|
|
2986
|
+
return {
|
|
2987
|
+
"content-type": "application/json",
|
|
2988
|
+
"api-key": resolveProviderApiKey({
|
|
2989
|
+
providerLabel: "Azure OpenAI",
|
|
2990
|
+
adapterFunctionName: "azureOpenai()",
|
|
2991
|
+
baseEnvVarName: "AZURE_OPENAI_API_KEY",
|
|
2992
|
+
apiKey: options.apiKey,
|
|
2993
|
+
apiKeyName: options.apiKeyName
|
|
2994
|
+
})
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
function buildAzureOpenAiChatRequestBody(input) {
|
|
2998
|
+
const requestBody = buildOpenAiRequestBody(
|
|
2999
|
+
azureDeploymentModelPlaceholder,
|
|
3000
|
+
input
|
|
3001
|
+
);
|
|
3002
|
+
const { model: ignoredModel, ...requestBodyWithoutModel } = requestBody;
|
|
3003
|
+
void ignoredModel;
|
|
3004
|
+
return requestBodyWithoutModel;
|
|
3005
|
+
}
|
|
3006
|
+
async function executeAzureOpenAiAdapterCall(options, input) {
|
|
3007
|
+
return await executeProviderCallWithRetry(
|
|
3008
|
+
options.retry,
|
|
3009
|
+
async () => await requestAzureOpenAiChatCompletion(options, input)
|
|
3010
|
+
);
|
|
3011
|
+
}
|
|
3012
|
+
async function requestAzureOpenAiChatCompletion(options, input) {
|
|
3013
|
+
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
3014
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
3015
|
+
buildAzureOpenAiChatRequestBody(input),
|
|
3016
|
+
options.extraBody,
|
|
3017
|
+
input.extraBody
|
|
3018
|
+
);
|
|
3019
|
+
const startedAt = Date.now();
|
|
3020
|
+
const response = await executeAbortableProviderFetch(
|
|
3021
|
+
"Azure OpenAI",
|
|
3022
|
+
async () => await fetchImplementation(buildAzureOpenAiChatCompletionsUrl(options), {
|
|
3023
|
+
method: "POST",
|
|
3024
|
+
headers: buildAzureOpenAiRequestHeaders(options),
|
|
3025
|
+
body: JSON.stringify(requestBody),
|
|
3026
|
+
signal: input.abortSignal
|
|
3027
|
+
})
|
|
3028
|
+
);
|
|
3029
|
+
const latencyMs = Date.now() - startedAt;
|
|
3030
|
+
if (!response.ok) {
|
|
3031
|
+
throw buildProviderStatusError("Azure OpenAI", response.status);
|
|
3032
|
+
}
|
|
3033
|
+
const parseResult = AzureOpenAiChatCompletionResponseSchema.safeParse(
|
|
3034
|
+
await response.json()
|
|
3035
|
+
);
|
|
3036
|
+
if (!parseResult.success) {
|
|
3037
|
+
throw buildProviderInvalidResponseError("Azure OpenAI", "chat completion");
|
|
3038
|
+
}
|
|
3039
|
+
return mapAzureOpenAiResponseToChatCompletionResult(
|
|
3040
|
+
parseResult.data,
|
|
3041
|
+
latencyMs
|
|
3042
|
+
);
|
|
3043
|
+
}
|
|
3044
|
+
function buildAzureOpenAiStreamInput(options, request) {
|
|
3045
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
3046
|
+
buildAzureOpenAiChatRequestBody({
|
|
3047
|
+
messages: request.messages,
|
|
3048
|
+
temperature: request.temperature ?? options.temperature,
|
|
3049
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
3050
|
+
tools: request.tools,
|
|
3051
|
+
toolChoice: request.toolChoice,
|
|
3052
|
+
abortSignal: request.abortSignal,
|
|
3053
|
+
extraBody: request.extraBody
|
|
3054
|
+
}),
|
|
3055
|
+
options.extraBody,
|
|
3056
|
+
request.extraBody
|
|
3057
|
+
);
|
|
3058
|
+
return {
|
|
3059
|
+
url: buildAzureOpenAiChatCompletionsUrl(options),
|
|
3060
|
+
headers: buildAzureOpenAiRequestHeaders(options),
|
|
3061
|
+
body: {
|
|
3062
|
+
...requestBody,
|
|
3063
|
+
stream: true,
|
|
3064
|
+
stream_options: { include_usage: true }
|
|
3065
|
+
},
|
|
3066
|
+
fetchImplementation: options.fetchImplementation ?? fetch,
|
|
3067
|
+
retry: options.retry,
|
|
3068
|
+
abortSignal: request.abortSignal
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
// src/bedrock-adapter.ts
|
|
3073
|
+
var import_zod11 = require("zod");
|
|
3074
|
+
var import_errors34 = require("@assemble-dev/shared-types/errors");
|
|
3075
|
+
var import_errors35 = require("@assemble-dev/shared-utils/errors");
|
|
3076
|
+
|
|
3077
|
+
// src/bedrock-sigv4.ts
|
|
3078
|
+
var awsSigV4Algorithm = "AWS4-HMAC-SHA256";
|
|
3079
|
+
async function signAwsRequestWithSigV4(input) {
|
|
3080
|
+
const amzDate = formatAmzDateTime(input.signingDate ?? /* @__PURE__ */ new Date());
|
|
3081
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
3082
|
+
const headersToSign = buildHeadersToSign(input, amzDate);
|
|
3083
|
+
const canonicalHeaderEntries = buildCanonicalHeaderEntries(headersToSign);
|
|
3084
|
+
const signedHeaderNames = canonicalHeaderEntries.map(([headerName]) => headerName).join(";");
|
|
3085
|
+
const credentialScope = `${dateStamp}/${input.region}/${input.service}/aws4_request`;
|
|
3086
|
+
const signatureHex = await computeAwsSigV4Signature(
|
|
3087
|
+
input,
|
|
3088
|
+
amzDate,
|
|
3089
|
+
credentialScope,
|
|
3090
|
+
canonicalHeaderEntries,
|
|
3091
|
+
signedHeaderNames
|
|
3092
|
+
);
|
|
3093
|
+
return {
|
|
3094
|
+
...headersToSign,
|
|
3095
|
+
authorization: `${awsSigV4Algorithm} Credential=${input.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaderNames}, Signature=${signatureHex}`
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
async function computeAwsSigV4Signature(input, amzDate, credentialScope, canonicalHeaderEntries, signedHeaderNames) {
|
|
3099
|
+
const canonicalRequest = await buildCanonicalRequest(
|
|
3100
|
+
input,
|
|
3101
|
+
canonicalHeaderEntries,
|
|
3102
|
+
signedHeaderNames
|
|
3103
|
+
);
|
|
3104
|
+
const stringToSign = [
|
|
3105
|
+
awsSigV4Algorithm,
|
|
3106
|
+
amzDate,
|
|
3107
|
+
credentialScope,
|
|
3108
|
+
await hashSha256Hex(canonicalRequest)
|
|
3109
|
+
].join("\n");
|
|
3110
|
+
const signingKey = await deriveAwsSigningKey(
|
|
3111
|
+
input.credentials.secretAccessKey,
|
|
3112
|
+
amzDate.slice(0, 8),
|
|
3113
|
+
input.region,
|
|
3114
|
+
input.service
|
|
3115
|
+
);
|
|
3116
|
+
return bytesToHex(await computeHmacSha256(signingKey, stringToSign));
|
|
3117
|
+
}
|
|
3118
|
+
function buildHeadersToSign(input, amzDate) {
|
|
3119
|
+
const headersToSign = {};
|
|
3120
|
+
for (const [headerName, headerValue] of Object.entries(input.headers)) {
|
|
3121
|
+
headersToSign[headerName.toLowerCase()] = headerValue;
|
|
3122
|
+
}
|
|
3123
|
+
headersToSign.host = input.url.host;
|
|
3124
|
+
headersToSign["x-amz-date"] = amzDate;
|
|
3125
|
+
if (input.credentials.sessionToken !== void 0) {
|
|
3126
|
+
headersToSign["x-amz-security-token"] = input.credentials.sessionToken;
|
|
3127
|
+
}
|
|
3128
|
+
return headersToSign;
|
|
3129
|
+
}
|
|
3130
|
+
function buildCanonicalHeaderEntries(headersToSign) {
|
|
3131
|
+
return Object.entries(headersToSign).map(([headerName, headerValue]) => [
|
|
3132
|
+
headerName.toLowerCase(),
|
|
3133
|
+
headerValue.trim().replace(/\s+/g, " ")
|
|
3134
|
+
]).sort(([leftName], [rightName]) => leftName.localeCompare(rightName));
|
|
3135
|
+
}
|
|
3136
|
+
async function buildCanonicalRequest(input, canonicalHeaderEntries, signedHeaderNames) {
|
|
3137
|
+
const canonicalHeadersText = canonicalHeaderEntries.map(([headerName, headerValue]) => `${headerName}:${headerValue}`).join("\n");
|
|
3138
|
+
return [
|
|
3139
|
+
input.method.toUpperCase(),
|
|
3140
|
+
buildCanonicalUri(input.url.pathname),
|
|
3141
|
+
buildCanonicalQueryString(input.url.searchParams),
|
|
3142
|
+
`${canonicalHeadersText}
|
|
3143
|
+
`,
|
|
3144
|
+
signedHeaderNames,
|
|
3145
|
+
await hashSha256Hex(input.bodyText)
|
|
3146
|
+
].join("\n");
|
|
3147
|
+
}
|
|
3148
|
+
function buildCanonicalUri(pathname) {
|
|
3149
|
+
if (pathname.length === 0) {
|
|
3150
|
+
return "/";
|
|
3151
|
+
}
|
|
3152
|
+
return pathname.split("/").map(encodeRfc3986UriComponent).join("/");
|
|
3153
|
+
}
|
|
3154
|
+
function buildCanonicalQueryString(searchParams) {
|
|
3155
|
+
const encodedEntries = [];
|
|
3156
|
+
searchParams.forEach((parameterValue, parameterName) => {
|
|
3157
|
+
encodedEntries.push([
|
|
3158
|
+
encodeRfc3986UriComponent(parameterName),
|
|
3159
|
+
encodeRfc3986UriComponent(parameterValue)
|
|
3160
|
+
]);
|
|
3161
|
+
});
|
|
3162
|
+
return encodedEntries.sort(
|
|
3163
|
+
([leftName, leftValue], [rightName, rightValue]) => leftName.localeCompare(rightName) || leftValue.localeCompare(rightValue)
|
|
3164
|
+
).map(([parameterName, parameterValue]) => `${parameterName}=${parameterValue}`).join("&");
|
|
3165
|
+
}
|
|
3166
|
+
function encodeRfc3986UriComponent(value) {
|
|
3167
|
+
return encodeURIComponent(value).replace(
|
|
3168
|
+
/[!'()*]/g,
|
|
3169
|
+
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
|
|
3170
|
+
);
|
|
3171
|
+
}
|
|
3172
|
+
function formatAmzDateTime(date) {
|
|
3173
|
+
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
3174
|
+
}
|
|
3175
|
+
async function deriveAwsSigningKey(secretAccessKey, dateStamp, region, service) {
|
|
3176
|
+
const dateKey = await computeHmacSha256(
|
|
3177
|
+
new TextEncoder().encode(`AWS4${secretAccessKey}`),
|
|
3178
|
+
dateStamp
|
|
3179
|
+
);
|
|
3180
|
+
const regionKey = await computeHmacSha256(dateKey, region);
|
|
3181
|
+
const serviceKey = await computeHmacSha256(regionKey, service);
|
|
3182
|
+
return await computeHmacSha256(serviceKey, "aws4_request");
|
|
3183
|
+
}
|
|
3184
|
+
async function hashSha256Hex(text) {
|
|
3185
|
+
const digest = await crypto.subtle.digest(
|
|
3186
|
+
"SHA-256",
|
|
3187
|
+
new TextEncoder().encode(text)
|
|
3188
|
+
);
|
|
3189
|
+
return bytesToHex(new Uint8Array(digest));
|
|
3190
|
+
}
|
|
3191
|
+
async function computeHmacSha256(keyBytes, text) {
|
|
3192
|
+
const hmacKey = await crypto.subtle.importKey(
|
|
3193
|
+
"raw",
|
|
3194
|
+
keyBytes,
|
|
3195
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
3196
|
+
false,
|
|
3197
|
+
["sign"]
|
|
3198
|
+
);
|
|
3199
|
+
const signature = await crypto.subtle.sign(
|
|
3200
|
+
"HMAC",
|
|
3201
|
+
hmacKey,
|
|
3202
|
+
new TextEncoder().encode(text)
|
|
3203
|
+
);
|
|
3204
|
+
return new Uint8Array(signature);
|
|
3205
|
+
}
|
|
3206
|
+
function bytesToHex(bytes) {
|
|
3207
|
+
return Array.from(bytes).map((byteValue) => byteValue.toString(16).padStart(2, "0")).join("");
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3210
|
+
// src/bedrock-adapter.ts
|
|
3211
|
+
var bedrockAnthropicVersion = "bedrock-2023-05-31";
|
|
3212
|
+
var defaultBedrockMaxOutputTokens = 1024;
|
|
3213
|
+
var bedrockSigningServiceName = "bedrock";
|
|
3214
|
+
var bedrockModelPlaceholder = "bedrock-invoke-model";
|
|
3215
|
+
var BedrockEnvCredentialsSchema = import_zod11.z.object({
|
|
3216
|
+
AWS_ACCESS_KEY_ID: import_zod11.z.string().optional(),
|
|
3217
|
+
AWS_SECRET_ACCESS_KEY: import_zod11.z.string().optional(),
|
|
3218
|
+
AWS_SESSION_TOKEN: import_zod11.z.string().optional()
|
|
3219
|
+
});
|
|
3220
|
+
function bedrock(options) {
|
|
3221
|
+
return {
|
|
3222
|
+
name: `bedrock:${options.modelId}`,
|
|
3223
|
+
async generateChatCompletion(request) {
|
|
3224
|
+
return await executeBedrockAdapterCall(options, {
|
|
3225
|
+
messages: request.messages,
|
|
3226
|
+
temperature: request.temperature ?? options.temperature,
|
|
3227
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
3228
|
+
tools: request.tools,
|
|
3229
|
+
toolChoice: request.toolChoice,
|
|
3230
|
+
abortSignal: request.abortSignal,
|
|
3231
|
+
extraBody: request.extraBody
|
|
3232
|
+
});
|
|
3233
|
+
},
|
|
3234
|
+
async generateStructuredOutput(request) {
|
|
3235
|
+
const completion = await executeBedrockAdapterCall(options, {
|
|
3236
|
+
messages: [
|
|
3237
|
+
buildBedrockStructuredOutputSystemMessage(request.schema),
|
|
3238
|
+
...request.messages
|
|
3239
|
+
],
|
|
3240
|
+
temperature: request.temperature ?? options.temperature,
|
|
3241
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
3242
|
+
abortSignal: request.abortSignal
|
|
3243
|
+
});
|
|
3244
|
+
return {
|
|
3245
|
+
value: parseStructuredJsonText(completion.content, request.schema),
|
|
3246
|
+
tokenUsage: completion.tokenUsage,
|
|
3247
|
+
latencyMs: completion.latencyMs
|
|
3248
|
+
};
|
|
3249
|
+
}
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
function buildBedrockInvokeModelUrl(region, modelId) {
|
|
3253
|
+
return `https://bedrock-runtime.${region}.amazonaws.com/model/${encodeURIComponent(modelId)}/invoke`;
|
|
3254
|
+
}
|
|
3255
|
+
function resolveBedrockCredentials(options) {
|
|
3256
|
+
if (options.credentials !== void 0) {
|
|
3257
|
+
return options.credentials;
|
|
3258
|
+
}
|
|
3259
|
+
const parsedEnv = BedrockEnvCredentialsSchema.parse(process.env);
|
|
3260
|
+
const accessKeyId = parsedEnv.AWS_ACCESS_KEY_ID;
|
|
3261
|
+
const secretAccessKey = parsedEnv.AWS_SECRET_ACCESS_KEY;
|
|
3262
|
+
if (accessKeyId === void 0 || accessKeyId.length === 0 || secretAccessKey === void 0 || secretAccessKey.length === 0) {
|
|
3263
|
+
throw new import_errors35.AppError({
|
|
3264
|
+
code: import_errors34.AppErrorCode.Unauthorized,
|
|
3265
|
+
message: "AWS credentials are missing. Pass credentials to bedrock() or set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.",
|
|
3266
|
+
statusCode: 401
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
const credentials = { accessKeyId, secretAccessKey };
|
|
3270
|
+
const sessionToken = parsedEnv.AWS_SESSION_TOKEN;
|
|
3271
|
+
if (sessionToken !== void 0 && sessionToken.length > 0) {
|
|
3272
|
+
credentials.sessionToken = sessionToken;
|
|
3273
|
+
}
|
|
3274
|
+
return credentials;
|
|
3275
|
+
}
|
|
3276
|
+
async function executeBedrockAdapterCall(options, input) {
|
|
3277
|
+
return await executeProviderCallWithRetry(
|
|
3278
|
+
options.retry,
|
|
3279
|
+
async () => await requestBedrockInvokeModel(options, input)
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
3282
|
+
async function requestBedrockInvokeModel(options, input) {
|
|
3283
|
+
const credentials = resolveBedrockCredentials(options);
|
|
3284
|
+
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
3285
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
3286
|
+
buildBedrockInvokeModelRequestBody(input),
|
|
3287
|
+
options.extraBody,
|
|
3288
|
+
input.extraBody
|
|
3289
|
+
);
|
|
3290
|
+
const bodyText = JSON.stringify(requestBody);
|
|
3291
|
+
const url = new URL(buildBedrockInvokeModelUrl(options.region, options.modelId));
|
|
3292
|
+
const signedHeaders = await signAwsRequestWithSigV4({
|
|
3293
|
+
method: "POST",
|
|
3294
|
+
url,
|
|
3295
|
+
headers: { "content-type": "application/json" },
|
|
3296
|
+
bodyText,
|
|
3297
|
+
region: options.region,
|
|
3298
|
+
service: bedrockSigningServiceName,
|
|
3299
|
+
credentials
|
|
3300
|
+
});
|
|
3301
|
+
const startedAt = Date.now();
|
|
3302
|
+
const response = await executeAbortableProviderFetch(
|
|
3303
|
+
"Bedrock",
|
|
3304
|
+
async () => await fetchImplementation(url.toString(), {
|
|
3305
|
+
method: "POST",
|
|
3306
|
+
headers: signedHeaders,
|
|
3307
|
+
body: bodyText,
|
|
3308
|
+
signal: input.abortSignal
|
|
3309
|
+
})
|
|
3310
|
+
);
|
|
3311
|
+
const latencyMs = Date.now() - startedAt;
|
|
3312
|
+
if (!response.ok) {
|
|
3313
|
+
throw buildProviderStatusError("Bedrock", response.status);
|
|
3314
|
+
}
|
|
3315
|
+
const parseResult = AnthropicMessagesResponseSchema.safeParse(
|
|
3316
|
+
await response.json()
|
|
3317
|
+
);
|
|
3318
|
+
if (!parseResult.success) {
|
|
3319
|
+
throw buildProviderInvalidResponseError("Bedrock", "invoke model");
|
|
3320
|
+
}
|
|
3321
|
+
return mapAnthropicResponseToChatCompletionResult(parseResult.data, latencyMs);
|
|
3322
|
+
}
|
|
3323
|
+
function buildBedrockInvokeModelRequestBody(input) {
|
|
3324
|
+
const anthropicRequestBody = buildAnthropicRequestBody(
|
|
3325
|
+
{
|
|
3326
|
+
model: bedrockModelPlaceholder,
|
|
3327
|
+
defaultMaxOutputTokens: defaultBedrockMaxOutputTokens
|
|
3328
|
+
},
|
|
3329
|
+
input
|
|
3330
|
+
);
|
|
3331
|
+
const {
|
|
3332
|
+
model: ignoredModel,
|
|
3333
|
+
stream: ignoredStream,
|
|
3334
|
+
...requestBodyWithoutModel
|
|
3335
|
+
} = anthropicRequestBody;
|
|
3336
|
+
void ignoredModel;
|
|
3337
|
+
void ignoredStream;
|
|
3338
|
+
return {
|
|
3339
|
+
...requestBodyWithoutModel,
|
|
3340
|
+
anthropic_version: bedrockAnthropicVersion
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
function buildBedrockStructuredOutputSystemMessage(schema) {
|
|
3344
|
+
return {
|
|
3345
|
+
role: "system",
|
|
3346
|
+
content: `Respond with a single JSON object that satisfies the following JSON Schema. Output only valid JSON with no surrounding text.
|
|
3347
|
+
|
|
3348
|
+
JSON Schema:
|
|
3349
|
+
${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
|
|
3350
|
+
};
|
|
1938
3351
|
}
|
|
1939
3352
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1940
3353
|
0 && (module.exports = {
|
|
1941
3354
|
AnthropicMessagesResponseSchema,
|
|
1942
3355
|
AnthropicStreamEventSchema,
|
|
3356
|
+
AzureOpenAiChatCompletionResponseSchema,
|
|
3357
|
+
ChatMessageCacheControlSchema,
|
|
1943
3358
|
ChatMessageRoleSchema,
|
|
1944
3359
|
ChatMessageSchema,
|
|
1945
3360
|
ChatStopReasonSchema,
|
|
1946
3361
|
MessageContentBlockSchema,
|
|
1947
3362
|
ModelToolCallSchema,
|
|
1948
3363
|
anthropic,
|
|
3364
|
+
anthropicBatchJsonOnlySystemInstruction,
|
|
3365
|
+
anthropicBatchTraceWorkflowName,
|
|
3366
|
+
applyCacheControlToLastContentBlock,
|
|
3367
|
+
awsSigV4Algorithm,
|
|
3368
|
+
azureOpenai,
|
|
3369
|
+
bedrock,
|
|
3370
|
+
bedrockAnthropicVersion,
|
|
3371
|
+
buildAnthropicCacheControl,
|
|
1949
3372
|
buildAnthropicRequestBody,
|
|
3373
|
+
buildAnthropicStructuredOutputJsonSchema,
|
|
3374
|
+
buildAzureOpenAiChatCompletionsUrl,
|
|
3375
|
+
buildAzureOpenAiChatRequestBody,
|
|
3376
|
+
buildBedrockInvokeModelRequestBody,
|
|
3377
|
+
buildBedrockInvokeModelUrl,
|
|
3378
|
+
buildGeminiResponseSchema,
|
|
1950
3379
|
buildOpenAiRequestBody,
|
|
3380
|
+
buildOpenAiStructuredOutputJsonSchema,
|
|
1951
3381
|
buildProviderCanceledError,
|
|
1952
3382
|
calculateProviderRetryDelayMs,
|
|
3383
|
+
convertZodSchemaToJsonSchema,
|
|
1953
3384
|
createAnthropicBatchClient,
|
|
1954
3385
|
defaultAnthropicBaseUrl,
|
|
1955
3386
|
defaultAnthropicBatchPollIntervalMs,
|
|
1956
3387
|
defaultAnthropicBatchTimeoutMs,
|
|
1957
3388
|
defaultAnthropicMaxOutputTokens,
|
|
3389
|
+
defaultAzureOpenAiApiVersion,
|
|
3390
|
+
defaultBedrockMaxOutputTokens,
|
|
1958
3391
|
defaultGeminiBaseUrl,
|
|
1959
3392
|
defaultOpenAiBaseUrl,
|
|
1960
3393
|
defaultProviderRetryInitialDelayMs,
|
|
@@ -1965,6 +3398,7 @@ function buildBatchToolResultText(message) {
|
|
|
1965
3398
|
executeProviderCallWithModelFallback,
|
|
1966
3399
|
executeProviderCallWithRetry,
|
|
1967
3400
|
extractAnthropicTextContent,
|
|
3401
|
+
extractAnthropicThinkingText,
|
|
1968
3402
|
extractAnthropicToolCalls,
|
|
1969
3403
|
extractMessageText,
|
|
1970
3404
|
gemini,
|
|
@@ -1973,17 +3407,32 @@ function buildBatchToolResultText(message) {
|
|
|
1973
3407
|
joinSystemMessageText,
|
|
1974
3408
|
mapAnthropicResponseToChatCompletionResult,
|
|
1975
3409
|
mapAnthropicUsageToTokenUsage,
|
|
3410
|
+
mapAzureOpenAiResponseToChatCompletionResult,
|
|
3411
|
+
mapAzureOpenAiUsageToTokenUsage,
|
|
1976
3412
|
mapChatMessageToAnthropicRequestMessage,
|
|
1977
3413
|
mapChatMessageToOpenAiRequestMessage,
|
|
1978
3414
|
mapModelToolChoiceToAnthropicToolChoice,
|
|
1979
3415
|
mapModelToolChoiceToOpenAiToolChoice,
|
|
1980
3416
|
mapModelToolDefinitionToOpenAiTool,
|
|
1981
3417
|
mapModelToolDefinitionsToAnthropicTools,
|
|
3418
|
+
mergeProviderRequestExtraBody,
|
|
1982
3419
|
mockModel,
|
|
1983
3420
|
normalizeAnthropicStopReason,
|
|
3421
|
+
normalizeAzureOpenAiFinishReason,
|
|
3422
|
+
openAiStructuredOutputSchemaName,
|
|
1984
3423
|
openai,
|
|
3424
|
+
parseAnthropicBatchStructuredContent,
|
|
1985
3425
|
parseAnthropicStreamEvents,
|
|
1986
3426
|
parseAnthropicToolInputJson,
|
|
3427
|
+
parseAzureOpenAiStreamEvents,
|
|
3428
|
+
parseAzureOpenAiToolArgumentsJson,
|
|
3429
|
+
resolveAnthropicPromptCachingSettings,
|
|
3430
|
+
resolveBedrockCredentials,
|
|
1987
3431
|
resolveProviderApiKey,
|
|
1988
|
-
|
|
3432
|
+
sanitizeJsonSchemaForAnthropicStructuredOutput,
|
|
3433
|
+
sanitizeJsonSchemaForGeminiResponseSchema,
|
|
3434
|
+
sanitizeJsonSchemaForOpenAiStructuredOutput,
|
|
3435
|
+
signAwsRequestWithSigV4,
|
|
3436
|
+
streamAnthropicChatCompletion,
|
|
3437
|
+
streamAzureOpenAiChatCompletion
|
|
1989
3438
|
});
|