@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.js
CHANGED
|
@@ -28,12 +28,16 @@ var ModelToolCallSchema = z.object({
|
|
|
28
28
|
toolName: z.string(),
|
|
29
29
|
input: JsonValueSchema
|
|
30
30
|
});
|
|
31
|
+
var ChatMessageCacheControlSchema = z.object({
|
|
32
|
+
ttl: z.enum(["5m", "1h"]).optional()
|
|
33
|
+
});
|
|
31
34
|
var ChatMessageSchema = z.object({
|
|
32
35
|
role: ChatMessageRoleSchema,
|
|
33
36
|
content: z.union([z.string(), z.array(MessageContentBlockSchema)]),
|
|
34
37
|
toolName: z.string().optional(),
|
|
35
38
|
toolCallId: z.string().optional(),
|
|
36
|
-
toolCalls: z.array(ModelToolCallSchema).optional()
|
|
39
|
+
toolCalls: z.array(ModelToolCallSchema).optional(),
|
|
40
|
+
cacheControl: ChatMessageCacheControlSchema.optional()
|
|
37
41
|
});
|
|
38
42
|
var ChatStopReasonSchema = z.enum([
|
|
39
43
|
"end_turn",
|
|
@@ -49,14 +53,222 @@ function extractMessageText(message) {
|
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
// src/openai-adapter.ts
|
|
52
|
-
import { z as
|
|
53
|
-
import { AppErrorCode as
|
|
56
|
+
import { z as z4 } from "zod";
|
|
57
|
+
import { AppErrorCode as AppErrorCode8 } from "@assemble-dev/shared-types/errors";
|
|
54
58
|
import { JsonValueSchema as JsonValueSchema3 } from "@assemble-dev/shared-types/json";
|
|
55
|
-
import { AppError as
|
|
59
|
+
import { AppError as AppError9 } from "@assemble-dev/shared-utils/errors";
|
|
56
60
|
|
|
57
61
|
// src/openai-request-mapping.ts
|
|
62
|
+
import { AppErrorCode as AppErrorCode2 } from "@assemble-dev/shared-types/errors";
|
|
63
|
+
import { AppError as AppError2 } from "@assemble-dev/shared-utils/errors";
|
|
64
|
+
|
|
65
|
+
// src/structured-output-json-schema.ts
|
|
66
|
+
import { z as z2 } from "zod";
|
|
58
67
|
import { AppErrorCode } from "@assemble-dev/shared-types/errors";
|
|
68
|
+
import { JsonObjectSchema } from "@assemble-dev/shared-types/json";
|
|
59
69
|
import { AppError } from "@assemble-dev/shared-utils/errors";
|
|
70
|
+
|
|
71
|
+
// src/json-schema-tree.ts
|
|
72
|
+
var schemaMapKeywords = ["properties", "$defs"];
|
|
73
|
+
var schemaListKeywords = ["anyOf", "oneOf", "allOf", "prefixItems"];
|
|
74
|
+
var schemaValueKeywords = [
|
|
75
|
+
"items",
|
|
76
|
+
"additionalProperties",
|
|
77
|
+
"not",
|
|
78
|
+
"if",
|
|
79
|
+
"then",
|
|
80
|
+
"else",
|
|
81
|
+
"contains",
|
|
82
|
+
"propertyNames"
|
|
83
|
+
];
|
|
84
|
+
function isJsonObject(value) {
|
|
85
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
86
|
+
}
|
|
87
|
+
function transformJsonSchemaNode(node, transformNode) {
|
|
88
|
+
const result = { ...transformNode({ ...node }) };
|
|
89
|
+
for (const keyword of schemaMapKeywords) {
|
|
90
|
+
const mapValue = result[keyword];
|
|
91
|
+
if (mapValue !== void 0 && isJsonObject(mapValue)) {
|
|
92
|
+
result[keyword] = transformJsonSchemaMap(mapValue, transformNode);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const keyword of schemaListKeywords) {
|
|
96
|
+
const listValue = result[keyword];
|
|
97
|
+
if (Array.isArray(listValue)) {
|
|
98
|
+
result[keyword] = listValue.map(
|
|
99
|
+
(entry) => isJsonObject(entry) ? transformJsonSchemaNode(entry, transformNode) : entry
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
for (const keyword of schemaValueKeywords) {
|
|
104
|
+
const schemaValue = result[keyword];
|
|
105
|
+
if (schemaValue !== void 0 && isJsonObject(schemaValue)) {
|
|
106
|
+
result[keyword] = transformJsonSchemaNode(schemaValue, transformNode);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
function transformJsonSchemaMap(schemaMap, transformNode) {
|
|
112
|
+
const result = {};
|
|
113
|
+
for (const [schemaName, schemaValue] of Object.entries(schemaMap)) {
|
|
114
|
+
result[schemaName] = isJsonObject(schemaValue) ? transformJsonSchemaNode(schemaValue, transformNode) : schemaValue;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
function inlineJsonSchemaReferences(root) {
|
|
119
|
+
const rootDefinitions = root["$defs"];
|
|
120
|
+
const definitions = rootDefinitions !== void 0 && isJsonObject(rootDefinitions) ? rootDefinitions : {};
|
|
121
|
+
const resolvedRoot = resolveJsonSchemaReferences(
|
|
122
|
+
root,
|
|
123
|
+
definitions,
|
|
124
|
+
/* @__PURE__ */ new Set()
|
|
125
|
+
);
|
|
126
|
+
return isJsonObject(resolvedRoot) ? resolvedRoot : {};
|
|
127
|
+
}
|
|
128
|
+
function resolveJsonSchemaReferences(value, definitions, activeReferences) {
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
return value.map(
|
|
131
|
+
(entry) => resolveJsonSchemaReferences(entry, definitions, activeReferences)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
if (!isJsonObject(value)) {
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
const referenceTarget = value["$ref"];
|
|
138
|
+
if (typeof referenceTarget === "string") {
|
|
139
|
+
return resolveJsonSchemaReferenceTarget(
|
|
140
|
+
referenceTarget,
|
|
141
|
+
definitions,
|
|
142
|
+
activeReferences
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const result = {};
|
|
146
|
+
for (const [key, entryValue] of Object.entries(value)) {
|
|
147
|
+
if (key !== "$defs") {
|
|
148
|
+
result[key] = resolveJsonSchemaReferences(
|
|
149
|
+
entryValue,
|
|
150
|
+
definitions,
|
|
151
|
+
activeReferences
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
function resolveJsonSchemaReferenceTarget(referenceTarget, definitions, activeReferences) {
|
|
158
|
+
if (activeReferences.has(referenceTarget)) {
|
|
159
|
+
return {};
|
|
160
|
+
}
|
|
161
|
+
const definitionsPrefix = "#/$defs/";
|
|
162
|
+
if (!referenceTarget.startsWith(definitionsPrefix)) {
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
const definition = definitions[referenceTarget.slice(definitionsPrefix.length)];
|
|
166
|
+
if (definition === void 0 || !isJsonObject(definition)) {
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
return resolveJsonSchemaReferences(
|
|
170
|
+
definition,
|
|
171
|
+
definitions,
|
|
172
|
+
/* @__PURE__ */ new Set([...activeReferences, referenceTarget])
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
function removeJsonSchemaKeywords(node, keywords) {
|
|
176
|
+
const result = {};
|
|
177
|
+
for (const [key, value] of Object.entries(node)) {
|
|
178
|
+
if (!keywords.includes(key)) {
|
|
179
|
+
result[key] = value;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/structured-output-json-schema.ts
|
|
186
|
+
var openAiStructuredOutputSchemaName = "structured_output";
|
|
187
|
+
var schemaMetadataKeywords = ["$schema", "$id"];
|
|
188
|
+
var anthropicUnsupportedSchemaKeywords = [
|
|
189
|
+
...schemaMetadataKeywords,
|
|
190
|
+
"minimum",
|
|
191
|
+
"maximum",
|
|
192
|
+
"exclusiveMinimum",
|
|
193
|
+
"exclusiveMaximum",
|
|
194
|
+
"minLength",
|
|
195
|
+
"maxLength",
|
|
196
|
+
"multipleOf"
|
|
197
|
+
];
|
|
198
|
+
var openAiUnsupportedSchemaKeywords = [
|
|
199
|
+
...anthropicUnsupportedSchemaKeywords,
|
|
200
|
+
"pattern",
|
|
201
|
+
"format",
|
|
202
|
+
"minItems",
|
|
203
|
+
"maxItems",
|
|
204
|
+
"uniqueItems",
|
|
205
|
+
"default"
|
|
206
|
+
];
|
|
207
|
+
function convertZodSchemaToJsonSchema(schema) {
|
|
208
|
+
try {
|
|
209
|
+
return JsonObjectSchema.parse(z2.toJSONSchema(schema, { io: "input" }));
|
|
210
|
+
} catch (error) {
|
|
211
|
+
throw new AppError({
|
|
212
|
+
code: AppErrorCode.BadRequest,
|
|
213
|
+
message: "Structured output schema could not be converted to JSON Schema",
|
|
214
|
+
statusCode: 400,
|
|
215
|
+
cause: error instanceof Error ? error : void 0
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function buildAnthropicStructuredOutputJsonSchema(schema) {
|
|
220
|
+
return sanitizeJsonSchemaForAnthropicStructuredOutput(
|
|
221
|
+
convertZodSchemaToJsonSchema(schema)
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
function sanitizeJsonSchemaForAnthropicStructuredOutput(jsonSchema) {
|
|
225
|
+
return transformJsonSchemaNode(
|
|
226
|
+
inlineJsonSchemaReferences(jsonSchema),
|
|
227
|
+
sanitizeAnthropicSchemaNode
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
function buildOpenAiStructuredOutputJsonSchema(schema) {
|
|
231
|
+
return sanitizeJsonSchemaForOpenAiStructuredOutput(
|
|
232
|
+
convertZodSchemaToJsonSchema(schema)
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
function sanitizeJsonSchemaForOpenAiStructuredOutput(jsonSchema) {
|
|
236
|
+
return transformJsonSchemaNode(jsonSchema, sanitizeOpenAiSchemaNode);
|
|
237
|
+
}
|
|
238
|
+
function sanitizeAnthropicSchemaNode(node) {
|
|
239
|
+
const sanitized = removeJsonSchemaKeywords(
|
|
240
|
+
node,
|
|
241
|
+
anthropicUnsupportedSchemaKeywords
|
|
242
|
+
);
|
|
243
|
+
if (sanitized["type"] === "object") {
|
|
244
|
+
sanitized["additionalProperties"] = false;
|
|
245
|
+
}
|
|
246
|
+
return sanitized;
|
|
247
|
+
}
|
|
248
|
+
function sanitizeOpenAiSchemaNode(node) {
|
|
249
|
+
const sanitized = removeJsonSchemaKeywords(
|
|
250
|
+
node,
|
|
251
|
+
openAiUnsupportedSchemaKeywords
|
|
252
|
+
);
|
|
253
|
+
if (sanitized["type"] !== "object") {
|
|
254
|
+
return sanitized;
|
|
255
|
+
}
|
|
256
|
+
sanitized["additionalProperties"] = false;
|
|
257
|
+
const propertyNames = listSchemaPropertyNames(sanitized);
|
|
258
|
+
if (propertyNames.length > 0) {
|
|
259
|
+
sanitized["required"] = propertyNames;
|
|
260
|
+
}
|
|
261
|
+
return sanitized;
|
|
262
|
+
}
|
|
263
|
+
function listSchemaPropertyNames(node) {
|
|
264
|
+
const properties = node["properties"];
|
|
265
|
+
if (properties === void 0 || !isJsonObject(properties)) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
return Object.keys(properties);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/openai-request-mapping.ts
|
|
60
272
|
function buildOpenAiRequestBody(model, input) {
|
|
61
273
|
const requestBody = {
|
|
62
274
|
model,
|
|
@@ -68,7 +280,16 @@ function buildOpenAiRequestBody(model, input) {
|
|
|
68
280
|
if (input.maxOutputTokens !== void 0) {
|
|
69
281
|
requestBody.max_tokens = input.maxOutputTokens;
|
|
70
282
|
}
|
|
71
|
-
if (input.
|
|
283
|
+
if (input.structuredOutputJsonSchema !== void 0) {
|
|
284
|
+
requestBody.response_format = {
|
|
285
|
+
type: "json_schema",
|
|
286
|
+
json_schema: {
|
|
287
|
+
name: openAiStructuredOutputSchemaName,
|
|
288
|
+
strict: true,
|
|
289
|
+
schema: input.structuredOutputJsonSchema
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
} else if (input.requireJsonObjectResponse === true) {
|
|
72
293
|
requestBody.response_format = { type: "json_object" };
|
|
73
294
|
}
|
|
74
295
|
if (input.tools !== void 0 && input.tools.length > 0) {
|
|
@@ -154,17 +375,17 @@ function mapMessageContentBlockToOpenAiContentPart(contentBlock) {
|
|
|
154
375
|
}
|
|
155
376
|
};
|
|
156
377
|
}
|
|
157
|
-
throw new
|
|
158
|
-
code:
|
|
378
|
+
throw new AppError2({
|
|
379
|
+
code: AppErrorCode2.ValidationFailed,
|
|
159
380
|
message: "OpenAI adapter does not support document content blocks in chat messages",
|
|
160
381
|
statusCode: 400
|
|
161
382
|
});
|
|
162
383
|
}
|
|
163
384
|
|
|
164
385
|
// src/parse-structured-json.ts
|
|
165
|
-
import { AppErrorCode as
|
|
386
|
+
import { AppErrorCode as AppErrorCode3 } from "@assemble-dev/shared-types/errors";
|
|
166
387
|
import { JsonValueSchema as JsonValueSchema2 } from "@assemble-dev/shared-types/json";
|
|
167
|
-
import { AppError as
|
|
388
|
+
import { AppError as AppError3 } from "@assemble-dev/shared-utils/errors";
|
|
168
389
|
var markdownCodeFencePattern = /^```[A-Za-z]*\s*\n?([\s\S]*?)\n?```$/;
|
|
169
390
|
function parseStructuredJsonText(text, schema) {
|
|
170
391
|
return schema.parse(parseJsonText(stripMarkdownCodeFences(text)));
|
|
@@ -181,8 +402,8 @@ function parseJsonText(text) {
|
|
|
181
402
|
try {
|
|
182
403
|
return JsonValueSchema2.parse(JSON.parse(text));
|
|
183
404
|
} catch (error) {
|
|
184
|
-
throw new
|
|
185
|
-
code:
|
|
405
|
+
throw new AppError3({
|
|
406
|
+
code: AppErrorCode3.ValidationFailed,
|
|
186
407
|
message: "Structured output was not valid JSON",
|
|
187
408
|
statusCode: 422,
|
|
188
409
|
cause: error instanceof Error ? error : void 0
|
|
@@ -191,9 +412,9 @@ function parseJsonText(text) {
|
|
|
191
412
|
}
|
|
192
413
|
|
|
193
414
|
// src/provider-api-key.ts
|
|
194
|
-
import { z as
|
|
195
|
-
import { AppErrorCode as
|
|
196
|
-
import { AppError as
|
|
415
|
+
import { z as z3 } from "zod";
|
|
416
|
+
import { AppErrorCode as AppErrorCode4 } from "@assemble-dev/shared-types/errors";
|
|
417
|
+
import { AppError as AppError4 } from "@assemble-dev/shared-utils/errors";
|
|
197
418
|
import {
|
|
198
419
|
buildNamedApiKeyEnvVarName,
|
|
199
420
|
expectedProviderApiKeyNameFormat,
|
|
@@ -210,8 +431,8 @@ function resolveProviderApiKey(input) {
|
|
|
210
431
|
}
|
|
211
432
|
function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
212
433
|
if (!validateProviderApiKeyName(apiKeyName)) {
|
|
213
|
-
throw new
|
|
214
|
-
code:
|
|
434
|
+
throw new AppError4({
|
|
435
|
+
code: AppErrorCode4.BadRequest,
|
|
215
436
|
message: `Invalid ${input.providerLabel} apiKeyName "${apiKeyName}". Expected ${expectedProviderApiKeyNameFormat}.`,
|
|
216
437
|
statusCode: 400
|
|
217
438
|
});
|
|
@@ -219,8 +440,8 @@ function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
|
219
440
|
const envVarName = buildNamedApiKeyEnvVarName(input.baseEnvVarName, apiKeyName);
|
|
220
441
|
const envApiKey = readEnvironmentVariable(envVarName);
|
|
221
442
|
if (envApiKey === void 0 || envApiKey.length === 0) {
|
|
222
|
-
throw new
|
|
223
|
-
code:
|
|
443
|
+
throw new AppError4({
|
|
444
|
+
code: AppErrorCode4.Unauthorized,
|
|
224
445
|
message: `${input.providerLabel} API key named "${apiKeyName}" is missing. Set the ${envVarName} environment variable or pass apiKey to ${input.adapterFunctionName}.`,
|
|
225
446
|
statusCode: 401
|
|
226
447
|
});
|
|
@@ -230,8 +451,8 @@ function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
|
230
451
|
function resolveBaseProviderApiKeyFromEnv(input) {
|
|
231
452
|
const envApiKey = readEnvironmentVariable(input.baseEnvVarName);
|
|
232
453
|
if (envApiKey === void 0 || envApiKey.length === 0) {
|
|
233
|
-
throw new
|
|
234
|
-
code:
|
|
454
|
+
throw new AppError4({
|
|
455
|
+
code: AppErrorCode4.Unauthorized,
|
|
235
456
|
message: `${input.providerLabel} API key is missing. Pass apiKey to ${input.adapterFunctionName} or set the ${input.baseEnvVarName} environment variable.`,
|
|
236
457
|
statusCode: 401
|
|
237
458
|
});
|
|
@@ -239,19 +460,19 @@ function resolveBaseProviderApiKeyFromEnv(input) {
|
|
|
239
460
|
return envApiKey;
|
|
240
461
|
}
|
|
241
462
|
function readEnvironmentVariable(envVarName) {
|
|
242
|
-
const parsedEnv =
|
|
463
|
+
const parsedEnv = z3.object({ [envVarName]: z3.string().optional() }).parse(process.env);
|
|
243
464
|
return parsedEnv[envVarName];
|
|
244
465
|
}
|
|
245
466
|
|
|
246
467
|
// src/provider-cancellation.ts
|
|
247
|
-
import { AppErrorCode as
|
|
248
|
-
import { AppError as
|
|
468
|
+
import { AppErrorCode as AppErrorCode5 } from "@assemble-dev/shared-types/errors";
|
|
469
|
+
import { AppError as AppError5 } from "@assemble-dev/shared-utils/errors";
|
|
249
470
|
function isAbortError(error) {
|
|
250
471
|
return error.name === "AbortError";
|
|
251
472
|
}
|
|
252
473
|
function buildProviderCanceledError(providerLabel) {
|
|
253
|
-
return new
|
|
254
|
-
code:
|
|
474
|
+
return new AppError5({
|
|
475
|
+
code: AppErrorCode5.Canceled,
|
|
255
476
|
message: `${providerLabel} request was canceled`,
|
|
256
477
|
statusCode: 499
|
|
257
478
|
});
|
|
@@ -267,19 +488,28 @@ async function executeAbortableProviderFetch(providerLabel, performFetch) {
|
|
|
267
488
|
}
|
|
268
489
|
}
|
|
269
490
|
|
|
491
|
+
// src/provider-extra-body.ts
|
|
492
|
+
function mergeProviderRequestExtraBody(requestBody, adapterExtraBody, requestExtraBody) {
|
|
493
|
+
return {
|
|
494
|
+
...requestBody,
|
|
495
|
+
...adapterExtraBody ?? {},
|
|
496
|
+
...requestExtraBody ?? {}
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
270
500
|
// src/provider-fallback.ts
|
|
271
|
-
import { AppError as
|
|
501
|
+
import { AppError as AppError7 } from "@assemble-dev/shared-utils/errors";
|
|
272
502
|
|
|
273
503
|
// src/provider-retry.ts
|
|
274
|
-
import { AppErrorCode as
|
|
275
|
-
import { AppError as
|
|
504
|
+
import { AppErrorCode as AppErrorCode6 } from "@assemble-dev/shared-types/errors";
|
|
505
|
+
import { AppError as AppError6 } from "@assemble-dev/shared-utils/errors";
|
|
276
506
|
var defaultProviderRetryMaxAttempts = 3;
|
|
277
507
|
var defaultProviderRetryInitialDelayMs = 500;
|
|
278
508
|
var defaultProviderRetryMaxDelayMs = 8e3;
|
|
279
509
|
var retryableAppErrorCodes = /* @__PURE__ */ new Set([
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
510
|
+
AppErrorCode6.RateLimited,
|
|
511
|
+
AppErrorCode6.ServiceUnavailable,
|
|
512
|
+
AppErrorCode6.InternalServerError
|
|
283
513
|
]);
|
|
284
514
|
function isRetryableProviderAppError(appError) {
|
|
285
515
|
return retryableAppErrorCodes.has(appError.code);
|
|
@@ -294,7 +524,7 @@ async function executeProviderCallWithRetry(retryOptions, executeCall, waitFor)
|
|
|
294
524
|
try {
|
|
295
525
|
return await executeCall();
|
|
296
526
|
} catch (error) {
|
|
297
|
-
if (error instanceof
|
|
527
|
+
if (error instanceof AppError6 && !isRetryableProviderAppError(error)) {
|
|
298
528
|
throw error;
|
|
299
529
|
}
|
|
300
530
|
if (error instanceof Error && isAbortError(error)) {
|
|
@@ -328,7 +558,7 @@ async function executeProviderCallWithModelFallback(fallbackModel, executePrimar
|
|
|
328
558
|
if (fallbackModel === void 0) {
|
|
329
559
|
throw error;
|
|
330
560
|
}
|
|
331
|
-
if (error instanceof
|
|
561
|
+
if (error instanceof AppError7 && !isRetryableProviderAppError(error)) {
|
|
332
562
|
throw error;
|
|
333
563
|
}
|
|
334
564
|
if (error instanceof Error && isAbortError(error)) {
|
|
@@ -339,11 +569,11 @@ async function executeProviderCallWithModelFallback(fallbackModel, executePrimar
|
|
|
339
569
|
}
|
|
340
570
|
|
|
341
571
|
// src/provider-status-error.ts
|
|
342
|
-
import { AppErrorCode as
|
|
343
|
-
import { AppError as
|
|
572
|
+
import { AppErrorCode as AppErrorCode7 } from "@assemble-dev/shared-types/errors";
|
|
573
|
+
import { AppError as AppError8 } from "@assemble-dev/shared-utils/errors";
|
|
344
574
|
function buildProviderStatusError(providerLabel, providerStatus) {
|
|
345
575
|
const code = mapProviderStatusToAppErrorCode(providerStatus);
|
|
346
|
-
return new
|
|
576
|
+
return new AppError8({
|
|
347
577
|
code,
|
|
348
578
|
message: `${providerLabel} request failed with status ${String(providerStatus)}`,
|
|
349
579
|
statusCode: mapAppErrorCodeToStatusCode(code),
|
|
@@ -351,44 +581,44 @@ function buildProviderStatusError(providerLabel, providerStatus) {
|
|
|
351
581
|
});
|
|
352
582
|
}
|
|
353
583
|
function buildProviderInvalidResponseError(providerLabel, expectedShapeDescription) {
|
|
354
|
-
return new
|
|
355
|
-
code:
|
|
584
|
+
return new AppError8({
|
|
585
|
+
code: AppErrorCode7.ValidationFailed,
|
|
356
586
|
message: `${providerLabel} response did not match the expected ${expectedShapeDescription} shape`,
|
|
357
587
|
statusCode: 502
|
|
358
588
|
});
|
|
359
589
|
}
|
|
360
590
|
function mapProviderStatusToAppErrorCode(providerStatus) {
|
|
361
591
|
if (providerStatus === 400) {
|
|
362
|
-
return
|
|
592
|
+
return AppErrorCode7.BadRequest;
|
|
363
593
|
}
|
|
364
594
|
if (providerStatus === 401) {
|
|
365
|
-
return
|
|
595
|
+
return AppErrorCode7.Unauthorized;
|
|
366
596
|
}
|
|
367
597
|
if (providerStatus === 403) {
|
|
368
|
-
return
|
|
598
|
+
return AppErrorCode7.Forbidden;
|
|
369
599
|
}
|
|
370
600
|
if (providerStatus === 404) {
|
|
371
|
-
return
|
|
601
|
+
return AppErrorCode7.NotFound;
|
|
372
602
|
}
|
|
373
603
|
if (providerStatus === 429) {
|
|
374
|
-
return
|
|
604
|
+
return AppErrorCode7.RateLimited;
|
|
375
605
|
}
|
|
376
|
-
return
|
|
606
|
+
return AppErrorCode7.InternalServerError;
|
|
377
607
|
}
|
|
378
608
|
function mapAppErrorCodeToStatusCode(code) {
|
|
379
|
-
if (code ===
|
|
609
|
+
if (code === AppErrorCode7.BadRequest) {
|
|
380
610
|
return 400;
|
|
381
611
|
}
|
|
382
|
-
if (code ===
|
|
612
|
+
if (code === AppErrorCode7.Unauthorized) {
|
|
383
613
|
return 401;
|
|
384
614
|
}
|
|
385
|
-
if (code ===
|
|
615
|
+
if (code === AppErrorCode7.Forbidden) {
|
|
386
616
|
return 403;
|
|
387
617
|
}
|
|
388
|
-
if (code ===
|
|
618
|
+
if (code === AppErrorCode7.NotFound) {
|
|
389
619
|
return 404;
|
|
390
620
|
}
|
|
391
|
-
if (code ===
|
|
621
|
+
if (code === AppErrorCode7.RateLimited) {
|
|
392
622
|
return 429;
|
|
393
623
|
}
|
|
394
624
|
return 500;
|
|
@@ -396,33 +626,29 @@ function mapAppErrorCodeToStatusCode(code) {
|
|
|
396
626
|
|
|
397
627
|
// src/openai-adapter.ts
|
|
398
628
|
var defaultOpenAiBaseUrl = "https://api.openai.com/v1";
|
|
399
|
-
var OpenAiResponseToolCallSchema =
|
|
400
|
-
id:
|
|
401
|
-
function:
|
|
402
|
-
name:
|
|
403
|
-
arguments:
|
|
629
|
+
var OpenAiResponseToolCallSchema = z4.object({
|
|
630
|
+
id: z4.string(),
|
|
631
|
+
function: z4.object({
|
|
632
|
+
name: z4.string(),
|
|
633
|
+
arguments: z4.string()
|
|
404
634
|
})
|
|
405
635
|
});
|
|
406
|
-
var OpenAiChatCompletionResponseSchema =
|
|
407
|
-
choices:
|
|
408
|
-
|
|
409
|
-
message:
|
|
410
|
-
content:
|
|
411
|
-
tool_calls:
|
|
636
|
+
var OpenAiChatCompletionResponseSchema = z4.object({
|
|
637
|
+
choices: z4.array(
|
|
638
|
+
z4.object({
|
|
639
|
+
message: z4.object({
|
|
640
|
+
content: z4.string().nullable(),
|
|
641
|
+
tool_calls: z4.array(OpenAiResponseToolCallSchema).optional()
|
|
412
642
|
}),
|
|
413
|
-
finish_reason:
|
|
643
|
+
finish_reason: z4.string().nullable().optional()
|
|
414
644
|
})
|
|
415
645
|
).min(1),
|
|
416
|
-
usage:
|
|
417
|
-
prompt_tokens:
|
|
418
|
-
completion_tokens:
|
|
419
|
-
total_tokens:
|
|
646
|
+
usage: z4.object({
|
|
647
|
+
prompt_tokens: z4.number().int().nonnegative(),
|
|
648
|
+
completion_tokens: z4.number().int().nonnegative(),
|
|
649
|
+
total_tokens: z4.number().int().nonnegative()
|
|
420
650
|
})
|
|
421
651
|
});
|
|
422
|
-
var structuredOutputSystemMessage = {
|
|
423
|
-
role: "system",
|
|
424
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
425
|
-
};
|
|
426
652
|
function openai(options) {
|
|
427
653
|
return {
|
|
428
654
|
name: `openai:${options.model}`,
|
|
@@ -431,18 +657,20 @@ function openai(options) {
|
|
|
431
657
|
messages: request.messages,
|
|
432
658
|
temperature: request.temperature ?? options.temperature,
|
|
433
659
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
434
|
-
requireJsonObjectResponse: false,
|
|
435
660
|
tools: request.tools,
|
|
436
661
|
toolChoice: request.toolChoice,
|
|
437
|
-
abortSignal: request.abortSignal
|
|
662
|
+
abortSignal: request.abortSignal,
|
|
663
|
+
extraBody: request.extraBody
|
|
438
664
|
});
|
|
439
665
|
},
|
|
440
666
|
async generateStructuredOutput(request) {
|
|
441
667
|
const completion = await executeOpenAiAdapterCall(options, {
|
|
442
|
-
messages:
|
|
668
|
+
messages: request.messages,
|
|
443
669
|
temperature: request.temperature ?? options.temperature,
|
|
444
670
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
445
|
-
|
|
671
|
+
structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
|
|
672
|
+
request.schema
|
|
673
|
+
),
|
|
446
674
|
abortSignal: request.abortSignal
|
|
447
675
|
});
|
|
448
676
|
return {
|
|
@@ -476,7 +704,11 @@ async function requestOpenAiChatCompletion(options, input) {
|
|
|
476
704
|
});
|
|
477
705
|
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
478
706
|
const baseUrl = options.baseURL ?? defaultOpenAiBaseUrl;
|
|
479
|
-
const requestBody =
|
|
707
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
708
|
+
buildOpenAiRequestBody(options.model, input),
|
|
709
|
+
options.extraBody,
|
|
710
|
+
input.extraBody
|
|
711
|
+
);
|
|
480
712
|
const startedAt = Date.now();
|
|
481
713
|
const response = await executeAbortableProviderFetch(
|
|
482
714
|
"OpenAI",
|
|
@@ -533,8 +765,8 @@ function parseOpenAiToolArgumentsJson(argumentsJsonText) {
|
|
|
533
765
|
try {
|
|
534
766
|
return JsonValueSchema3.parse(JSON.parse(argumentsJsonText));
|
|
535
767
|
} catch (error) {
|
|
536
|
-
throw new
|
|
537
|
-
code:
|
|
768
|
+
throw new AppError9({
|
|
769
|
+
code: AppErrorCode8.ValidationFailed,
|
|
538
770
|
message: "OpenAI tool call arguments were not valid JSON",
|
|
539
771
|
statusCode: 502,
|
|
540
772
|
cause: error instanceof Error ? error : void 0
|
|
@@ -556,8 +788,8 @@ function extractOpenAiMessageContent(response, allowEmptyContent) {
|
|
|
556
788
|
if (allowEmptyContent) {
|
|
557
789
|
return "";
|
|
558
790
|
}
|
|
559
|
-
throw new
|
|
560
|
-
code:
|
|
791
|
+
throw new AppError9({
|
|
792
|
+
code: AppErrorCode8.ValidationFailed,
|
|
561
793
|
message: "OpenAI response contained no message content",
|
|
562
794
|
statusCode: 502
|
|
563
795
|
});
|
|
@@ -572,6 +804,36 @@ function mapOpenAiUsageToTokenUsage(response) {
|
|
|
572
804
|
};
|
|
573
805
|
}
|
|
574
806
|
|
|
807
|
+
// src/anthropic-cache-control.ts
|
|
808
|
+
function resolveAnthropicPromptCachingSettings(promptCaching) {
|
|
809
|
+
if (promptCaching === void 0) {
|
|
810
|
+
return { mode: "off" };
|
|
811
|
+
}
|
|
812
|
+
if (typeof promptCaching === "string") {
|
|
813
|
+
return { mode: promptCaching };
|
|
814
|
+
}
|
|
815
|
+
return promptCaching;
|
|
816
|
+
}
|
|
817
|
+
function buildAnthropicCacheControl(ttl) {
|
|
818
|
+
if (ttl === void 0) {
|
|
819
|
+
return { type: "ephemeral" };
|
|
820
|
+
}
|
|
821
|
+
return { type: "ephemeral", ttl };
|
|
822
|
+
}
|
|
823
|
+
function applyCacheControlToLastContentBlock(requestMessage, cacheControl) {
|
|
824
|
+
if (typeof requestMessage.content === "string") {
|
|
825
|
+
if (requestMessage.content.length === 0) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
requestMessage.content = [{ type: "text", text: requestMessage.content }];
|
|
829
|
+
}
|
|
830
|
+
const lastContentBlock = requestMessage.content[requestMessage.content.length - 1];
|
|
831
|
+
if (lastContentBlock === void 0) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
lastContentBlock.cache_control = cacheControl;
|
|
835
|
+
}
|
|
836
|
+
|
|
575
837
|
// src/anthropic-request-mapping.ts
|
|
576
838
|
function buildAnthropicRequestBody(settings, input) {
|
|
577
839
|
const requestBody = {
|
|
@@ -581,6 +843,7 @@ function buildAnthropicRequestBody(settings, input) {
|
|
|
581
843
|
};
|
|
582
844
|
applyAnthropicSystemField(requestBody, settings, input);
|
|
583
845
|
applyAnthropicToolFields(requestBody, settings, input);
|
|
846
|
+
applyAnthropicMessageCacheBreakpoints(requestBody, settings, input);
|
|
584
847
|
if (input.temperature !== void 0) {
|
|
585
848
|
requestBody.temperature = input.temperature;
|
|
586
849
|
}
|
|
@@ -590,21 +853,54 @@ function buildAnthropicRequestBody(settings, input) {
|
|
|
590
853
|
budget_tokens: settings.thinking.budgetTokens
|
|
591
854
|
};
|
|
592
855
|
}
|
|
856
|
+
if (input.structuredOutputJsonSchema !== void 0) {
|
|
857
|
+
requestBody.output_config = {
|
|
858
|
+
format: { type: "json_schema", schema: input.structuredOutputJsonSchema }
|
|
859
|
+
};
|
|
860
|
+
}
|
|
593
861
|
return requestBody;
|
|
594
862
|
}
|
|
595
863
|
function applyAnthropicSystemField(requestBody, settings, input) {
|
|
864
|
+
const caching = resolveAnthropicPromptCachingSettings(settings.promptCaching);
|
|
596
865
|
const systemText = joinSystemMessageText(input.messages);
|
|
597
866
|
if (systemText.length === 0) {
|
|
598
867
|
return;
|
|
599
868
|
}
|
|
600
|
-
if (
|
|
869
|
+
if (caching.mode === "off") {
|
|
601
870
|
requestBody.system = systemText;
|
|
602
871
|
return;
|
|
603
872
|
}
|
|
604
873
|
requestBody.system = [
|
|
605
|
-
{
|
|
874
|
+
{
|
|
875
|
+
type: "text",
|
|
876
|
+
text: systemText,
|
|
877
|
+
cache_control: buildAnthropicCacheControl(caching.ttl)
|
|
878
|
+
}
|
|
606
879
|
];
|
|
607
880
|
}
|
|
881
|
+
function applyAnthropicMessageCacheBreakpoints(requestBody, settings, input) {
|
|
882
|
+
const caching = resolveAnthropicPromptCachingSettings(settings.promptCaching);
|
|
883
|
+
const finalRequestMessage = requestBody.messages[requestBody.messages.length - 1];
|
|
884
|
+
if (caching.mode === "messages" && finalRequestMessage !== void 0) {
|
|
885
|
+
applyCacheControlToLastContentBlock(
|
|
886
|
+
finalRequestMessage,
|
|
887
|
+
buildAnthropicCacheControl(caching.ttl)
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
const nonSystemMessages = input.messages.filter(
|
|
891
|
+
(message) => message.role !== "system"
|
|
892
|
+
);
|
|
893
|
+
nonSystemMessages.forEach((message, messageIndex) => {
|
|
894
|
+
const requestMessage = requestBody.messages[messageIndex];
|
|
895
|
+
if (message.cacheControl === void 0 || requestMessage === void 0) {
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
applyCacheControlToLastContentBlock(
|
|
899
|
+
requestMessage,
|
|
900
|
+
buildAnthropicCacheControl(message.cacheControl.ttl ?? caching.ttl)
|
|
901
|
+
);
|
|
902
|
+
});
|
|
903
|
+
}
|
|
608
904
|
function applyAnthropicToolFields(requestBody, settings, input) {
|
|
609
905
|
if (input.tools !== void 0 && input.tools.length > 0) {
|
|
610
906
|
requestBody.tools = mapModelToolDefinitionsToAnthropicTools(
|
|
@@ -619,14 +915,15 @@ function applyAnthropicToolFields(requestBody, settings, input) {
|
|
|
619
915
|
}
|
|
620
916
|
}
|
|
621
917
|
function mapModelToolDefinitionsToAnthropicTools(tools, promptCaching) {
|
|
918
|
+
const caching = resolveAnthropicPromptCachingSettings(promptCaching);
|
|
622
919
|
return tools.map((tool, toolIndex) => {
|
|
623
920
|
const anthropicTool = {
|
|
624
921
|
name: tool.name,
|
|
625
922
|
description: tool.description,
|
|
626
923
|
input_schema: tool.inputJsonSchema
|
|
627
924
|
};
|
|
628
|
-
if (
|
|
629
|
-
anthropicTool.cache_control =
|
|
925
|
+
if (caching.mode !== "off" && toolIndex === tools.length - 1) {
|
|
926
|
+
anthropicTool.cache_control = buildAnthropicCacheControl(caching.ttl);
|
|
630
927
|
}
|
|
631
928
|
return anthropicTool;
|
|
632
929
|
});
|
|
@@ -717,44 +1014,45 @@ function mapMessageContentBlockToAnthropic(contentBlock) {
|
|
|
717
1014
|
}
|
|
718
1015
|
|
|
719
1016
|
// src/anthropic-response-parsing.ts
|
|
720
|
-
import { z as
|
|
721
|
-
import { AppErrorCode as
|
|
1017
|
+
import { z as z5 } from "zod";
|
|
1018
|
+
import { AppErrorCode as AppErrorCode9 } from "@assemble-dev/shared-types/errors";
|
|
722
1019
|
import { JsonValueSchema as JsonValueSchema4 } from "@assemble-dev/shared-types/json";
|
|
723
|
-
import { AppError as
|
|
724
|
-
var AnthropicUsageSchema =
|
|
725
|
-
input_tokens:
|
|
726
|
-
output_tokens:
|
|
727
|
-
cache_creation_input_tokens:
|
|
728
|
-
cache_read_input_tokens:
|
|
1020
|
+
import { AppError as AppError10 } from "@assemble-dev/shared-utils/errors";
|
|
1021
|
+
var AnthropicUsageSchema = z5.object({
|
|
1022
|
+
input_tokens: z5.number().int().nonnegative(),
|
|
1023
|
+
output_tokens: z5.number().int().nonnegative(),
|
|
1024
|
+
cache_creation_input_tokens: z5.number().int().nonnegative().optional(),
|
|
1025
|
+
cache_read_input_tokens: z5.number().int().nonnegative().optional()
|
|
729
1026
|
});
|
|
730
|
-
var AnthropicContentBlockSchema =
|
|
731
|
-
|
|
732
|
-
type:
|
|
733
|
-
text:
|
|
1027
|
+
var AnthropicContentBlockSchema = z5.discriminatedUnion("type", [
|
|
1028
|
+
z5.object({
|
|
1029
|
+
type: z5.literal("text"),
|
|
1030
|
+
text: z5.string()
|
|
734
1031
|
}),
|
|
735
|
-
|
|
736
|
-
type:
|
|
737
|
-
id:
|
|
738
|
-
name:
|
|
1032
|
+
z5.object({
|
|
1033
|
+
type: z5.literal("tool_use"),
|
|
1034
|
+
id: z5.string(),
|
|
1035
|
+
name: z5.string(),
|
|
739
1036
|
input: JsonValueSchema4
|
|
740
1037
|
}),
|
|
741
|
-
|
|
742
|
-
type:
|
|
743
|
-
thinking:
|
|
744
|
-
signature:
|
|
1038
|
+
z5.object({
|
|
1039
|
+
type: z5.literal("thinking"),
|
|
1040
|
+
thinking: z5.string().optional(),
|
|
1041
|
+
signature: z5.string().optional()
|
|
745
1042
|
}),
|
|
746
|
-
|
|
747
|
-
type:
|
|
748
|
-
data:
|
|
1043
|
+
z5.object({
|
|
1044
|
+
type: z5.literal("redacted_thinking"),
|
|
1045
|
+
data: z5.string().optional()
|
|
749
1046
|
})
|
|
750
1047
|
]);
|
|
751
|
-
var AnthropicMessagesResponseSchema =
|
|
752
|
-
content:
|
|
753
|
-
stop_reason:
|
|
1048
|
+
var AnthropicMessagesResponseSchema = z5.object({
|
|
1049
|
+
content: z5.array(AnthropicContentBlockSchema),
|
|
1050
|
+
stop_reason: z5.string().nullable().optional(),
|
|
754
1051
|
usage: AnthropicUsageSchema
|
|
755
1052
|
});
|
|
756
1053
|
function mapAnthropicResponseToChatCompletionResult(response, latencyMs) {
|
|
757
1054
|
const toolCalls = extractAnthropicToolCalls(response);
|
|
1055
|
+
const thinkingText = extractAnthropicThinkingText(response);
|
|
758
1056
|
const result = {
|
|
759
1057
|
content: extractAnthropicTextContent(response, toolCalls.length > 0),
|
|
760
1058
|
tokenUsage: mapAnthropicUsageToTokenUsage(response.usage),
|
|
@@ -763,6 +1061,9 @@ function mapAnthropicResponseToChatCompletionResult(response, latencyMs) {
|
|
|
763
1061
|
if (toolCalls.length > 0) {
|
|
764
1062
|
result.toolCalls = toolCalls;
|
|
765
1063
|
}
|
|
1064
|
+
if (thinkingText.length > 0) {
|
|
1065
|
+
result.thinkingText = thinkingText;
|
|
1066
|
+
}
|
|
766
1067
|
if (response.stop_reason !== null && response.stop_reason !== void 0) {
|
|
767
1068
|
result.stopReason = normalizeAnthropicStopReason(response.stop_reason);
|
|
768
1069
|
}
|
|
@@ -775,13 +1076,16 @@ function extractAnthropicToolCalls(response) {
|
|
|
775
1076
|
input: contentBlock.input
|
|
776
1077
|
}));
|
|
777
1078
|
}
|
|
1079
|
+
function extractAnthropicThinkingText(response) {
|
|
1080
|
+
return response.content.filter((contentBlock) => contentBlock.type === "thinking").map((contentBlock) => contentBlock.thinking ?? "").join("");
|
|
1081
|
+
}
|
|
778
1082
|
function extractAnthropicTextContent(response, allowEmptyText) {
|
|
779
1083
|
const textBlocks = response.content.filter(
|
|
780
1084
|
(contentBlock) => contentBlock.type === "text"
|
|
781
1085
|
);
|
|
782
1086
|
if (textBlocks.length === 0 && !allowEmptyText) {
|
|
783
|
-
throw new
|
|
784
|
-
code:
|
|
1087
|
+
throw new AppError10({
|
|
1088
|
+
code: AppErrorCode9.ValidationFailed,
|
|
785
1089
|
message: "Anthropic response contained no text content",
|
|
786
1090
|
statusCode: 502
|
|
787
1091
|
});
|
|
@@ -818,58 +1122,68 @@ function mapAnthropicUsageToTokenUsage(usage) {
|
|
|
818
1122
|
}
|
|
819
1123
|
|
|
820
1124
|
// src/anthropic-stream.ts
|
|
821
|
-
import { AppErrorCode as
|
|
1125
|
+
import { AppErrorCode as AppErrorCode10 } from "@assemble-dev/shared-types/errors";
|
|
822
1126
|
import { JsonValueSchema as JsonValueSchema5 } from "@assemble-dev/shared-types/json";
|
|
823
|
-
import { AppError as
|
|
1127
|
+
import { AppError as AppError11 } from "@assemble-dev/shared-utils/errors";
|
|
824
1128
|
|
|
825
1129
|
// src/anthropic-stream-events.ts
|
|
826
|
-
import { z as
|
|
827
|
-
var AnthropicStreamEventSchema =
|
|
828
|
-
|
|
829
|
-
type:
|
|
830
|
-
message:
|
|
831
|
-
usage:
|
|
832
|
-
input_tokens:
|
|
833
|
-
output_tokens:
|
|
834
|
-
cache_creation_input_tokens:
|
|
835
|
-
cache_read_input_tokens:
|
|
1130
|
+
import { z as z6 } from "zod";
|
|
1131
|
+
var AnthropicStreamEventSchema = z6.discriminatedUnion("type", [
|
|
1132
|
+
z6.object({
|
|
1133
|
+
type: z6.literal("message_start"),
|
|
1134
|
+
message: z6.object({
|
|
1135
|
+
usage: z6.object({
|
|
1136
|
+
input_tokens: z6.number().int().nonnegative(),
|
|
1137
|
+
output_tokens: z6.number().int().nonnegative().optional(),
|
|
1138
|
+
cache_creation_input_tokens: z6.number().int().nonnegative().optional(),
|
|
1139
|
+
cache_read_input_tokens: z6.number().int().nonnegative().optional()
|
|
836
1140
|
})
|
|
837
1141
|
})
|
|
838
1142
|
}),
|
|
839
|
-
|
|
840
|
-
type:
|
|
841
|
-
index:
|
|
842
|
-
content_block:
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
type:
|
|
846
|
-
|
|
847
|
-
|
|
1143
|
+
z6.object({
|
|
1144
|
+
type: z6.literal("content_block_start"),
|
|
1145
|
+
index: z6.number().int().nonnegative(),
|
|
1146
|
+
content_block: z6.discriminatedUnion("type", [
|
|
1147
|
+
z6.object({ type: z6.literal("text"), text: z6.string().optional() }),
|
|
1148
|
+
z6.object({
|
|
1149
|
+
type: z6.literal("thinking"),
|
|
1150
|
+
thinking: z6.string().optional()
|
|
1151
|
+
}),
|
|
1152
|
+
z6.object({
|
|
1153
|
+
type: z6.literal("redacted_thinking"),
|
|
1154
|
+
data: z6.string().optional()
|
|
1155
|
+
}),
|
|
1156
|
+
z6.object({
|
|
1157
|
+
type: z6.literal("tool_use"),
|
|
1158
|
+
id: z6.string(),
|
|
1159
|
+
name: z6.string()
|
|
848
1160
|
})
|
|
849
1161
|
])
|
|
850
1162
|
}),
|
|
851
|
-
|
|
852
|
-
type:
|
|
853
|
-
index:
|
|
854
|
-
delta:
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1163
|
+
z6.object({
|
|
1164
|
+
type: z6.literal("content_block_delta"),
|
|
1165
|
+
index: z6.number().int().nonnegative(),
|
|
1166
|
+
delta: z6.discriminatedUnion("type", [
|
|
1167
|
+
z6.object({ type: z6.literal("text_delta"), text: z6.string() }),
|
|
1168
|
+
z6.object({ type: z6.literal("thinking_delta"), thinking: z6.string() }),
|
|
1169
|
+
z6.object({ type: z6.literal("signature_delta"), signature: z6.string() }),
|
|
1170
|
+
z6.object({
|
|
1171
|
+
type: z6.literal("input_json_delta"),
|
|
1172
|
+
partial_json: z6.string()
|
|
859
1173
|
})
|
|
860
1174
|
])
|
|
861
1175
|
}),
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
type:
|
|
865
|
-
delta:
|
|
866
|
-
usage:
|
|
1176
|
+
z6.object({ type: z6.literal("content_block_stop") }),
|
|
1177
|
+
z6.object({
|
|
1178
|
+
type: z6.literal("message_delta"),
|
|
1179
|
+
delta: z6.object({ stop_reason: z6.string().nullable().optional() }),
|
|
1180
|
+
usage: z6.object({ output_tokens: z6.number().int().nonnegative() }).optional()
|
|
867
1181
|
}),
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
type:
|
|
872
|
-
error:
|
|
1182
|
+
z6.object({ type: z6.literal("message_stop") }),
|
|
1183
|
+
z6.object({ type: z6.literal("ping") }),
|
|
1184
|
+
z6.object({
|
|
1185
|
+
type: z6.literal("error"),
|
|
1186
|
+
error: z6.object({ message: z6.string() })
|
|
873
1187
|
})
|
|
874
1188
|
]);
|
|
875
1189
|
|
|
@@ -955,6 +1269,7 @@ async function* readServerSentEventDataLines(body, abortSignal) {
|
|
|
955
1269
|
function createAnthropicStreamAccumulator() {
|
|
956
1270
|
return {
|
|
957
1271
|
contentText: "",
|
|
1272
|
+
thinkingText: "",
|
|
958
1273
|
toolCallsByBlockIndex: /* @__PURE__ */ new Map(),
|
|
959
1274
|
inputTokens: 0,
|
|
960
1275
|
outputTokens: 0
|
|
@@ -962,8 +1277,8 @@ function createAnthropicStreamAccumulator() {
|
|
|
962
1277
|
}
|
|
963
1278
|
function* applyAnthropicStreamEvent(event, accumulator) {
|
|
964
1279
|
if (event.type === "error") {
|
|
965
|
-
throw new
|
|
966
|
-
code:
|
|
1280
|
+
throw new AppError11({
|
|
1281
|
+
code: AppErrorCode10.InternalServerError,
|
|
967
1282
|
message: `Anthropic stream reported an error: ${event.error.message}`,
|
|
968
1283
|
statusCode: 502
|
|
969
1284
|
});
|
|
@@ -999,6 +1314,17 @@ function* applyAnthropicContentBlockStart(event, accumulator) {
|
|
|
999
1314
|
}
|
|
1000
1315
|
return;
|
|
1001
1316
|
}
|
|
1317
|
+
if (event.content_block.type === "thinking") {
|
|
1318
|
+
const initialThinkingText = event.content_block.thinking ?? "";
|
|
1319
|
+
if (initialThinkingText.length > 0) {
|
|
1320
|
+
accumulator.thinkingText += initialThinkingText;
|
|
1321
|
+
yield { type: "thinking_delta", text: initialThinkingText };
|
|
1322
|
+
}
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
if (event.content_block.type === "redacted_thinking") {
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1002
1328
|
accumulator.toolCallsByBlockIndex.set(event.index, {
|
|
1003
1329
|
id: event.content_block.id,
|
|
1004
1330
|
toolName: event.content_block.name,
|
|
@@ -1016,6 +1342,14 @@ function* applyAnthropicContentBlockDelta(event, accumulator) {
|
|
|
1016
1342
|
yield { type: "text_delta", text: event.delta.text };
|
|
1017
1343
|
return;
|
|
1018
1344
|
}
|
|
1345
|
+
if (event.delta.type === "thinking_delta") {
|
|
1346
|
+
accumulator.thinkingText += event.delta.thinking;
|
|
1347
|
+
yield { type: "thinking_delta", text: event.delta.thinking };
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
if (event.delta.type === "signature_delta") {
|
|
1351
|
+
return;
|
|
1352
|
+
}
|
|
1019
1353
|
const toolCallAccumulation = accumulator.toolCallsByBlockIndex.get(
|
|
1020
1354
|
event.index
|
|
1021
1355
|
);
|
|
@@ -1062,6 +1396,9 @@ function buildAnthropicStreamResult(accumulator, latencyMs) {
|
|
|
1062
1396
|
if (accumulator.stopReason !== void 0) {
|
|
1063
1397
|
result.stopReason = accumulator.stopReason;
|
|
1064
1398
|
}
|
|
1399
|
+
if (accumulator.thinkingText.length > 0) {
|
|
1400
|
+
result.thinkingText = accumulator.thinkingText;
|
|
1401
|
+
}
|
|
1065
1402
|
return result;
|
|
1066
1403
|
}
|
|
1067
1404
|
function mapAccumulatedToolCallToModelToolCall(toolCallAccumulation) {
|
|
@@ -1078,8 +1415,8 @@ function parseAnthropicToolInputJson(inputJsonText) {
|
|
|
1078
1415
|
try {
|
|
1079
1416
|
return JsonValueSchema5.parse(JSON.parse(inputJsonText));
|
|
1080
1417
|
} catch (error) {
|
|
1081
|
-
throw new
|
|
1082
|
-
code:
|
|
1418
|
+
throw new AppError11({
|
|
1419
|
+
code: AppErrorCode10.ValidationFailed,
|
|
1083
1420
|
message: "Anthropic tool input was not valid JSON",
|
|
1084
1421
|
statusCode: 502,
|
|
1085
1422
|
cause: error instanceof Error ? error : void 0
|
|
@@ -1091,10 +1428,6 @@ function parseAnthropicToolInputJson(inputJsonText) {
|
|
|
1091
1428
|
var defaultAnthropicBaseUrl = "https://api.anthropic.com";
|
|
1092
1429
|
var defaultAnthropicMaxOutputTokens = 1024;
|
|
1093
1430
|
var anthropicApiVersion = "2023-06-01";
|
|
1094
|
-
var structuredOutputSystemMessage2 = {
|
|
1095
|
-
role: "system",
|
|
1096
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
1097
|
-
};
|
|
1098
1431
|
function anthropic(options) {
|
|
1099
1432
|
return {
|
|
1100
1433
|
name: `anthropic:${options.model}`,
|
|
@@ -1106,9 +1439,12 @@ function anthropic(options) {
|
|
|
1106
1439
|
},
|
|
1107
1440
|
async generateStructuredOutput(request) {
|
|
1108
1441
|
const completion = await executeAnthropicAdapterCall(options, {
|
|
1109
|
-
messages:
|
|
1442
|
+
messages: request.messages,
|
|
1110
1443
|
temperature: request.temperature ?? options.temperature,
|
|
1111
1444
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1445
|
+
structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
|
|
1446
|
+
request.schema
|
|
1447
|
+
),
|
|
1112
1448
|
abortSignal: request.abortSignal
|
|
1113
1449
|
});
|
|
1114
1450
|
return {
|
|
@@ -1131,7 +1467,8 @@ function buildAnthropicCallInput(options, request) {
|
|
|
1131
1467
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1132
1468
|
tools: request.tools,
|
|
1133
1469
|
toolChoice: request.toolChoice,
|
|
1134
|
-
abortSignal: request.abortSignal
|
|
1470
|
+
abortSignal: request.abortSignal,
|
|
1471
|
+
extraBody: request.extraBody
|
|
1135
1472
|
};
|
|
1136
1473
|
}
|
|
1137
1474
|
async function executeAnthropicAdapterCall(options, input) {
|
|
@@ -1148,9 +1485,10 @@ async function executeAnthropicAdapterCall(options, input) {
|
|
|
1148
1485
|
);
|
|
1149
1486
|
}
|
|
1150
1487
|
async function requestAnthropicMessages(options, input) {
|
|
1151
|
-
const requestBody =
|
|
1152
|
-
buildAnthropicRequestSettings(options),
|
|
1153
|
-
|
|
1488
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
1489
|
+
buildAnthropicRequestBody(buildAnthropicRequestSettings(options), input),
|
|
1490
|
+
options.extraBody,
|
|
1491
|
+
input.extraBody
|
|
1154
1492
|
);
|
|
1155
1493
|
const startedAt = Date.now();
|
|
1156
1494
|
const response = await executeAbortableProviderFetch(
|
|
@@ -1179,9 +1517,10 @@ async function requestAnthropicMessages(options, input) {
|
|
|
1179
1517
|
}
|
|
1180
1518
|
function buildAnthropicStreamInput(options, request) {
|
|
1181
1519
|
const input = buildAnthropicCallInput(options, request);
|
|
1182
|
-
const requestBody =
|
|
1183
|
-
buildAnthropicRequestSettings(options),
|
|
1184
|
-
|
|
1520
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
1521
|
+
buildAnthropicRequestBody(buildAnthropicRequestSettings(options), input),
|
|
1522
|
+
options.extraBody,
|
|
1523
|
+
input.extraBody
|
|
1185
1524
|
);
|
|
1186
1525
|
return {
|
|
1187
1526
|
url: `${resolveAnthropicBaseUrl(options)}/v1/messages`,
|
|
@@ -1221,28 +1560,116 @@ function resolveAnthropicFetch(options) {
|
|
|
1221
1560
|
}
|
|
1222
1561
|
|
|
1223
1562
|
// src/gemini-adapter.ts
|
|
1224
|
-
import { z as
|
|
1225
|
-
import { AppErrorCode as
|
|
1226
|
-
import { AppError as
|
|
1563
|
+
import { z as z7 } from "zod";
|
|
1564
|
+
import { AppErrorCode as AppErrorCode11 } from "@assemble-dev/shared-types/errors";
|
|
1565
|
+
import { AppError as AppError12 } from "@assemble-dev/shared-utils/errors";
|
|
1566
|
+
|
|
1567
|
+
// src/gemini-response-schema.ts
|
|
1568
|
+
var geminiAllowedSchemaKeywords = [
|
|
1569
|
+
"type",
|
|
1570
|
+
"format",
|
|
1571
|
+
"description",
|
|
1572
|
+
"nullable",
|
|
1573
|
+
"enum",
|
|
1574
|
+
"properties",
|
|
1575
|
+
"required",
|
|
1576
|
+
"items",
|
|
1577
|
+
"minItems",
|
|
1578
|
+
"maxItems",
|
|
1579
|
+
"anyOf"
|
|
1580
|
+
];
|
|
1581
|
+
var geminiAllowedFormats = [
|
|
1582
|
+
"date-time",
|
|
1583
|
+
"int32",
|
|
1584
|
+
"int64",
|
|
1585
|
+
"float",
|
|
1586
|
+
"double"
|
|
1587
|
+
];
|
|
1588
|
+
function buildGeminiResponseSchema(schema) {
|
|
1589
|
+
return sanitizeJsonSchemaForGeminiResponseSchema(
|
|
1590
|
+
convertZodSchemaToJsonSchema(schema)
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
function sanitizeJsonSchemaForGeminiResponseSchema(jsonSchema) {
|
|
1594
|
+
return transformJsonSchemaNode(
|
|
1595
|
+
inlineJsonSchemaReferences(jsonSchema),
|
|
1596
|
+
sanitizeGeminiSchemaNode
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
function sanitizeGeminiSchemaNode(node) {
|
|
1600
|
+
const normalized = normalizeGeminiNullableType(
|
|
1601
|
+
mergeNullUnionIntoNullableNode(node)
|
|
1602
|
+
);
|
|
1603
|
+
const sanitized = {};
|
|
1604
|
+
for (const [key, value] of Object.entries(normalized)) {
|
|
1605
|
+
if (geminiAllowedSchemaKeywords.includes(key)) {
|
|
1606
|
+
sanitized[key] = value;
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return removeUnsupportedGeminiFormat(sanitized);
|
|
1610
|
+
}
|
|
1611
|
+
function mergeNullUnionIntoNullableNode(node) {
|
|
1612
|
+
const unionKeyword = Array.isArray(node["anyOf"]) ? "anyOf" : "oneOf";
|
|
1613
|
+
const unionMembers = node[unionKeyword];
|
|
1614
|
+
if (!Array.isArray(unionMembers)) {
|
|
1615
|
+
return node;
|
|
1616
|
+
}
|
|
1617
|
+
const nonNullMembers = unionMembers.filter(
|
|
1618
|
+
(member) => !isNullTypeSchemaNode(member)
|
|
1619
|
+
);
|
|
1620
|
+
if (nonNullMembers.length === unionMembers.length) {
|
|
1621
|
+
return node;
|
|
1622
|
+
}
|
|
1623
|
+
const merged = { ...node };
|
|
1624
|
+
delete merged[unionKeyword];
|
|
1625
|
+
const singleMember = nonNullMembers.length === 1 ? nonNullMembers[0] : void 0;
|
|
1626
|
+
if (singleMember !== void 0 && isJsonObject(singleMember)) {
|
|
1627
|
+
return { ...merged, ...singleMember, nullable: true };
|
|
1628
|
+
}
|
|
1629
|
+
return { ...merged, anyOf: nonNullMembers, nullable: true };
|
|
1630
|
+
}
|
|
1631
|
+
function normalizeGeminiNullableType(node) {
|
|
1632
|
+
const typeValue = node["type"];
|
|
1633
|
+
if (!Array.isArray(typeValue)) {
|
|
1634
|
+
return node;
|
|
1635
|
+
}
|
|
1636
|
+
const nonNullTypes = typeValue.filter((typeName) => typeName !== "null");
|
|
1637
|
+
const normalized = { ...node };
|
|
1638
|
+
if (nonNullTypes.length < typeValue.length) {
|
|
1639
|
+
normalized["nullable"] = true;
|
|
1640
|
+
}
|
|
1641
|
+
normalized["type"] = nonNullTypes[0] ?? "string";
|
|
1642
|
+
return normalized;
|
|
1643
|
+
}
|
|
1644
|
+
function removeUnsupportedGeminiFormat(node) {
|
|
1645
|
+
const formatValue = node["format"];
|
|
1646
|
+
if (typeof formatValue === "string" && !geminiAllowedFormats.includes(formatValue)) {
|
|
1647
|
+
const withoutFormat = { ...node };
|
|
1648
|
+
delete withoutFormat["format"];
|
|
1649
|
+
return withoutFormat;
|
|
1650
|
+
}
|
|
1651
|
+
return node;
|
|
1652
|
+
}
|
|
1653
|
+
function isNullTypeSchemaNode(value) {
|
|
1654
|
+
return isJsonObject(value) && value["type"] === "null";
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
// src/gemini-adapter.ts
|
|
1227
1658
|
var defaultGeminiBaseUrl = "https://generativelanguage.googleapis.com";
|
|
1228
|
-
var GeminiGenerateContentResponseSchema =
|
|
1229
|
-
candidates:
|
|
1230
|
-
|
|
1231
|
-
content:
|
|
1232
|
-
parts:
|
|
1659
|
+
var GeminiGenerateContentResponseSchema = z7.object({
|
|
1660
|
+
candidates: z7.array(
|
|
1661
|
+
z7.object({
|
|
1662
|
+
content: z7.object({
|
|
1663
|
+
parts: z7.array(z7.object({ text: z7.string() }))
|
|
1233
1664
|
})
|
|
1234
1665
|
})
|
|
1235
1666
|
).optional(),
|
|
1236
|
-
usageMetadata:
|
|
1237
|
-
promptTokenCount:
|
|
1238
|
-
candidatesTokenCount:
|
|
1239
|
-
totalTokenCount:
|
|
1667
|
+
usageMetadata: z7.object({
|
|
1668
|
+
promptTokenCount: z7.number().int().nonnegative().optional(),
|
|
1669
|
+
candidatesTokenCount: z7.number().int().nonnegative().optional(),
|
|
1670
|
+
totalTokenCount: z7.number().int().nonnegative().optional()
|
|
1240
1671
|
}).optional()
|
|
1241
1672
|
});
|
|
1242
|
-
var structuredOutputSystemMessage3 = {
|
|
1243
|
-
role: "system",
|
|
1244
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
1245
|
-
};
|
|
1246
1673
|
function gemini(options) {
|
|
1247
1674
|
return {
|
|
1248
1675
|
name: `gemini:${options.model}`,
|
|
@@ -1252,16 +1679,16 @@ function gemini(options) {
|
|
|
1252
1679
|
messages: request.messages,
|
|
1253
1680
|
temperature: request.temperature ?? options.temperature,
|
|
1254
1681
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1255
|
-
|
|
1256
|
-
|
|
1682
|
+
abortSignal: request.abortSignal,
|
|
1683
|
+
extraBody: request.extraBody
|
|
1257
1684
|
});
|
|
1258
1685
|
},
|
|
1259
1686
|
async generateStructuredOutput(request) {
|
|
1260
1687
|
const completion = await executeGeminiAdapterCall(options, {
|
|
1261
|
-
messages:
|
|
1688
|
+
messages: request.messages,
|
|
1262
1689
|
temperature: request.temperature ?? options.temperature,
|
|
1263
1690
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1264
|
-
|
|
1691
|
+
responseSchema: buildGeminiResponseSchema(request.schema),
|
|
1265
1692
|
abortSignal: request.abortSignal
|
|
1266
1693
|
});
|
|
1267
1694
|
return {
|
|
@@ -1295,7 +1722,11 @@ async function requestGeminiGenerateContent(options, input) {
|
|
|
1295
1722
|
});
|
|
1296
1723
|
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
1297
1724
|
const baseUrl = options.baseURL ?? defaultGeminiBaseUrl;
|
|
1298
|
-
const requestBody =
|
|
1725
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
1726
|
+
buildGeminiRequestBody(input),
|
|
1727
|
+
options.extraBody,
|
|
1728
|
+
input.extraBody
|
|
1729
|
+
);
|
|
1299
1730
|
const requestUrl = `${baseUrl}/v1beta/models/${options.model}:generateContent`;
|
|
1300
1731
|
const startedAt = Date.now();
|
|
1301
1732
|
const response = await executeAbortableProviderFetch(
|
|
@@ -1344,8 +1775,8 @@ function ensureGeminiRequestHasNoTools(request) {
|
|
|
1344
1775
|
if (request.tools === void 0 || request.tools.length === 0) {
|
|
1345
1776
|
return;
|
|
1346
1777
|
}
|
|
1347
|
-
throw new
|
|
1348
|
-
code:
|
|
1778
|
+
throw new AppError12({
|
|
1779
|
+
code: AppErrorCode11.ValidationFailed,
|
|
1349
1780
|
message: "gemini adapter does not support model-driven tool use; remove tools from the request or use another adapter",
|
|
1350
1781
|
statusCode: 400
|
|
1351
1782
|
});
|
|
@@ -1382,8 +1813,9 @@ function buildGeminiGenerationConfig(input) {
|
|
|
1382
1813
|
if (input.maxOutputTokens !== void 0) {
|
|
1383
1814
|
generationConfig.maxOutputTokens = input.maxOutputTokens;
|
|
1384
1815
|
}
|
|
1385
|
-
if (input.
|
|
1816
|
+
if (input.responseSchema !== void 0) {
|
|
1386
1817
|
generationConfig.responseMimeType = "application/json";
|
|
1818
|
+
generationConfig.responseSchema = input.responseSchema;
|
|
1387
1819
|
}
|
|
1388
1820
|
if (Object.keys(generationConfig).length === 0) {
|
|
1389
1821
|
return void 0;
|
|
@@ -1393,8 +1825,8 @@ function buildGeminiGenerationConfig(input) {
|
|
|
1393
1825
|
function extractGeminiCandidateText(response) {
|
|
1394
1826
|
const firstCandidate = response.candidates?.[0];
|
|
1395
1827
|
if (firstCandidate === void 0) {
|
|
1396
|
-
throw new
|
|
1397
|
-
code:
|
|
1828
|
+
throw new AppError12({
|
|
1829
|
+
code: AppErrorCode11.ValidationFailed,
|
|
1398
1830
|
message: "Gemini response contained no candidates",
|
|
1399
1831
|
statusCode: 502
|
|
1400
1832
|
});
|
|
@@ -1413,8 +1845,8 @@ function mapGeminiUsageToTokenUsage(response) {
|
|
|
1413
1845
|
}
|
|
1414
1846
|
|
|
1415
1847
|
// src/mock-adapter.ts
|
|
1416
|
-
import { AppErrorCode as
|
|
1417
|
-
import { AppError as
|
|
1848
|
+
import { AppErrorCode as AppErrorCode12 } from "@assemble-dev/shared-types/errors";
|
|
1849
|
+
import { AppError as AppError13 } from "@assemble-dev/shared-utils/errors";
|
|
1418
1850
|
var mockStreamChunkCount = 3;
|
|
1419
1851
|
function mockModel(options = {}) {
|
|
1420
1852
|
const remainingScriptedResponses = [...options.scriptedResponses ?? []];
|
|
@@ -1494,8 +1926,8 @@ function resolveNextMockResponseText(request, remainingScriptedResponses, respon
|
|
|
1494
1926
|
if (respond !== void 0) {
|
|
1495
1927
|
return respond(request);
|
|
1496
1928
|
}
|
|
1497
|
-
throw new
|
|
1498
|
-
code:
|
|
1929
|
+
throw new AppError13({
|
|
1930
|
+
code: AppErrorCode12.BadRequest,
|
|
1499
1931
|
message: "Mock model has no scripted responses left and no respond function was provided",
|
|
1500
1932
|
statusCode: 400
|
|
1501
1933
|
});
|
|
@@ -1583,60 +2015,277 @@ function calculateModelCallCostUsd(pricing, tokenUsage) {
|
|
|
1583
2015
|
}
|
|
1584
2016
|
|
|
1585
2017
|
// src/anthropic-batch.ts
|
|
1586
|
-
import { AppErrorCode as
|
|
2018
|
+
import { AppErrorCode as AppErrorCode14 } from "@assemble-dev/shared-types/errors";
|
|
2019
|
+
import { JsonValueSchema as JsonValueSchema7 } from "@assemble-dev/shared-types/json";
|
|
2020
|
+
import { AppError as AppError15 } from "@assemble-dev/shared-utils/errors";
|
|
2021
|
+
|
|
2022
|
+
// src/anthropic-batch-structured.ts
|
|
2023
|
+
import { AppErrorCode as AppErrorCode13 } from "@assemble-dev/shared-types/errors";
|
|
1587
2024
|
import { JsonValueSchema as JsonValueSchema6 } from "@assemble-dev/shared-types/json";
|
|
1588
|
-
import { AppError as
|
|
2025
|
+
import { AppError as AppError14 } from "@assemble-dev/shared-utils/errors";
|
|
2026
|
+
var anthropicBatchJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
|
|
2027
|
+
function parseAnthropicBatchStructuredContent(content, schema) {
|
|
2028
|
+
const jsonValue = parseStructuredJsonText(
|
|
2029
|
+
content,
|
|
2030
|
+
JsonValueSchema6
|
|
2031
|
+
);
|
|
2032
|
+
const validationResult = schema.safeParse(jsonValue);
|
|
2033
|
+
if (!validationResult.success) {
|
|
2034
|
+
throw new AppError14({
|
|
2035
|
+
code: AppErrorCode13.ValidationFailed,
|
|
2036
|
+
message: "Anthropic batch structured output did not match the expected schema",
|
|
2037
|
+
statusCode: 422,
|
|
2038
|
+
details: { validationErrorMessage: validationResult.error.message }
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
return validationResult.data;
|
|
2042
|
+
}
|
|
1589
2043
|
|
|
1590
|
-
// src/anthropic-batch-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
2044
|
+
// src/anthropic-batch-request.ts
|
|
2045
|
+
function mapBatchMessageRequestToApiBatchRequest(request) {
|
|
2046
|
+
const params = {
|
|
2047
|
+
model: request.model,
|
|
2048
|
+
max_tokens: request.maxOutputTokens ?? defaultAnthropicMaxOutputTokens,
|
|
2049
|
+
messages: request.messages.filter((message) => message.role !== "system").map(mapChatMessageToBatchApiRequestMessage)
|
|
2050
|
+
};
|
|
2051
|
+
applyBatchSystemField(params, request);
|
|
2052
|
+
applyBatchStructuredOutputConfig(params, request);
|
|
2053
|
+
if (request.temperature !== void 0) {
|
|
2054
|
+
params.temperature = request.temperature;
|
|
2055
|
+
}
|
|
2056
|
+
return { custom_id: request.customId, params };
|
|
2057
|
+
}
|
|
2058
|
+
function applyBatchStructuredOutputConfig(params, request) {
|
|
2059
|
+
if (request.structuredOutputJsonSchema === void 0) {
|
|
2060
|
+
return;
|
|
2061
|
+
}
|
|
2062
|
+
params.output_config = {
|
|
2063
|
+
format: {
|
|
2064
|
+
type: "json_schema",
|
|
2065
|
+
schema: sanitizeJsonSchemaForAnthropicStructuredOutput(
|
|
2066
|
+
request.structuredOutputJsonSchema
|
|
2067
|
+
)
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
function applyBatchSystemField(params, request) {
|
|
2072
|
+
const systemText = buildBatchSystemText(request);
|
|
2073
|
+
if (systemText.length === 0) {
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
if (request.promptCaching !== "auto") {
|
|
2077
|
+
params.system = systemText;
|
|
2078
|
+
return;
|
|
2079
|
+
}
|
|
2080
|
+
params.system = [
|
|
2081
|
+
{ type: "text", text: systemText, cache_control: { type: "ephemeral" } }
|
|
2082
|
+
];
|
|
2083
|
+
}
|
|
2084
|
+
function buildBatchSystemText(request) {
|
|
2085
|
+
const joinedSystemText = joinBatchSystemMessageText(request.messages);
|
|
2086
|
+
if (request.requireJsonResponse !== true || request.structuredOutputJsonSchema !== void 0) {
|
|
2087
|
+
return joinedSystemText;
|
|
2088
|
+
}
|
|
2089
|
+
if (joinedSystemText.length === 0) {
|
|
2090
|
+
return anthropicBatchJsonOnlySystemInstruction;
|
|
2091
|
+
}
|
|
2092
|
+
return `${anthropicBatchJsonOnlySystemInstruction}
|
|
2093
|
+
|
|
2094
|
+
${joinedSystemText}`;
|
|
2095
|
+
}
|
|
2096
|
+
function joinBatchSystemMessageText(messages) {
|
|
2097
|
+
return messages.filter((message) => message.role === "system").map(extractMessageText).join("\n\n");
|
|
2098
|
+
}
|
|
2099
|
+
function mapChatMessageToBatchApiRequestMessage(message) {
|
|
2100
|
+
if (message.role === "assistant") {
|
|
2101
|
+
return { role: "assistant", content: extractMessageText(message) };
|
|
2102
|
+
}
|
|
2103
|
+
if (message.role === "tool") {
|
|
2104
|
+
return { role: "user", content: buildBatchToolResultText(message) };
|
|
2105
|
+
}
|
|
2106
|
+
return { role: "user", content: extractMessageText(message) };
|
|
2107
|
+
}
|
|
2108
|
+
function buildBatchToolResultText(message) {
|
|
2109
|
+
if (message.toolName === void 0) {
|
|
2110
|
+
return `Tool result: ${extractMessageText(message)}`;
|
|
2111
|
+
}
|
|
2112
|
+
return `Tool result (${message.toolName}): ${extractMessageText(message)}`;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// src/anthropic-batch-trace-events.ts
|
|
2116
|
+
import {
|
|
2117
|
+
generateRunId,
|
|
2118
|
+
generateTraceEventId
|
|
2119
|
+
} from "@assemble-dev/shared-utils/ids";
|
|
2120
|
+
var anthropicBatchTraceWorkflowName = "anthropic-message-batch";
|
|
2121
|
+
function createAnthropicBatchTraceEmitter(input) {
|
|
2122
|
+
if (input.onTraceEvent === void 0) {
|
|
2123
|
+
return createNoopAnthropicBatchTraceEmitter();
|
|
2124
|
+
}
|
|
2125
|
+
const onTraceEvent = input.onTraceEvent;
|
|
2126
|
+
const runId = generateRunId();
|
|
2127
|
+
const completedBatchIds = /* @__PURE__ */ new Set();
|
|
2128
|
+
const submittedEventIdByBatchId = /* @__PURE__ */ new Map();
|
|
2129
|
+
const modelByBatchScopedCustomId = /* @__PURE__ */ new Map();
|
|
2130
|
+
function buildBatchTraceEventBase(parentEventId) {
|
|
2131
|
+
return {
|
|
2132
|
+
id: generateTraceEventId(),
|
|
2133
|
+
runId,
|
|
2134
|
+
workflowName: anthropicBatchTraceWorkflowName,
|
|
2135
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2136
|
+
parentEventId,
|
|
2137
|
+
redacted: false
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
function stampMetadataOntoEvent(event) {
|
|
2141
|
+
if (input.metadata === void 0) {
|
|
2142
|
+
return event;
|
|
2143
|
+
}
|
|
2144
|
+
return { ...event, metadata: input.metadata };
|
|
2145
|
+
}
|
|
2146
|
+
function emitBatchSubmittedEvent(batch, requestSummaries) {
|
|
2147
|
+
const submittedEvent = {
|
|
2148
|
+
...buildBatchTraceEventBase(null),
|
|
2149
|
+
type: "batch.submitted",
|
|
2150
|
+
batchId: batch.id,
|
|
2151
|
+
requestCount: requestSummaries.length,
|
|
2152
|
+
models: requestSummaries.map((requestSummary) => requestSummary.model)
|
|
2153
|
+
};
|
|
2154
|
+
submittedEventIdByBatchId.set(batch.id, submittedEvent.id);
|
|
2155
|
+
for (const requestSummary of requestSummaries) {
|
|
2156
|
+
modelByBatchScopedCustomId.set(
|
|
2157
|
+
buildBatchScopedCustomIdKey(batch.id, requestSummary.customId),
|
|
2158
|
+
requestSummary.model
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
2161
|
+
onTraceEvent(stampMetadataOntoEvent(submittedEvent));
|
|
2162
|
+
}
|
|
2163
|
+
function emitBatchCompletedEventOnce(batch) {
|
|
2164
|
+
if (batch.processingStatus !== "ended" || completedBatchIds.has(batch.id)) {
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
completedBatchIds.add(batch.id);
|
|
2168
|
+
const completedEvent = {
|
|
2169
|
+
...buildBatchTraceEventBase(
|
|
2170
|
+
submittedEventIdByBatchId.get(batch.id) ?? null
|
|
2171
|
+
),
|
|
2172
|
+
type: "batch.completed",
|
|
2173
|
+
batchId: batch.id,
|
|
2174
|
+
requestCounts: batch.requestCounts,
|
|
2175
|
+
latencyMs: calculateBatchLatencyMs(batch)
|
|
2176
|
+
};
|
|
2177
|
+
onTraceEvent(stampMetadataOntoEvent(completedEvent));
|
|
2178
|
+
}
|
|
2179
|
+
function emitBatchResultEvent(batchId, result) {
|
|
2180
|
+
const resultEvent = buildBatchResultEvent(
|
|
2181
|
+
buildBatchTraceEventBase(submittedEventIdByBatchId.get(batchId) ?? null),
|
|
2182
|
+
batchId,
|
|
2183
|
+
result,
|
|
2184
|
+
modelByBatchScopedCustomId.get(
|
|
2185
|
+
buildBatchScopedCustomIdKey(batchId, result.customId)
|
|
2186
|
+
)
|
|
2187
|
+
);
|
|
2188
|
+
onTraceEvent(stampMetadataOntoEvent(resultEvent));
|
|
2189
|
+
}
|
|
2190
|
+
return {
|
|
2191
|
+
emitBatchSubmittedEvent,
|
|
2192
|
+
emitBatchCompletedEventOnce,
|
|
2193
|
+
emitBatchResultEvent
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
function createNoopAnthropicBatchTraceEmitter() {
|
|
2197
|
+
return {
|
|
2198
|
+
emitBatchSubmittedEvent() {
|
|
2199
|
+
},
|
|
2200
|
+
emitBatchCompletedEventOnce() {
|
|
2201
|
+
},
|
|
2202
|
+
emitBatchResultEvent() {
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
function buildBatchScopedCustomIdKey(batchId, customId) {
|
|
2207
|
+
return `${batchId}:${customId}`;
|
|
2208
|
+
}
|
|
2209
|
+
function calculateBatchLatencyMs(batch) {
|
|
2210
|
+
const createdAtMs = Date.parse(batch.createdAt);
|
|
2211
|
+
const endedAtMs = batch.endedAt === null ? Date.now() : Date.parse(batch.endedAt);
|
|
2212
|
+
if (Number.isNaN(createdAtMs) || Number.isNaN(endedAtMs)) {
|
|
2213
|
+
return 0;
|
|
2214
|
+
}
|
|
2215
|
+
return Math.max(0, endedAtMs - createdAtMs);
|
|
2216
|
+
}
|
|
2217
|
+
function buildBatchResultEvent(eventBase, batchId, result, modelName) {
|
|
2218
|
+
if (result.outcome.type !== "succeeded") {
|
|
2219
|
+
return {
|
|
2220
|
+
...eventBase,
|
|
2221
|
+
type: "batch.result",
|
|
2222
|
+
batchId,
|
|
2223
|
+
customId: result.customId,
|
|
2224
|
+
outcomeType: result.outcome.type,
|
|
2225
|
+
costUsd: null
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
return {
|
|
2229
|
+
...eventBase,
|
|
2230
|
+
type: "batch.result",
|
|
2231
|
+
batchId,
|
|
2232
|
+
customId: result.customId,
|
|
2233
|
+
outcomeType: "succeeded",
|
|
2234
|
+
tokenUsage: result.outcome.tokenUsage,
|
|
2235
|
+
costUsd: modelName === void 0 ? null : estimateModelCallCostUsd(modelName, result.outcome.tokenUsage)
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
// src/anthropic-batch-types.ts
|
|
2240
|
+
import { z as z8 } from "zod";
|
|
2241
|
+
var AnthropicMessageBatchResponseSchema = z8.object({
|
|
2242
|
+
id: z8.string(),
|
|
2243
|
+
processing_status: z8.enum(["in_progress", "canceling", "ended"]),
|
|
2244
|
+
request_counts: z8.object({
|
|
2245
|
+
processing: z8.number().int().nonnegative(),
|
|
2246
|
+
succeeded: z8.number().int().nonnegative(),
|
|
2247
|
+
errored: z8.number().int().nonnegative(),
|
|
2248
|
+
canceled: z8.number().int().nonnegative(),
|
|
2249
|
+
expired: z8.number().int().nonnegative()
|
|
2250
|
+
}),
|
|
2251
|
+
results_url: z8.string().nullable(),
|
|
2252
|
+
created_at: z8.string(),
|
|
2253
|
+
ended_at: z8.string().nullable()
|
|
2254
|
+
});
|
|
2255
|
+
var AnthropicBatchContentBlockSchema = z8.discriminatedUnion("type", [
|
|
2256
|
+
z8.object({
|
|
2257
|
+
type: z8.literal("text"),
|
|
2258
|
+
text: z8.string()
|
|
2259
|
+
}),
|
|
2260
|
+
z8.object({
|
|
2261
|
+
type: z8.literal("thinking"),
|
|
2262
|
+
thinking: z8.string().optional(),
|
|
2263
|
+
signature: z8.string().optional()
|
|
2264
|
+
}),
|
|
2265
|
+
z8.object({
|
|
2266
|
+
type: z8.literal("redacted_thinking"),
|
|
2267
|
+
data: z8.string().optional()
|
|
2268
|
+
})
|
|
2269
|
+
]);
|
|
2270
|
+
var AnthropicBatchResultLineSchema = z8.object({
|
|
2271
|
+
custom_id: z8.string(),
|
|
2272
|
+
result: z8.discriminatedUnion("type", [
|
|
2273
|
+
z8.object({
|
|
2274
|
+
type: z8.literal("succeeded"),
|
|
2275
|
+
message: z8.object({
|
|
2276
|
+
content: z8.array(AnthropicBatchContentBlockSchema),
|
|
2277
|
+
usage: AnthropicUsageSchema
|
|
2278
|
+
})
|
|
2279
|
+
}),
|
|
2280
|
+
z8.object({
|
|
2281
|
+
type: z8.literal("errored"),
|
|
2282
|
+
error: z8.object({
|
|
2283
|
+
type: z8.string(),
|
|
2284
|
+
message: z8.string()
|
|
1636
2285
|
})
|
|
1637
2286
|
}),
|
|
1638
|
-
|
|
1639
|
-
|
|
2287
|
+
z8.object({ type: z8.literal("canceled") }),
|
|
2288
|
+
z8.object({ type: z8.literal("expired") })
|
|
1640
2289
|
])
|
|
1641
2290
|
});
|
|
1642
2291
|
function mapAnthropicMessageBatchResponseToMessageBatch(response) {
|
|
@@ -1690,19 +2339,26 @@ var defaultAnthropicBatchPollIntervalMs = 5e3;
|
|
|
1690
2339
|
var defaultAnthropicBatchTimeoutMs = 36e5;
|
|
1691
2340
|
var anthropicBatchApiVersion = "2023-06-01";
|
|
1692
2341
|
function createAnthropicBatchClient(options) {
|
|
2342
|
+
const clientContext = {
|
|
2343
|
+
options,
|
|
2344
|
+
traceEmitter: createAnthropicBatchTraceEmitter({
|
|
2345
|
+
onTraceEvent: options.onTraceEvent,
|
|
2346
|
+
metadata: options.metadata
|
|
2347
|
+
})
|
|
2348
|
+
};
|
|
1693
2349
|
return {
|
|
1694
2350
|
async submitMessageBatch(requests) {
|
|
1695
|
-
return await submitAnthropicMessageBatch(
|
|
2351
|
+
return await submitAnthropicMessageBatch(clientContext, requests);
|
|
1696
2352
|
},
|
|
1697
2353
|
async fetchMessageBatch(batchId) {
|
|
1698
|
-
return await fetchAnthropicMessageBatch(
|
|
2354
|
+
return await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1699
2355
|
},
|
|
1700
2356
|
async listMessageBatchResults(batchId) {
|
|
1701
|
-
return await listAnthropicMessageBatchResults(
|
|
2357
|
+
return await listAnthropicMessageBatchResults(clientContext, batchId);
|
|
1702
2358
|
},
|
|
1703
2359
|
async waitForMessageBatchCompletion(batchId, waitOptions) {
|
|
1704
2360
|
return await waitForAnthropicMessageBatchCompletion(
|
|
1705
|
-
|
|
2361
|
+
clientContext,
|
|
1706
2362
|
batchId,
|
|
1707
2363
|
waitOptions
|
|
1708
2364
|
);
|
|
@@ -1729,8 +2385,8 @@ function buildAnthropicBatchRequestHeaders(apiKey) {
|
|
|
1729
2385
|
"anthropic-version": anthropicBatchApiVersion
|
|
1730
2386
|
};
|
|
1731
2387
|
}
|
|
1732
|
-
async function submitAnthropicMessageBatch(
|
|
1733
|
-
const context = resolveAnthropicBatchRequestContext(options);
|
|
2388
|
+
async function submitAnthropicMessageBatch(clientContext, requests) {
|
|
2389
|
+
const context = resolveAnthropicBatchRequestContext(clientContext.options);
|
|
1734
2390
|
const response = await context.fetchImplementation(
|
|
1735
2391
|
`${context.baseUrl}/v1/messages/batches`,
|
|
1736
2392
|
{
|
|
@@ -1741,10 +2397,18 @@ async function submitAnthropicMessageBatch(options, requests) {
|
|
|
1741
2397
|
})
|
|
1742
2398
|
}
|
|
1743
2399
|
);
|
|
1744
|
-
|
|
2400
|
+
const batch = await parseAnthropicMessageBatchHttpResponse(response);
|
|
2401
|
+
clientContext.traceEmitter.emitBatchSubmittedEvent(
|
|
2402
|
+
batch,
|
|
2403
|
+
requests.map((request) => ({
|
|
2404
|
+
customId: request.customId,
|
|
2405
|
+
model: request.model
|
|
2406
|
+
}))
|
|
2407
|
+
);
|
|
2408
|
+
return batch;
|
|
1745
2409
|
}
|
|
1746
|
-
async function fetchAnthropicMessageBatch(
|
|
1747
|
-
const context = resolveAnthropicBatchRequestContext(options);
|
|
2410
|
+
async function fetchAnthropicMessageBatch(clientContext, batchId) {
|
|
2411
|
+
const context = resolveAnthropicBatchRequestContext(clientContext.options);
|
|
1748
2412
|
const response = await context.fetchImplementation(
|
|
1749
2413
|
`${context.baseUrl}/v1/messages/batches/${batchId}`,
|
|
1750
2414
|
{
|
|
@@ -1752,7 +2416,9 @@ async function fetchAnthropicMessageBatch(options, batchId) {
|
|
|
1752
2416
|
headers: buildAnthropicBatchRequestHeaders(context.apiKey)
|
|
1753
2417
|
}
|
|
1754
2418
|
);
|
|
1755
|
-
|
|
2419
|
+
const batch = await parseAnthropicMessageBatchHttpResponse(response);
|
|
2420
|
+
clientContext.traceEmitter.emitBatchCompletedEventOnce(batch);
|
|
2421
|
+
return batch;
|
|
1756
2422
|
}
|
|
1757
2423
|
async function parseAnthropicMessageBatchHttpResponse(response) {
|
|
1758
2424
|
if (!response.ok) {
|
|
@@ -1766,17 +2432,17 @@ async function parseAnthropicMessageBatchHttpResponse(response) {
|
|
|
1766
2432
|
}
|
|
1767
2433
|
return mapAnthropicMessageBatchResponseToMessageBatch(parseResult.data);
|
|
1768
2434
|
}
|
|
1769
|
-
async function listAnthropicMessageBatchResults(
|
|
1770
|
-
const batch = await fetchAnthropicMessageBatch(
|
|
2435
|
+
async function listAnthropicMessageBatchResults(clientContext, batchId) {
|
|
2436
|
+
const batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1771
2437
|
if (batch.resultsUrl === null) {
|
|
1772
|
-
throw new
|
|
1773
|
-
code:
|
|
2438
|
+
throw new AppError15({
|
|
2439
|
+
code: AppErrorCode14.Conflict,
|
|
1774
2440
|
message: `Anthropic message batch ${batchId} has no results because processing has not ended`,
|
|
1775
2441
|
statusCode: 409,
|
|
1776
2442
|
details: { batchId, processingStatus: batch.processingStatus }
|
|
1777
2443
|
});
|
|
1778
2444
|
}
|
|
1779
|
-
const context = resolveAnthropicBatchRequestContext(options);
|
|
2445
|
+
const context = resolveAnthropicBatchRequestContext(clientContext.options);
|
|
1780
2446
|
const response = await context.fetchImplementation(batch.resultsUrl, {
|
|
1781
2447
|
method: "GET",
|
|
1782
2448
|
headers: buildAnthropicBatchRequestHeaders(context.apiKey)
|
|
@@ -1784,7 +2450,11 @@ async function listAnthropicMessageBatchResults(options, batchId) {
|
|
|
1784
2450
|
if (!response.ok) {
|
|
1785
2451
|
throw buildProviderStatusError("Anthropic", response.status);
|
|
1786
2452
|
}
|
|
1787
|
-
|
|
2453
|
+
const results = parseAnthropicBatchResultsJsonl(await response.text());
|
|
2454
|
+
for (const result of results) {
|
|
2455
|
+
clientContext.traceEmitter.emitBatchResultEvent(batchId, result);
|
|
2456
|
+
}
|
|
2457
|
+
return results;
|
|
1788
2458
|
}
|
|
1789
2459
|
function parseAnthropicBatchResultsJsonl(jsonlText) {
|
|
1790
2460
|
return jsonlText.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map(parseAnthropicBatchResultLine);
|
|
@@ -1803,7 +2473,7 @@ function parseAnthropicBatchResultLine(line) {
|
|
|
1803
2473
|
}
|
|
1804
2474
|
function parseAnthropicBatchResultLineJson(line) {
|
|
1805
2475
|
try {
|
|
1806
|
-
return
|
|
2476
|
+
return JsonValueSchema7.parse(JSON.parse(line));
|
|
1807
2477
|
} catch {
|
|
1808
2478
|
throw buildProviderInvalidResponseError(
|
|
1809
2479
|
"Anthropic",
|
|
@@ -1811,80 +2481,810 @@ function parseAnthropicBatchResultLineJson(line) {
|
|
|
1811
2481
|
);
|
|
1812
2482
|
}
|
|
1813
2483
|
}
|
|
1814
|
-
async function waitForAnthropicMessageBatchCompletion(
|
|
2484
|
+
async function waitForAnthropicMessageBatchCompletion(clientContext, batchId, waitOptions) {
|
|
1815
2485
|
const pollIntervalMs = waitOptions?.pollIntervalMs ?? defaultAnthropicBatchPollIntervalMs;
|
|
1816
2486
|
const timeoutMs = waitOptions?.timeoutMs ?? defaultAnthropicBatchTimeoutMs;
|
|
1817
2487
|
const waitFor = waitOptions?.waitFor ?? waitForMilliseconds;
|
|
1818
2488
|
const startedAt = Date.now();
|
|
1819
|
-
let batch = await fetchAnthropicMessageBatch(
|
|
2489
|
+
let batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1820
2490
|
while (batch.processingStatus !== "ended") {
|
|
1821
2491
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
1822
2492
|
throw buildAnthropicBatchTimeoutError(batchId, timeoutMs);
|
|
1823
2493
|
}
|
|
1824
2494
|
await waitFor(pollIntervalMs);
|
|
1825
|
-
batch = await fetchAnthropicMessageBatch(
|
|
2495
|
+
batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
1826
2496
|
}
|
|
1827
2497
|
return batch;
|
|
1828
2498
|
}
|
|
1829
2499
|
function buildAnthropicBatchTimeoutError(batchId, timeoutMs) {
|
|
1830
|
-
return new
|
|
1831
|
-
code:
|
|
2500
|
+
return new AppError15({
|
|
2501
|
+
code: AppErrorCode14.ServiceUnavailable,
|
|
1832
2502
|
message: `Anthropic message batch ${batchId} did not complete within ${String(timeoutMs)}ms`,
|
|
1833
2503
|
statusCode: 504,
|
|
1834
2504
|
details: { batchId, timeoutMs }
|
|
1835
2505
|
});
|
|
1836
2506
|
}
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
2507
|
+
|
|
2508
|
+
// src/azure-openai-adapter.ts
|
|
2509
|
+
import { AppErrorCode as AppErrorCode16 } from "@assemble-dev/shared-types/errors";
|
|
2510
|
+
import { AppError as AppError17 } from "@assemble-dev/shared-utils/errors";
|
|
2511
|
+
|
|
2512
|
+
// src/azure-openai-response-parsing.ts
|
|
2513
|
+
import { z as z9 } from "zod";
|
|
2514
|
+
import { AppErrorCode as AppErrorCode15 } from "@assemble-dev/shared-types/errors";
|
|
2515
|
+
import { JsonValueSchema as JsonValueSchema8 } from "@assemble-dev/shared-types/json";
|
|
2516
|
+
import { AppError as AppError16 } from "@assemble-dev/shared-utils/errors";
|
|
2517
|
+
var AzureOpenAiUsageSchema = z9.object({
|
|
2518
|
+
prompt_tokens: z9.number().int().nonnegative(),
|
|
2519
|
+
completion_tokens: z9.number().int().nonnegative(),
|
|
2520
|
+
total_tokens: z9.number().int().nonnegative()
|
|
2521
|
+
});
|
|
2522
|
+
var AzureOpenAiResponseToolCallSchema = z9.object({
|
|
2523
|
+
id: z9.string(),
|
|
2524
|
+
function: z9.object({
|
|
2525
|
+
name: z9.string(),
|
|
2526
|
+
arguments: z9.string()
|
|
2527
|
+
})
|
|
2528
|
+
});
|
|
2529
|
+
var AzureOpenAiChatCompletionResponseSchema = z9.object({
|
|
2530
|
+
choices: z9.array(
|
|
2531
|
+
z9.object({
|
|
2532
|
+
message: z9.object({
|
|
2533
|
+
content: z9.string().nullable(),
|
|
2534
|
+
tool_calls: z9.array(AzureOpenAiResponseToolCallSchema).optional()
|
|
2535
|
+
}),
|
|
2536
|
+
finish_reason: z9.string().nullable().optional()
|
|
2537
|
+
})
|
|
2538
|
+
).min(1),
|
|
2539
|
+
usage: AzureOpenAiUsageSchema
|
|
2540
|
+
});
|
|
2541
|
+
function mapAzureOpenAiResponseToChatCompletionResult(response, latencyMs) {
|
|
2542
|
+
const toolCalls = extractAzureOpenAiToolCalls(response);
|
|
2543
|
+
const result = {
|
|
2544
|
+
content: extractAzureOpenAiMessageContent(response, toolCalls.length > 0),
|
|
2545
|
+
tokenUsage: mapAzureOpenAiUsageToTokenUsage(response.usage),
|
|
2546
|
+
latencyMs
|
|
1842
2547
|
};
|
|
1843
|
-
const
|
|
1844
|
-
if (
|
|
1845
|
-
|
|
2548
|
+
const finishReason = response.choices[0]?.finish_reason;
|
|
2549
|
+
if (toolCalls.length > 0) {
|
|
2550
|
+
result.toolCalls = toolCalls;
|
|
1846
2551
|
}
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
2552
|
+
if (finishReason !== null && finishReason !== void 0) {
|
|
2553
|
+
result.stopReason = normalizeAzureOpenAiFinishReason(finishReason);
|
|
1849
2554
|
}
|
|
1850
|
-
return
|
|
2555
|
+
return result;
|
|
1851
2556
|
}
|
|
1852
|
-
function
|
|
1853
|
-
|
|
2557
|
+
function extractAzureOpenAiToolCalls(response) {
|
|
2558
|
+
const responseToolCalls = response.choices[0]?.message.tool_calls ?? [];
|
|
2559
|
+
return responseToolCalls.map((responseToolCall) => ({
|
|
2560
|
+
id: responseToolCall.id,
|
|
2561
|
+
toolName: responseToolCall.function.name,
|
|
2562
|
+
input: parseAzureOpenAiToolArgumentsJson(responseToolCall.function.arguments)
|
|
2563
|
+
}));
|
|
1854
2564
|
}
|
|
1855
|
-
function
|
|
1856
|
-
if (
|
|
1857
|
-
return {
|
|
2565
|
+
function parseAzureOpenAiToolArgumentsJson(argumentsJsonText) {
|
|
2566
|
+
if (argumentsJsonText.trim().length === 0) {
|
|
2567
|
+
return {};
|
|
1858
2568
|
}
|
|
1859
|
-
|
|
1860
|
-
return
|
|
2569
|
+
try {
|
|
2570
|
+
return JsonValueSchema8.parse(JSON.parse(argumentsJsonText));
|
|
2571
|
+
} catch (error) {
|
|
2572
|
+
throw new AppError16({
|
|
2573
|
+
code: AppErrorCode15.ValidationFailed,
|
|
2574
|
+
message: "Azure OpenAI tool call arguments were not valid JSON",
|
|
2575
|
+
statusCode: 502,
|
|
2576
|
+
cause: error instanceof Error ? error : void 0
|
|
2577
|
+
});
|
|
1861
2578
|
}
|
|
1862
|
-
return { role: "user", content: extractMessageText(message) };
|
|
1863
2579
|
}
|
|
1864
|
-
function
|
|
1865
|
-
if (
|
|
1866
|
-
return
|
|
2580
|
+
function normalizeAzureOpenAiFinishReason(finishReason) {
|
|
2581
|
+
if (finishReason === "tool_calls") {
|
|
2582
|
+
return "tool_use";
|
|
1867
2583
|
}
|
|
1868
|
-
|
|
2584
|
+
if (finishReason === "length") {
|
|
2585
|
+
return "max_tokens";
|
|
2586
|
+
}
|
|
2587
|
+
return "end_turn";
|
|
2588
|
+
}
|
|
2589
|
+
function extractAzureOpenAiMessageContent(response, allowEmptyContent) {
|
|
2590
|
+
const content = response.choices[0]?.message.content;
|
|
2591
|
+
if (content === void 0 || content === null) {
|
|
2592
|
+
if (allowEmptyContent) {
|
|
2593
|
+
return "";
|
|
2594
|
+
}
|
|
2595
|
+
throw new AppError16({
|
|
2596
|
+
code: AppErrorCode15.ValidationFailed,
|
|
2597
|
+
message: "Azure OpenAI response contained no message content",
|
|
2598
|
+
statusCode: 502
|
|
2599
|
+
});
|
|
2600
|
+
}
|
|
2601
|
+
return content;
|
|
2602
|
+
}
|
|
2603
|
+
function mapAzureOpenAiUsageToTokenUsage(usage) {
|
|
2604
|
+
return {
|
|
2605
|
+
inputTokens: usage.prompt_tokens,
|
|
2606
|
+
outputTokens: usage.completion_tokens,
|
|
2607
|
+
totalTokens: usage.total_tokens
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
// src/azure-openai-stream.ts
|
|
2612
|
+
import { z as z10 } from "zod";
|
|
2613
|
+
var AzureOpenAiStreamToolCallDeltaSchema = z10.object({
|
|
2614
|
+
index: z10.number().int().nonnegative(),
|
|
2615
|
+
id: z10.string().optional(),
|
|
2616
|
+
function: z10.object({
|
|
2617
|
+
name: z10.string().optional(),
|
|
2618
|
+
arguments: z10.string().optional()
|
|
2619
|
+
}).optional()
|
|
2620
|
+
});
|
|
2621
|
+
var AzureOpenAiStreamChunkSchema = z10.object({
|
|
2622
|
+
choices: z10.array(
|
|
2623
|
+
z10.object({
|
|
2624
|
+
delta: z10.object({
|
|
2625
|
+
content: z10.string().nullable().optional(),
|
|
2626
|
+
tool_calls: z10.array(AzureOpenAiStreamToolCallDeltaSchema).optional()
|
|
2627
|
+
}).optional(),
|
|
2628
|
+
finish_reason: z10.string().nullable().optional()
|
|
2629
|
+
})
|
|
2630
|
+
).optional(),
|
|
2631
|
+
usage: AzureOpenAiUsageSchema.nullable().optional()
|
|
2632
|
+
});
|
|
2633
|
+
async function* streamAzureOpenAiChatCompletion(input) {
|
|
2634
|
+
const startedAt = Date.now();
|
|
2635
|
+
const response = await establishAzureOpenAiStreamResponse(input);
|
|
2636
|
+
if (response.body === null) {
|
|
2637
|
+
throw buildProviderInvalidResponseError("Azure OpenAI", "stream body");
|
|
2638
|
+
}
|
|
2639
|
+
yield* parseAzureOpenAiStreamEvents(
|
|
2640
|
+
response.body,
|
|
2641
|
+
startedAt,
|
|
2642
|
+
input.abortSignal
|
|
2643
|
+
);
|
|
2644
|
+
}
|
|
2645
|
+
async function establishAzureOpenAiStreamResponse(input) {
|
|
2646
|
+
return await executeProviderCallWithRetry(input.retry, async () => {
|
|
2647
|
+
const response = await executeAbortableProviderFetch(
|
|
2648
|
+
"Azure OpenAI",
|
|
2649
|
+
async () => await input.fetchImplementation(input.url, {
|
|
2650
|
+
method: "POST",
|
|
2651
|
+
headers: input.headers,
|
|
2652
|
+
body: JSON.stringify(input.body),
|
|
2653
|
+
signal: input.abortSignal
|
|
2654
|
+
})
|
|
2655
|
+
);
|
|
2656
|
+
if (!response.ok) {
|
|
2657
|
+
throw buildProviderStatusError("Azure OpenAI", response.status);
|
|
2658
|
+
}
|
|
2659
|
+
return response;
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
async function* parseAzureOpenAiStreamEvents(body, startedAt, abortSignal) {
|
|
2663
|
+
const accumulator = createAzureOpenAiStreamAccumulator();
|
|
2664
|
+
for await (const dataText of readServerSentEventDataLines2(body, abortSignal)) {
|
|
2665
|
+
if (dataText === "[DONE]") {
|
|
2666
|
+
yield {
|
|
2667
|
+
type: "completed",
|
|
2668
|
+
result: buildAzureOpenAiStreamResult(
|
|
2669
|
+
accumulator,
|
|
2670
|
+
Date.now() - startedAt
|
|
2671
|
+
)
|
|
2672
|
+
};
|
|
2673
|
+
return;
|
|
2674
|
+
}
|
|
2675
|
+
const parsedChunk = AzureOpenAiStreamChunkSchema.safeParse(
|
|
2676
|
+
JSON.parse(dataText)
|
|
2677
|
+
);
|
|
2678
|
+
if (!parsedChunk.success) {
|
|
2679
|
+
continue;
|
|
2680
|
+
}
|
|
2681
|
+
yield* applyAzureOpenAiStreamChunk(parsedChunk.data, accumulator);
|
|
2682
|
+
}
|
|
2683
|
+
throw buildProviderInvalidResponseError("Azure OpenAI", "stream termination");
|
|
2684
|
+
}
|
|
2685
|
+
async function* readServerSentEventDataLines2(body, abortSignal) {
|
|
2686
|
+
const reader = body.getReader();
|
|
2687
|
+
const decoder = new TextDecoder();
|
|
2688
|
+
let bufferedText = "";
|
|
2689
|
+
try {
|
|
2690
|
+
for (; ; ) {
|
|
2691
|
+
if (abortSignal?.aborted === true) {
|
|
2692
|
+
await reader.cancel();
|
|
2693
|
+
throw buildProviderCanceledError("Azure OpenAI");
|
|
2694
|
+
}
|
|
2695
|
+
const readResult = await reader.read();
|
|
2696
|
+
if (readResult.done) {
|
|
2697
|
+
return;
|
|
2698
|
+
}
|
|
2699
|
+
bufferedText += decoder.decode(readResult.value, { stream: true });
|
|
2700
|
+
const lines = bufferedText.split("\n");
|
|
2701
|
+
bufferedText = lines.pop() ?? "";
|
|
2702
|
+
for (const line of lines) {
|
|
2703
|
+
if (line.startsWith("data:")) {
|
|
2704
|
+
const dataText = line.slice("data:".length).trim();
|
|
2705
|
+
if (dataText.length > 0) {
|
|
2706
|
+
yield dataText;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
} catch (error) {
|
|
2712
|
+
if (error instanceof Error && isAbortError(error)) {
|
|
2713
|
+
throw buildProviderCanceledError("Azure OpenAI");
|
|
2714
|
+
}
|
|
2715
|
+
throw error;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
function createAzureOpenAiStreamAccumulator() {
|
|
2719
|
+
return {
|
|
2720
|
+
contentText: "",
|
|
2721
|
+
toolCallsByIndex: /* @__PURE__ */ new Map()
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
function* applyAzureOpenAiStreamChunk(chunk, accumulator) {
|
|
2725
|
+
if (chunk.usage !== null && chunk.usage !== void 0) {
|
|
2726
|
+
accumulator.usage = chunk.usage;
|
|
2727
|
+
}
|
|
2728
|
+
const choice = chunk.choices?.[0];
|
|
2729
|
+
if (choice === void 0) {
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
if (choice.finish_reason !== null && choice.finish_reason !== void 0) {
|
|
2733
|
+
accumulator.finishReason = choice.finish_reason;
|
|
2734
|
+
}
|
|
2735
|
+
if (choice.delta === void 0) {
|
|
2736
|
+
return;
|
|
2737
|
+
}
|
|
2738
|
+
const contentDelta = choice.delta.content;
|
|
2739
|
+
if (contentDelta !== null && contentDelta !== void 0 && contentDelta.length > 0) {
|
|
2740
|
+
accumulator.contentText += contentDelta;
|
|
2741
|
+
yield { type: "text_delta", text: contentDelta };
|
|
2742
|
+
}
|
|
2743
|
+
for (const toolCallDelta of choice.delta.tool_calls ?? []) {
|
|
2744
|
+
yield* applyAzureOpenAiToolCallDelta(toolCallDelta, accumulator);
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
function* applyAzureOpenAiToolCallDelta(toolCallDelta, accumulator) {
|
|
2748
|
+
const existingToolCall = accumulator.toolCallsByIndex.get(toolCallDelta.index);
|
|
2749
|
+
if (existingToolCall === void 0) {
|
|
2750
|
+
yield* startAzureOpenAiToolCallAccumulation(toolCallDelta, accumulator);
|
|
2751
|
+
return;
|
|
2752
|
+
}
|
|
2753
|
+
const argumentsDelta = toolCallDelta.function?.arguments;
|
|
2754
|
+
if (argumentsDelta !== void 0 && argumentsDelta.length > 0) {
|
|
2755
|
+
existingToolCall.argumentsJsonText += argumentsDelta;
|
|
2756
|
+
yield {
|
|
2757
|
+
type: "tool_call_input_delta",
|
|
2758
|
+
id: existingToolCall.id,
|
|
2759
|
+
inputJsonText: argumentsDelta
|
|
2760
|
+
};
|
|
2761
|
+
}
|
|
2762
|
+
}
|
|
2763
|
+
function* startAzureOpenAiToolCallAccumulation(toolCallDelta, accumulator) {
|
|
2764
|
+
const toolName = toolCallDelta.function?.name;
|
|
2765
|
+
if (toolCallDelta.id === void 0 || toolName === void 0) {
|
|
2766
|
+
throw buildProviderInvalidResponseError(
|
|
2767
|
+
"Azure OpenAI",
|
|
2768
|
+
"stream tool call start"
|
|
2769
|
+
);
|
|
2770
|
+
}
|
|
2771
|
+
const startedToolCall = {
|
|
2772
|
+
id: toolCallDelta.id,
|
|
2773
|
+
toolName,
|
|
2774
|
+
argumentsJsonText: toolCallDelta.function?.arguments ?? ""
|
|
2775
|
+
};
|
|
2776
|
+
accumulator.toolCallsByIndex.set(toolCallDelta.index, startedToolCall);
|
|
2777
|
+
yield {
|
|
2778
|
+
type: "tool_call_started",
|
|
2779
|
+
id: startedToolCall.id,
|
|
2780
|
+
toolName: startedToolCall.toolName
|
|
2781
|
+
};
|
|
2782
|
+
if (startedToolCall.argumentsJsonText.length > 0) {
|
|
2783
|
+
yield {
|
|
2784
|
+
type: "tool_call_input_delta",
|
|
2785
|
+
id: startedToolCall.id,
|
|
2786
|
+
inputJsonText: startedToolCall.argumentsJsonText
|
|
2787
|
+
};
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
function buildAzureOpenAiStreamResult(accumulator, latencyMs) {
|
|
2791
|
+
const result = {
|
|
2792
|
+
content: accumulator.contentText,
|
|
2793
|
+
tokenUsage: mapAzureOpenAiUsageToTokenUsage(
|
|
2794
|
+
accumulator.usage ?? {
|
|
2795
|
+
prompt_tokens: 0,
|
|
2796
|
+
completion_tokens: 0,
|
|
2797
|
+
total_tokens: 0
|
|
2798
|
+
}
|
|
2799
|
+
),
|
|
2800
|
+
latencyMs
|
|
2801
|
+
};
|
|
2802
|
+
const toolCalls = [...accumulator.toolCallsByIndex.values()].map(
|
|
2803
|
+
mapAccumulatedToolCallToModelToolCall2
|
|
2804
|
+
);
|
|
2805
|
+
if (toolCalls.length > 0) {
|
|
2806
|
+
result.toolCalls = toolCalls;
|
|
2807
|
+
}
|
|
2808
|
+
if (accumulator.finishReason !== void 0) {
|
|
2809
|
+
result.stopReason = normalizeAzureOpenAiFinishReason(
|
|
2810
|
+
accumulator.finishReason
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
return result;
|
|
2814
|
+
}
|
|
2815
|
+
function mapAccumulatedToolCallToModelToolCall2(toolCallAccumulation) {
|
|
2816
|
+
return {
|
|
2817
|
+
id: toolCallAccumulation.id,
|
|
2818
|
+
toolName: toolCallAccumulation.toolName,
|
|
2819
|
+
input: parseAzureOpenAiToolArgumentsJson(
|
|
2820
|
+
toolCallAccumulation.argumentsJsonText
|
|
2821
|
+
)
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
// src/azure-openai-adapter.ts
|
|
2826
|
+
var defaultAzureOpenAiApiVersion = "2024-10-21";
|
|
2827
|
+
var azureDeploymentModelPlaceholder = "azure-deployment";
|
|
2828
|
+
function azureOpenai(options) {
|
|
2829
|
+
return {
|
|
2830
|
+
name: `azure-openai:${options.deployment}`,
|
|
2831
|
+
async generateChatCompletion(request) {
|
|
2832
|
+
return await executeAzureOpenAiAdapterCall(options, {
|
|
2833
|
+
messages: request.messages,
|
|
2834
|
+
temperature: request.temperature ?? options.temperature,
|
|
2835
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
2836
|
+
tools: request.tools,
|
|
2837
|
+
toolChoice: request.toolChoice,
|
|
2838
|
+
abortSignal: request.abortSignal,
|
|
2839
|
+
extraBody: request.extraBody
|
|
2840
|
+
});
|
|
2841
|
+
},
|
|
2842
|
+
async generateStructuredOutput(request) {
|
|
2843
|
+
const completion = await executeAzureOpenAiAdapterCall(options, {
|
|
2844
|
+
messages: request.messages,
|
|
2845
|
+
temperature: request.temperature ?? options.temperature,
|
|
2846
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
2847
|
+
structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
|
|
2848
|
+
request.schema
|
|
2849
|
+
),
|
|
2850
|
+
abortSignal: request.abortSignal
|
|
2851
|
+
});
|
|
2852
|
+
return {
|
|
2853
|
+
value: parseStructuredJsonText(completion.content, request.schema),
|
|
2854
|
+
tokenUsage: completion.tokenUsage,
|
|
2855
|
+
latencyMs: completion.latencyMs
|
|
2856
|
+
};
|
|
2857
|
+
},
|
|
2858
|
+
streamChatCompletion(request) {
|
|
2859
|
+
return streamAzureOpenAiChatCompletion(
|
|
2860
|
+
buildAzureOpenAiStreamInput(options, request)
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
};
|
|
2864
|
+
}
|
|
2865
|
+
function buildAzureOpenAiChatCompletionsUrl(options) {
|
|
2866
|
+
const endpoint = resolveAzureOpenAiEndpoint(options);
|
|
2867
|
+
const apiVersion = options.apiVersion ?? defaultAzureOpenAiApiVersion;
|
|
2868
|
+
return `${endpoint}/openai/deployments/${encodeURIComponent(options.deployment)}/chat/completions?api-version=${encodeURIComponent(apiVersion)}`;
|
|
2869
|
+
}
|
|
2870
|
+
function resolveAzureOpenAiEndpoint(options) {
|
|
2871
|
+
if (options.baseURL !== void 0 && options.baseURL.length > 0) {
|
|
2872
|
+
return options.baseURL.replace(/\/+$/, "");
|
|
2873
|
+
}
|
|
2874
|
+
if (options.resourceName !== void 0 && options.resourceName.length > 0) {
|
|
2875
|
+
return `https://${options.resourceName}.openai.azure.com`;
|
|
2876
|
+
}
|
|
2877
|
+
throw new AppError17({
|
|
2878
|
+
code: AppErrorCode16.BadRequest,
|
|
2879
|
+
message: "Azure OpenAI endpoint is missing. Pass resourceName or baseURL to azureOpenai().",
|
|
2880
|
+
statusCode: 400
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
function buildAzureOpenAiRequestHeaders(options) {
|
|
2884
|
+
return {
|
|
2885
|
+
"content-type": "application/json",
|
|
2886
|
+
"api-key": resolveProviderApiKey({
|
|
2887
|
+
providerLabel: "Azure OpenAI",
|
|
2888
|
+
adapterFunctionName: "azureOpenai()",
|
|
2889
|
+
baseEnvVarName: "AZURE_OPENAI_API_KEY",
|
|
2890
|
+
apiKey: options.apiKey,
|
|
2891
|
+
apiKeyName: options.apiKeyName
|
|
2892
|
+
})
|
|
2893
|
+
};
|
|
2894
|
+
}
|
|
2895
|
+
function buildAzureOpenAiChatRequestBody(input) {
|
|
2896
|
+
const requestBody = buildOpenAiRequestBody(
|
|
2897
|
+
azureDeploymentModelPlaceholder,
|
|
2898
|
+
input
|
|
2899
|
+
);
|
|
2900
|
+
const { model: ignoredModel, ...requestBodyWithoutModel } = requestBody;
|
|
2901
|
+
void ignoredModel;
|
|
2902
|
+
return requestBodyWithoutModel;
|
|
2903
|
+
}
|
|
2904
|
+
async function executeAzureOpenAiAdapterCall(options, input) {
|
|
2905
|
+
return await executeProviderCallWithRetry(
|
|
2906
|
+
options.retry,
|
|
2907
|
+
async () => await requestAzureOpenAiChatCompletion(options, input)
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
async function requestAzureOpenAiChatCompletion(options, input) {
|
|
2911
|
+
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
2912
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
2913
|
+
buildAzureOpenAiChatRequestBody(input),
|
|
2914
|
+
options.extraBody,
|
|
2915
|
+
input.extraBody
|
|
2916
|
+
);
|
|
2917
|
+
const startedAt = Date.now();
|
|
2918
|
+
const response = await executeAbortableProviderFetch(
|
|
2919
|
+
"Azure OpenAI",
|
|
2920
|
+
async () => await fetchImplementation(buildAzureOpenAiChatCompletionsUrl(options), {
|
|
2921
|
+
method: "POST",
|
|
2922
|
+
headers: buildAzureOpenAiRequestHeaders(options),
|
|
2923
|
+
body: JSON.stringify(requestBody),
|
|
2924
|
+
signal: input.abortSignal
|
|
2925
|
+
})
|
|
2926
|
+
);
|
|
2927
|
+
const latencyMs = Date.now() - startedAt;
|
|
2928
|
+
if (!response.ok) {
|
|
2929
|
+
throw buildProviderStatusError("Azure OpenAI", response.status);
|
|
2930
|
+
}
|
|
2931
|
+
const parseResult = AzureOpenAiChatCompletionResponseSchema.safeParse(
|
|
2932
|
+
await response.json()
|
|
2933
|
+
);
|
|
2934
|
+
if (!parseResult.success) {
|
|
2935
|
+
throw buildProviderInvalidResponseError("Azure OpenAI", "chat completion");
|
|
2936
|
+
}
|
|
2937
|
+
return mapAzureOpenAiResponseToChatCompletionResult(
|
|
2938
|
+
parseResult.data,
|
|
2939
|
+
latencyMs
|
|
2940
|
+
);
|
|
2941
|
+
}
|
|
2942
|
+
function buildAzureOpenAiStreamInput(options, request) {
|
|
2943
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
2944
|
+
buildAzureOpenAiChatRequestBody({
|
|
2945
|
+
messages: request.messages,
|
|
2946
|
+
temperature: request.temperature ?? options.temperature,
|
|
2947
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
2948
|
+
tools: request.tools,
|
|
2949
|
+
toolChoice: request.toolChoice,
|
|
2950
|
+
abortSignal: request.abortSignal,
|
|
2951
|
+
extraBody: request.extraBody
|
|
2952
|
+
}),
|
|
2953
|
+
options.extraBody,
|
|
2954
|
+
request.extraBody
|
|
2955
|
+
);
|
|
2956
|
+
return {
|
|
2957
|
+
url: buildAzureOpenAiChatCompletionsUrl(options),
|
|
2958
|
+
headers: buildAzureOpenAiRequestHeaders(options),
|
|
2959
|
+
body: {
|
|
2960
|
+
...requestBody,
|
|
2961
|
+
stream: true,
|
|
2962
|
+
stream_options: { include_usage: true }
|
|
2963
|
+
},
|
|
2964
|
+
fetchImplementation: options.fetchImplementation ?? fetch,
|
|
2965
|
+
retry: options.retry,
|
|
2966
|
+
abortSignal: request.abortSignal
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// src/bedrock-adapter.ts
|
|
2971
|
+
import { z as z11 } from "zod";
|
|
2972
|
+
import { AppErrorCode as AppErrorCode17 } from "@assemble-dev/shared-types/errors";
|
|
2973
|
+
import { AppError as AppError18 } from "@assemble-dev/shared-utils/errors";
|
|
2974
|
+
|
|
2975
|
+
// src/bedrock-sigv4.ts
|
|
2976
|
+
var awsSigV4Algorithm = "AWS4-HMAC-SHA256";
|
|
2977
|
+
async function signAwsRequestWithSigV4(input) {
|
|
2978
|
+
const amzDate = formatAmzDateTime(input.signingDate ?? /* @__PURE__ */ new Date());
|
|
2979
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
2980
|
+
const headersToSign = buildHeadersToSign(input, amzDate);
|
|
2981
|
+
const canonicalHeaderEntries = buildCanonicalHeaderEntries(headersToSign);
|
|
2982
|
+
const signedHeaderNames = canonicalHeaderEntries.map(([headerName]) => headerName).join(";");
|
|
2983
|
+
const credentialScope = `${dateStamp}/${input.region}/${input.service}/aws4_request`;
|
|
2984
|
+
const signatureHex = await computeAwsSigV4Signature(
|
|
2985
|
+
input,
|
|
2986
|
+
amzDate,
|
|
2987
|
+
credentialScope,
|
|
2988
|
+
canonicalHeaderEntries,
|
|
2989
|
+
signedHeaderNames
|
|
2990
|
+
);
|
|
2991
|
+
return {
|
|
2992
|
+
...headersToSign,
|
|
2993
|
+
authorization: `${awsSigV4Algorithm} Credential=${input.credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaderNames}, Signature=${signatureHex}`
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
async function computeAwsSigV4Signature(input, amzDate, credentialScope, canonicalHeaderEntries, signedHeaderNames) {
|
|
2997
|
+
const canonicalRequest = await buildCanonicalRequest(
|
|
2998
|
+
input,
|
|
2999
|
+
canonicalHeaderEntries,
|
|
3000
|
+
signedHeaderNames
|
|
3001
|
+
);
|
|
3002
|
+
const stringToSign = [
|
|
3003
|
+
awsSigV4Algorithm,
|
|
3004
|
+
amzDate,
|
|
3005
|
+
credentialScope,
|
|
3006
|
+
await hashSha256Hex(canonicalRequest)
|
|
3007
|
+
].join("\n");
|
|
3008
|
+
const signingKey = await deriveAwsSigningKey(
|
|
3009
|
+
input.credentials.secretAccessKey,
|
|
3010
|
+
amzDate.slice(0, 8),
|
|
3011
|
+
input.region,
|
|
3012
|
+
input.service
|
|
3013
|
+
);
|
|
3014
|
+
return bytesToHex(await computeHmacSha256(signingKey, stringToSign));
|
|
3015
|
+
}
|
|
3016
|
+
function buildHeadersToSign(input, amzDate) {
|
|
3017
|
+
const headersToSign = {};
|
|
3018
|
+
for (const [headerName, headerValue] of Object.entries(input.headers)) {
|
|
3019
|
+
headersToSign[headerName.toLowerCase()] = headerValue;
|
|
3020
|
+
}
|
|
3021
|
+
headersToSign.host = input.url.host;
|
|
3022
|
+
headersToSign["x-amz-date"] = amzDate;
|
|
3023
|
+
if (input.credentials.sessionToken !== void 0) {
|
|
3024
|
+
headersToSign["x-amz-security-token"] = input.credentials.sessionToken;
|
|
3025
|
+
}
|
|
3026
|
+
return headersToSign;
|
|
3027
|
+
}
|
|
3028
|
+
function buildCanonicalHeaderEntries(headersToSign) {
|
|
3029
|
+
return Object.entries(headersToSign).map(([headerName, headerValue]) => [
|
|
3030
|
+
headerName.toLowerCase(),
|
|
3031
|
+
headerValue.trim().replace(/\s+/g, " ")
|
|
3032
|
+
]).sort(([leftName], [rightName]) => leftName.localeCompare(rightName));
|
|
3033
|
+
}
|
|
3034
|
+
async function buildCanonicalRequest(input, canonicalHeaderEntries, signedHeaderNames) {
|
|
3035
|
+
const canonicalHeadersText = canonicalHeaderEntries.map(([headerName, headerValue]) => `${headerName}:${headerValue}`).join("\n");
|
|
3036
|
+
return [
|
|
3037
|
+
input.method.toUpperCase(),
|
|
3038
|
+
buildCanonicalUri(input.url.pathname),
|
|
3039
|
+
buildCanonicalQueryString(input.url.searchParams),
|
|
3040
|
+
`${canonicalHeadersText}
|
|
3041
|
+
`,
|
|
3042
|
+
signedHeaderNames,
|
|
3043
|
+
await hashSha256Hex(input.bodyText)
|
|
3044
|
+
].join("\n");
|
|
3045
|
+
}
|
|
3046
|
+
function buildCanonicalUri(pathname) {
|
|
3047
|
+
if (pathname.length === 0) {
|
|
3048
|
+
return "/";
|
|
3049
|
+
}
|
|
3050
|
+
return pathname.split("/").map(encodeRfc3986UriComponent).join("/");
|
|
3051
|
+
}
|
|
3052
|
+
function buildCanonicalQueryString(searchParams) {
|
|
3053
|
+
const encodedEntries = [];
|
|
3054
|
+
searchParams.forEach((parameterValue, parameterName) => {
|
|
3055
|
+
encodedEntries.push([
|
|
3056
|
+
encodeRfc3986UriComponent(parameterName),
|
|
3057
|
+
encodeRfc3986UriComponent(parameterValue)
|
|
3058
|
+
]);
|
|
3059
|
+
});
|
|
3060
|
+
return encodedEntries.sort(
|
|
3061
|
+
([leftName, leftValue], [rightName, rightValue]) => leftName.localeCompare(rightName) || leftValue.localeCompare(rightValue)
|
|
3062
|
+
).map(([parameterName, parameterValue]) => `${parameterName}=${parameterValue}`).join("&");
|
|
3063
|
+
}
|
|
3064
|
+
function encodeRfc3986UriComponent(value) {
|
|
3065
|
+
return encodeURIComponent(value).replace(
|
|
3066
|
+
/[!'()*]/g,
|
|
3067
|
+
(character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
|
|
3068
|
+
);
|
|
3069
|
+
}
|
|
3070
|
+
function formatAmzDateTime(date) {
|
|
3071
|
+
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
3072
|
+
}
|
|
3073
|
+
async function deriveAwsSigningKey(secretAccessKey, dateStamp, region, service) {
|
|
3074
|
+
const dateKey = await computeHmacSha256(
|
|
3075
|
+
new TextEncoder().encode(`AWS4${secretAccessKey}`),
|
|
3076
|
+
dateStamp
|
|
3077
|
+
);
|
|
3078
|
+
const regionKey = await computeHmacSha256(dateKey, region);
|
|
3079
|
+
const serviceKey = await computeHmacSha256(regionKey, service);
|
|
3080
|
+
return await computeHmacSha256(serviceKey, "aws4_request");
|
|
3081
|
+
}
|
|
3082
|
+
async function hashSha256Hex(text) {
|
|
3083
|
+
const digest = await crypto.subtle.digest(
|
|
3084
|
+
"SHA-256",
|
|
3085
|
+
new TextEncoder().encode(text)
|
|
3086
|
+
);
|
|
3087
|
+
return bytesToHex(new Uint8Array(digest));
|
|
3088
|
+
}
|
|
3089
|
+
async function computeHmacSha256(keyBytes, text) {
|
|
3090
|
+
const hmacKey = await crypto.subtle.importKey(
|
|
3091
|
+
"raw",
|
|
3092
|
+
keyBytes,
|
|
3093
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
3094
|
+
false,
|
|
3095
|
+
["sign"]
|
|
3096
|
+
);
|
|
3097
|
+
const signature = await crypto.subtle.sign(
|
|
3098
|
+
"HMAC",
|
|
3099
|
+
hmacKey,
|
|
3100
|
+
new TextEncoder().encode(text)
|
|
3101
|
+
);
|
|
3102
|
+
return new Uint8Array(signature);
|
|
3103
|
+
}
|
|
3104
|
+
function bytesToHex(bytes) {
|
|
3105
|
+
return Array.from(bytes).map((byteValue) => byteValue.toString(16).padStart(2, "0")).join("");
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
// src/bedrock-adapter.ts
|
|
3109
|
+
var bedrockAnthropicVersion = "bedrock-2023-05-31";
|
|
3110
|
+
var defaultBedrockMaxOutputTokens = 1024;
|
|
3111
|
+
var bedrockSigningServiceName = "bedrock";
|
|
3112
|
+
var bedrockModelPlaceholder = "bedrock-invoke-model";
|
|
3113
|
+
var BedrockEnvCredentialsSchema = z11.object({
|
|
3114
|
+
AWS_ACCESS_KEY_ID: z11.string().optional(),
|
|
3115
|
+
AWS_SECRET_ACCESS_KEY: z11.string().optional(),
|
|
3116
|
+
AWS_SESSION_TOKEN: z11.string().optional()
|
|
3117
|
+
});
|
|
3118
|
+
function bedrock(options) {
|
|
3119
|
+
return {
|
|
3120
|
+
name: `bedrock:${options.modelId}`,
|
|
3121
|
+
async generateChatCompletion(request) {
|
|
3122
|
+
return await executeBedrockAdapterCall(options, {
|
|
3123
|
+
messages: request.messages,
|
|
3124
|
+
temperature: request.temperature ?? options.temperature,
|
|
3125
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
3126
|
+
tools: request.tools,
|
|
3127
|
+
toolChoice: request.toolChoice,
|
|
3128
|
+
abortSignal: request.abortSignal,
|
|
3129
|
+
extraBody: request.extraBody
|
|
3130
|
+
});
|
|
3131
|
+
},
|
|
3132
|
+
async generateStructuredOutput(request) {
|
|
3133
|
+
const completion = await executeBedrockAdapterCall(options, {
|
|
3134
|
+
messages: [
|
|
3135
|
+
buildBedrockStructuredOutputSystemMessage(request.schema),
|
|
3136
|
+
...request.messages
|
|
3137
|
+
],
|
|
3138
|
+
temperature: request.temperature ?? options.temperature,
|
|
3139
|
+
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
3140
|
+
abortSignal: request.abortSignal
|
|
3141
|
+
});
|
|
3142
|
+
return {
|
|
3143
|
+
value: parseStructuredJsonText(completion.content, request.schema),
|
|
3144
|
+
tokenUsage: completion.tokenUsage,
|
|
3145
|
+
latencyMs: completion.latencyMs
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3150
|
+
function buildBedrockInvokeModelUrl(region, modelId) {
|
|
3151
|
+
return `https://bedrock-runtime.${region}.amazonaws.com/model/${encodeURIComponent(modelId)}/invoke`;
|
|
3152
|
+
}
|
|
3153
|
+
function resolveBedrockCredentials(options) {
|
|
3154
|
+
if (options.credentials !== void 0) {
|
|
3155
|
+
return options.credentials;
|
|
3156
|
+
}
|
|
3157
|
+
const parsedEnv = BedrockEnvCredentialsSchema.parse(process.env);
|
|
3158
|
+
const accessKeyId = parsedEnv.AWS_ACCESS_KEY_ID;
|
|
3159
|
+
const secretAccessKey = parsedEnv.AWS_SECRET_ACCESS_KEY;
|
|
3160
|
+
if (accessKeyId === void 0 || accessKeyId.length === 0 || secretAccessKey === void 0 || secretAccessKey.length === 0) {
|
|
3161
|
+
throw new AppError18({
|
|
3162
|
+
code: AppErrorCode17.Unauthorized,
|
|
3163
|
+
message: "AWS credentials are missing. Pass credentials to bedrock() or set the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables.",
|
|
3164
|
+
statusCode: 401
|
|
3165
|
+
});
|
|
3166
|
+
}
|
|
3167
|
+
const credentials = { accessKeyId, secretAccessKey };
|
|
3168
|
+
const sessionToken = parsedEnv.AWS_SESSION_TOKEN;
|
|
3169
|
+
if (sessionToken !== void 0 && sessionToken.length > 0) {
|
|
3170
|
+
credentials.sessionToken = sessionToken;
|
|
3171
|
+
}
|
|
3172
|
+
return credentials;
|
|
3173
|
+
}
|
|
3174
|
+
async function executeBedrockAdapterCall(options, input) {
|
|
3175
|
+
return await executeProviderCallWithRetry(
|
|
3176
|
+
options.retry,
|
|
3177
|
+
async () => await requestBedrockInvokeModel(options, input)
|
|
3178
|
+
);
|
|
3179
|
+
}
|
|
3180
|
+
async function requestBedrockInvokeModel(options, input) {
|
|
3181
|
+
const credentials = resolveBedrockCredentials(options);
|
|
3182
|
+
const fetchImplementation = options.fetchImplementation ?? fetch;
|
|
3183
|
+
const requestBody = mergeProviderRequestExtraBody(
|
|
3184
|
+
buildBedrockInvokeModelRequestBody(input),
|
|
3185
|
+
options.extraBody,
|
|
3186
|
+
input.extraBody
|
|
3187
|
+
);
|
|
3188
|
+
const bodyText = JSON.stringify(requestBody);
|
|
3189
|
+
const url = new URL(buildBedrockInvokeModelUrl(options.region, options.modelId));
|
|
3190
|
+
const signedHeaders = await signAwsRequestWithSigV4({
|
|
3191
|
+
method: "POST",
|
|
3192
|
+
url,
|
|
3193
|
+
headers: { "content-type": "application/json" },
|
|
3194
|
+
bodyText,
|
|
3195
|
+
region: options.region,
|
|
3196
|
+
service: bedrockSigningServiceName,
|
|
3197
|
+
credentials
|
|
3198
|
+
});
|
|
3199
|
+
const startedAt = Date.now();
|
|
3200
|
+
const response = await executeAbortableProviderFetch(
|
|
3201
|
+
"Bedrock",
|
|
3202
|
+
async () => await fetchImplementation(url.toString(), {
|
|
3203
|
+
method: "POST",
|
|
3204
|
+
headers: signedHeaders,
|
|
3205
|
+
body: bodyText,
|
|
3206
|
+
signal: input.abortSignal
|
|
3207
|
+
})
|
|
3208
|
+
);
|
|
3209
|
+
const latencyMs = Date.now() - startedAt;
|
|
3210
|
+
if (!response.ok) {
|
|
3211
|
+
throw buildProviderStatusError("Bedrock", response.status);
|
|
3212
|
+
}
|
|
3213
|
+
const parseResult = AnthropicMessagesResponseSchema.safeParse(
|
|
3214
|
+
await response.json()
|
|
3215
|
+
);
|
|
3216
|
+
if (!parseResult.success) {
|
|
3217
|
+
throw buildProviderInvalidResponseError("Bedrock", "invoke model");
|
|
3218
|
+
}
|
|
3219
|
+
return mapAnthropicResponseToChatCompletionResult(parseResult.data, latencyMs);
|
|
3220
|
+
}
|
|
3221
|
+
function buildBedrockInvokeModelRequestBody(input) {
|
|
3222
|
+
const anthropicRequestBody = buildAnthropicRequestBody(
|
|
3223
|
+
{
|
|
3224
|
+
model: bedrockModelPlaceholder,
|
|
3225
|
+
defaultMaxOutputTokens: defaultBedrockMaxOutputTokens
|
|
3226
|
+
},
|
|
3227
|
+
input
|
|
3228
|
+
);
|
|
3229
|
+
const {
|
|
3230
|
+
model: ignoredModel,
|
|
3231
|
+
stream: ignoredStream,
|
|
3232
|
+
...requestBodyWithoutModel
|
|
3233
|
+
} = anthropicRequestBody;
|
|
3234
|
+
void ignoredModel;
|
|
3235
|
+
void ignoredStream;
|
|
3236
|
+
return {
|
|
3237
|
+
...requestBodyWithoutModel,
|
|
3238
|
+
anthropic_version: bedrockAnthropicVersion
|
|
3239
|
+
};
|
|
3240
|
+
}
|
|
3241
|
+
function buildBedrockStructuredOutputSystemMessage(schema) {
|
|
3242
|
+
return {
|
|
3243
|
+
role: "system",
|
|
3244
|
+
content: `Respond with a single JSON object that satisfies the following JSON Schema. Output only valid JSON with no surrounding text.
|
|
3245
|
+
|
|
3246
|
+
JSON Schema:
|
|
3247
|
+
${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
|
|
3248
|
+
};
|
|
1869
3249
|
}
|
|
1870
3250
|
export {
|
|
1871
3251
|
AnthropicMessagesResponseSchema,
|
|
1872
3252
|
AnthropicStreamEventSchema,
|
|
3253
|
+
AzureOpenAiChatCompletionResponseSchema,
|
|
3254
|
+
ChatMessageCacheControlSchema,
|
|
1873
3255
|
ChatMessageRoleSchema,
|
|
1874
3256
|
ChatMessageSchema,
|
|
1875
3257
|
ChatStopReasonSchema,
|
|
1876
3258
|
MessageContentBlockSchema,
|
|
1877
3259
|
ModelToolCallSchema,
|
|
1878
3260
|
anthropic,
|
|
3261
|
+
anthropicBatchJsonOnlySystemInstruction,
|
|
3262
|
+
anthropicBatchTraceWorkflowName,
|
|
3263
|
+
applyCacheControlToLastContentBlock,
|
|
3264
|
+
awsSigV4Algorithm,
|
|
3265
|
+
azureOpenai,
|
|
3266
|
+
bedrock,
|
|
3267
|
+
bedrockAnthropicVersion,
|
|
3268
|
+
buildAnthropicCacheControl,
|
|
1879
3269
|
buildAnthropicRequestBody,
|
|
3270
|
+
buildAnthropicStructuredOutputJsonSchema,
|
|
3271
|
+
buildAzureOpenAiChatCompletionsUrl,
|
|
3272
|
+
buildAzureOpenAiChatRequestBody,
|
|
3273
|
+
buildBedrockInvokeModelRequestBody,
|
|
3274
|
+
buildBedrockInvokeModelUrl,
|
|
3275
|
+
buildGeminiResponseSchema,
|
|
1880
3276
|
buildOpenAiRequestBody,
|
|
3277
|
+
buildOpenAiStructuredOutputJsonSchema,
|
|
1881
3278
|
buildProviderCanceledError,
|
|
1882
3279
|
calculateProviderRetryDelayMs,
|
|
3280
|
+
convertZodSchemaToJsonSchema,
|
|
1883
3281
|
createAnthropicBatchClient,
|
|
1884
3282
|
defaultAnthropicBaseUrl,
|
|
1885
3283
|
defaultAnthropicBatchPollIntervalMs,
|
|
1886
3284
|
defaultAnthropicBatchTimeoutMs,
|
|
1887
3285
|
defaultAnthropicMaxOutputTokens,
|
|
3286
|
+
defaultAzureOpenAiApiVersion,
|
|
3287
|
+
defaultBedrockMaxOutputTokens,
|
|
1888
3288
|
defaultGeminiBaseUrl,
|
|
1889
3289
|
defaultOpenAiBaseUrl,
|
|
1890
3290
|
defaultProviderRetryInitialDelayMs,
|
|
@@ -1895,6 +3295,7 @@ export {
|
|
|
1895
3295
|
executeProviderCallWithModelFallback,
|
|
1896
3296
|
executeProviderCallWithRetry,
|
|
1897
3297
|
extractAnthropicTextContent,
|
|
3298
|
+
extractAnthropicThinkingText,
|
|
1898
3299
|
extractAnthropicToolCalls,
|
|
1899
3300
|
extractMessageText,
|
|
1900
3301
|
gemini,
|
|
@@ -1903,17 +3304,32 @@ export {
|
|
|
1903
3304
|
joinSystemMessageText,
|
|
1904
3305
|
mapAnthropicResponseToChatCompletionResult,
|
|
1905
3306
|
mapAnthropicUsageToTokenUsage,
|
|
3307
|
+
mapAzureOpenAiResponseToChatCompletionResult,
|
|
3308
|
+
mapAzureOpenAiUsageToTokenUsage,
|
|
1906
3309
|
mapChatMessageToAnthropicRequestMessage,
|
|
1907
3310
|
mapChatMessageToOpenAiRequestMessage,
|
|
1908
3311
|
mapModelToolChoiceToAnthropicToolChoice,
|
|
1909
3312
|
mapModelToolChoiceToOpenAiToolChoice,
|
|
1910
3313
|
mapModelToolDefinitionToOpenAiTool,
|
|
1911
3314
|
mapModelToolDefinitionsToAnthropicTools,
|
|
3315
|
+
mergeProviderRequestExtraBody,
|
|
1912
3316
|
mockModel,
|
|
1913
3317
|
normalizeAnthropicStopReason,
|
|
3318
|
+
normalizeAzureOpenAiFinishReason,
|
|
3319
|
+
openAiStructuredOutputSchemaName,
|
|
1914
3320
|
openai,
|
|
3321
|
+
parseAnthropicBatchStructuredContent,
|
|
1915
3322
|
parseAnthropicStreamEvents,
|
|
1916
3323
|
parseAnthropicToolInputJson,
|
|
3324
|
+
parseAzureOpenAiStreamEvents,
|
|
3325
|
+
parseAzureOpenAiToolArgumentsJson,
|
|
3326
|
+
resolveAnthropicPromptCachingSettings,
|
|
3327
|
+
resolveBedrockCredentials,
|
|
1917
3328
|
resolveProviderApiKey,
|
|
1918
|
-
|
|
3329
|
+
sanitizeJsonSchemaForAnthropicStructuredOutput,
|
|
3330
|
+
sanitizeJsonSchemaForGeminiResponseSchema,
|
|
3331
|
+
sanitizeJsonSchemaForOpenAiStructuredOutput,
|
|
3332
|
+
signAwsRequestWithSigV4,
|
|
3333
|
+
streamAnthropicChatCompletion,
|
|
3334
|
+
streamAzureOpenAiChatCompletion
|
|
1919
3335
|
};
|