@assemble-dev/providers 0.2.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 +1400 -241
- package/dist/index.d.cts +159 -6
- package/dist/index.d.ts +159 -6
- package/dist/index.js +1360 -229
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -53,14 +53,222 @@ function extractMessageText(message) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// src/openai-adapter.ts
|
|
56
|
-
import { z as
|
|
57
|
-
import { AppErrorCode as
|
|
56
|
+
import { z as z4 } from "zod";
|
|
57
|
+
import { AppErrorCode as AppErrorCode8 } from "@assemble-dev/shared-types/errors";
|
|
58
58
|
import { JsonValueSchema as JsonValueSchema3 } from "@assemble-dev/shared-types/json";
|
|
59
|
-
import { AppError as
|
|
59
|
+
import { AppError as AppError9 } from "@assemble-dev/shared-utils/errors";
|
|
60
60
|
|
|
61
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";
|
|
62
67
|
import { AppErrorCode } from "@assemble-dev/shared-types/errors";
|
|
68
|
+
import { JsonObjectSchema } from "@assemble-dev/shared-types/json";
|
|
63
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
|
|
64
272
|
function buildOpenAiRequestBody(model, input) {
|
|
65
273
|
const requestBody = {
|
|
66
274
|
model,
|
|
@@ -72,7 +280,16 @@ function buildOpenAiRequestBody(model, input) {
|
|
|
72
280
|
if (input.maxOutputTokens !== void 0) {
|
|
73
281
|
requestBody.max_tokens = input.maxOutputTokens;
|
|
74
282
|
}
|
|
75
|
-
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) {
|
|
76
293
|
requestBody.response_format = { type: "json_object" };
|
|
77
294
|
}
|
|
78
295
|
if (input.tools !== void 0 && input.tools.length > 0) {
|
|
@@ -158,17 +375,17 @@ function mapMessageContentBlockToOpenAiContentPart(contentBlock) {
|
|
|
158
375
|
}
|
|
159
376
|
};
|
|
160
377
|
}
|
|
161
|
-
throw new
|
|
162
|
-
code:
|
|
378
|
+
throw new AppError2({
|
|
379
|
+
code: AppErrorCode2.ValidationFailed,
|
|
163
380
|
message: "OpenAI adapter does not support document content blocks in chat messages",
|
|
164
381
|
statusCode: 400
|
|
165
382
|
});
|
|
166
383
|
}
|
|
167
384
|
|
|
168
385
|
// src/parse-structured-json.ts
|
|
169
|
-
import { AppErrorCode as
|
|
386
|
+
import { AppErrorCode as AppErrorCode3 } from "@assemble-dev/shared-types/errors";
|
|
170
387
|
import { JsonValueSchema as JsonValueSchema2 } from "@assemble-dev/shared-types/json";
|
|
171
|
-
import { AppError as
|
|
388
|
+
import { AppError as AppError3 } from "@assemble-dev/shared-utils/errors";
|
|
172
389
|
var markdownCodeFencePattern = /^```[A-Za-z]*\s*\n?([\s\S]*?)\n?```$/;
|
|
173
390
|
function parseStructuredJsonText(text, schema) {
|
|
174
391
|
return schema.parse(parseJsonText(stripMarkdownCodeFences(text)));
|
|
@@ -185,8 +402,8 @@ function parseJsonText(text) {
|
|
|
185
402
|
try {
|
|
186
403
|
return JsonValueSchema2.parse(JSON.parse(text));
|
|
187
404
|
} catch (error) {
|
|
188
|
-
throw new
|
|
189
|
-
code:
|
|
405
|
+
throw new AppError3({
|
|
406
|
+
code: AppErrorCode3.ValidationFailed,
|
|
190
407
|
message: "Structured output was not valid JSON",
|
|
191
408
|
statusCode: 422,
|
|
192
409
|
cause: error instanceof Error ? error : void 0
|
|
@@ -195,9 +412,9 @@ function parseJsonText(text) {
|
|
|
195
412
|
}
|
|
196
413
|
|
|
197
414
|
// src/provider-api-key.ts
|
|
198
|
-
import { z as
|
|
199
|
-
import { AppErrorCode as
|
|
200
|
-
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";
|
|
201
418
|
import {
|
|
202
419
|
buildNamedApiKeyEnvVarName,
|
|
203
420
|
expectedProviderApiKeyNameFormat,
|
|
@@ -214,8 +431,8 @@ function resolveProviderApiKey(input) {
|
|
|
214
431
|
}
|
|
215
432
|
function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
216
433
|
if (!validateProviderApiKeyName(apiKeyName)) {
|
|
217
|
-
throw new
|
|
218
|
-
code:
|
|
434
|
+
throw new AppError4({
|
|
435
|
+
code: AppErrorCode4.BadRequest,
|
|
219
436
|
message: `Invalid ${input.providerLabel} apiKeyName "${apiKeyName}". Expected ${expectedProviderApiKeyNameFormat}.`,
|
|
220
437
|
statusCode: 400
|
|
221
438
|
});
|
|
@@ -223,8 +440,8 @@ function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
|
223
440
|
const envVarName = buildNamedApiKeyEnvVarName(input.baseEnvVarName, apiKeyName);
|
|
224
441
|
const envApiKey = readEnvironmentVariable(envVarName);
|
|
225
442
|
if (envApiKey === void 0 || envApiKey.length === 0) {
|
|
226
|
-
throw new
|
|
227
|
-
code:
|
|
443
|
+
throw new AppError4({
|
|
444
|
+
code: AppErrorCode4.Unauthorized,
|
|
228
445
|
message: `${input.providerLabel} API key named "${apiKeyName}" is missing. Set the ${envVarName} environment variable or pass apiKey to ${input.adapterFunctionName}.`,
|
|
229
446
|
statusCode: 401
|
|
230
447
|
});
|
|
@@ -234,8 +451,8 @@ function resolveNamedProviderApiKeyFromEnv(input, apiKeyName) {
|
|
|
234
451
|
function resolveBaseProviderApiKeyFromEnv(input) {
|
|
235
452
|
const envApiKey = readEnvironmentVariable(input.baseEnvVarName);
|
|
236
453
|
if (envApiKey === void 0 || envApiKey.length === 0) {
|
|
237
|
-
throw new
|
|
238
|
-
code:
|
|
454
|
+
throw new AppError4({
|
|
455
|
+
code: AppErrorCode4.Unauthorized,
|
|
239
456
|
message: `${input.providerLabel} API key is missing. Pass apiKey to ${input.adapterFunctionName} or set the ${input.baseEnvVarName} environment variable.`,
|
|
240
457
|
statusCode: 401
|
|
241
458
|
});
|
|
@@ -243,19 +460,19 @@ function resolveBaseProviderApiKeyFromEnv(input) {
|
|
|
243
460
|
return envApiKey;
|
|
244
461
|
}
|
|
245
462
|
function readEnvironmentVariable(envVarName) {
|
|
246
|
-
const parsedEnv =
|
|
463
|
+
const parsedEnv = z3.object({ [envVarName]: z3.string().optional() }).parse(process.env);
|
|
247
464
|
return parsedEnv[envVarName];
|
|
248
465
|
}
|
|
249
466
|
|
|
250
467
|
// src/provider-cancellation.ts
|
|
251
|
-
import { AppErrorCode as
|
|
252
|
-
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";
|
|
253
470
|
function isAbortError(error) {
|
|
254
471
|
return error.name === "AbortError";
|
|
255
472
|
}
|
|
256
473
|
function buildProviderCanceledError(providerLabel) {
|
|
257
|
-
return new
|
|
258
|
-
code:
|
|
474
|
+
return new AppError5({
|
|
475
|
+
code: AppErrorCode5.Canceled,
|
|
259
476
|
message: `${providerLabel} request was canceled`,
|
|
260
477
|
statusCode: 499
|
|
261
478
|
});
|
|
@@ -281,18 +498,18 @@ function mergeProviderRequestExtraBody(requestBody, adapterExtraBody, requestExt
|
|
|
281
498
|
}
|
|
282
499
|
|
|
283
500
|
// src/provider-fallback.ts
|
|
284
|
-
import { AppError as
|
|
501
|
+
import { AppError as AppError7 } from "@assemble-dev/shared-utils/errors";
|
|
285
502
|
|
|
286
503
|
// src/provider-retry.ts
|
|
287
|
-
import { AppErrorCode as
|
|
288
|
-
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";
|
|
289
506
|
var defaultProviderRetryMaxAttempts = 3;
|
|
290
507
|
var defaultProviderRetryInitialDelayMs = 500;
|
|
291
508
|
var defaultProviderRetryMaxDelayMs = 8e3;
|
|
292
509
|
var retryableAppErrorCodes = /* @__PURE__ */ new Set([
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
510
|
+
AppErrorCode6.RateLimited,
|
|
511
|
+
AppErrorCode6.ServiceUnavailable,
|
|
512
|
+
AppErrorCode6.InternalServerError
|
|
296
513
|
]);
|
|
297
514
|
function isRetryableProviderAppError(appError) {
|
|
298
515
|
return retryableAppErrorCodes.has(appError.code);
|
|
@@ -307,7 +524,7 @@ async function executeProviderCallWithRetry(retryOptions, executeCall, waitFor)
|
|
|
307
524
|
try {
|
|
308
525
|
return await executeCall();
|
|
309
526
|
} catch (error) {
|
|
310
|
-
if (error instanceof
|
|
527
|
+
if (error instanceof AppError6 && !isRetryableProviderAppError(error)) {
|
|
311
528
|
throw error;
|
|
312
529
|
}
|
|
313
530
|
if (error instanceof Error && isAbortError(error)) {
|
|
@@ -341,7 +558,7 @@ async function executeProviderCallWithModelFallback(fallbackModel, executePrimar
|
|
|
341
558
|
if (fallbackModel === void 0) {
|
|
342
559
|
throw error;
|
|
343
560
|
}
|
|
344
|
-
if (error instanceof
|
|
561
|
+
if (error instanceof AppError7 && !isRetryableProviderAppError(error)) {
|
|
345
562
|
throw error;
|
|
346
563
|
}
|
|
347
564
|
if (error instanceof Error && isAbortError(error)) {
|
|
@@ -352,11 +569,11 @@ async function executeProviderCallWithModelFallback(fallbackModel, executePrimar
|
|
|
352
569
|
}
|
|
353
570
|
|
|
354
571
|
// src/provider-status-error.ts
|
|
355
|
-
import { AppErrorCode as
|
|
356
|
-
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";
|
|
357
574
|
function buildProviderStatusError(providerLabel, providerStatus) {
|
|
358
575
|
const code = mapProviderStatusToAppErrorCode(providerStatus);
|
|
359
|
-
return new
|
|
576
|
+
return new AppError8({
|
|
360
577
|
code,
|
|
361
578
|
message: `${providerLabel} request failed with status ${String(providerStatus)}`,
|
|
362
579
|
statusCode: mapAppErrorCodeToStatusCode(code),
|
|
@@ -364,44 +581,44 @@ function buildProviderStatusError(providerLabel, providerStatus) {
|
|
|
364
581
|
});
|
|
365
582
|
}
|
|
366
583
|
function buildProviderInvalidResponseError(providerLabel, expectedShapeDescription) {
|
|
367
|
-
return new
|
|
368
|
-
code:
|
|
584
|
+
return new AppError8({
|
|
585
|
+
code: AppErrorCode7.ValidationFailed,
|
|
369
586
|
message: `${providerLabel} response did not match the expected ${expectedShapeDescription} shape`,
|
|
370
587
|
statusCode: 502
|
|
371
588
|
});
|
|
372
589
|
}
|
|
373
590
|
function mapProviderStatusToAppErrorCode(providerStatus) {
|
|
374
591
|
if (providerStatus === 400) {
|
|
375
|
-
return
|
|
592
|
+
return AppErrorCode7.BadRequest;
|
|
376
593
|
}
|
|
377
594
|
if (providerStatus === 401) {
|
|
378
|
-
return
|
|
595
|
+
return AppErrorCode7.Unauthorized;
|
|
379
596
|
}
|
|
380
597
|
if (providerStatus === 403) {
|
|
381
|
-
return
|
|
598
|
+
return AppErrorCode7.Forbidden;
|
|
382
599
|
}
|
|
383
600
|
if (providerStatus === 404) {
|
|
384
|
-
return
|
|
601
|
+
return AppErrorCode7.NotFound;
|
|
385
602
|
}
|
|
386
603
|
if (providerStatus === 429) {
|
|
387
|
-
return
|
|
604
|
+
return AppErrorCode7.RateLimited;
|
|
388
605
|
}
|
|
389
|
-
return
|
|
606
|
+
return AppErrorCode7.InternalServerError;
|
|
390
607
|
}
|
|
391
608
|
function mapAppErrorCodeToStatusCode(code) {
|
|
392
|
-
if (code ===
|
|
609
|
+
if (code === AppErrorCode7.BadRequest) {
|
|
393
610
|
return 400;
|
|
394
611
|
}
|
|
395
|
-
if (code ===
|
|
612
|
+
if (code === AppErrorCode7.Unauthorized) {
|
|
396
613
|
return 401;
|
|
397
614
|
}
|
|
398
|
-
if (code ===
|
|
615
|
+
if (code === AppErrorCode7.Forbidden) {
|
|
399
616
|
return 403;
|
|
400
617
|
}
|
|
401
|
-
if (code ===
|
|
618
|
+
if (code === AppErrorCode7.NotFound) {
|
|
402
619
|
return 404;
|
|
403
620
|
}
|
|
404
|
-
if (code ===
|
|
621
|
+
if (code === AppErrorCode7.RateLimited) {
|
|
405
622
|
return 429;
|
|
406
623
|
}
|
|
407
624
|
return 500;
|
|
@@ -409,33 +626,29 @@ function mapAppErrorCodeToStatusCode(code) {
|
|
|
409
626
|
|
|
410
627
|
// src/openai-adapter.ts
|
|
411
628
|
var defaultOpenAiBaseUrl = "https://api.openai.com/v1";
|
|
412
|
-
var OpenAiResponseToolCallSchema =
|
|
413
|
-
id:
|
|
414
|
-
function:
|
|
415
|
-
name:
|
|
416
|
-
arguments:
|
|
629
|
+
var OpenAiResponseToolCallSchema = z4.object({
|
|
630
|
+
id: z4.string(),
|
|
631
|
+
function: z4.object({
|
|
632
|
+
name: z4.string(),
|
|
633
|
+
arguments: z4.string()
|
|
417
634
|
})
|
|
418
635
|
});
|
|
419
|
-
var OpenAiChatCompletionResponseSchema =
|
|
420
|
-
choices:
|
|
421
|
-
|
|
422
|
-
message:
|
|
423
|
-
content:
|
|
424
|
-
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()
|
|
425
642
|
}),
|
|
426
|
-
finish_reason:
|
|
643
|
+
finish_reason: z4.string().nullable().optional()
|
|
427
644
|
})
|
|
428
645
|
).min(1),
|
|
429
|
-
usage:
|
|
430
|
-
prompt_tokens:
|
|
431
|
-
completion_tokens:
|
|
432
|
-
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()
|
|
433
650
|
})
|
|
434
651
|
});
|
|
435
|
-
var structuredOutputSystemMessage = {
|
|
436
|
-
role: "system",
|
|
437
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
438
|
-
};
|
|
439
652
|
function openai(options) {
|
|
440
653
|
return {
|
|
441
654
|
name: `openai:${options.model}`,
|
|
@@ -444,7 +657,6 @@ function openai(options) {
|
|
|
444
657
|
messages: request.messages,
|
|
445
658
|
temperature: request.temperature ?? options.temperature,
|
|
446
659
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
447
|
-
requireJsonObjectResponse: false,
|
|
448
660
|
tools: request.tools,
|
|
449
661
|
toolChoice: request.toolChoice,
|
|
450
662
|
abortSignal: request.abortSignal,
|
|
@@ -453,10 +665,12 @@ function openai(options) {
|
|
|
453
665
|
},
|
|
454
666
|
async generateStructuredOutput(request) {
|
|
455
667
|
const completion = await executeOpenAiAdapterCall(options, {
|
|
456
|
-
messages:
|
|
668
|
+
messages: request.messages,
|
|
457
669
|
temperature: request.temperature ?? options.temperature,
|
|
458
670
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
459
|
-
|
|
671
|
+
structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
|
|
672
|
+
request.schema
|
|
673
|
+
),
|
|
460
674
|
abortSignal: request.abortSignal
|
|
461
675
|
});
|
|
462
676
|
return {
|
|
@@ -551,8 +765,8 @@ function parseOpenAiToolArgumentsJson(argumentsJsonText) {
|
|
|
551
765
|
try {
|
|
552
766
|
return JsonValueSchema3.parse(JSON.parse(argumentsJsonText));
|
|
553
767
|
} catch (error) {
|
|
554
|
-
throw new
|
|
555
|
-
code:
|
|
768
|
+
throw new AppError9({
|
|
769
|
+
code: AppErrorCode8.ValidationFailed,
|
|
556
770
|
message: "OpenAI tool call arguments were not valid JSON",
|
|
557
771
|
statusCode: 502,
|
|
558
772
|
cause: error instanceof Error ? error : void 0
|
|
@@ -574,8 +788,8 @@ function extractOpenAiMessageContent(response, allowEmptyContent) {
|
|
|
574
788
|
if (allowEmptyContent) {
|
|
575
789
|
return "";
|
|
576
790
|
}
|
|
577
|
-
throw new
|
|
578
|
-
code:
|
|
791
|
+
throw new AppError9({
|
|
792
|
+
code: AppErrorCode8.ValidationFailed,
|
|
579
793
|
message: "OpenAI response contained no message content",
|
|
580
794
|
statusCode: 502
|
|
581
795
|
});
|
|
@@ -639,6 +853,11 @@ function buildAnthropicRequestBody(settings, input) {
|
|
|
639
853
|
budget_tokens: settings.thinking.budgetTokens
|
|
640
854
|
};
|
|
641
855
|
}
|
|
856
|
+
if (input.structuredOutputJsonSchema !== void 0) {
|
|
857
|
+
requestBody.output_config = {
|
|
858
|
+
format: { type: "json_schema", schema: input.structuredOutputJsonSchema }
|
|
859
|
+
};
|
|
860
|
+
}
|
|
642
861
|
return requestBody;
|
|
643
862
|
}
|
|
644
863
|
function applyAnthropicSystemField(requestBody, settings, input) {
|
|
@@ -795,44 +1014,45 @@ function mapMessageContentBlockToAnthropic(contentBlock) {
|
|
|
795
1014
|
}
|
|
796
1015
|
|
|
797
1016
|
// src/anthropic-response-parsing.ts
|
|
798
|
-
import { z as
|
|
799
|
-
import { AppErrorCode as
|
|
1017
|
+
import { z as z5 } from "zod";
|
|
1018
|
+
import { AppErrorCode as AppErrorCode9 } from "@assemble-dev/shared-types/errors";
|
|
800
1019
|
import { JsonValueSchema as JsonValueSchema4 } from "@assemble-dev/shared-types/json";
|
|
801
|
-
import { AppError as
|
|
802
|
-
var AnthropicUsageSchema =
|
|
803
|
-
input_tokens:
|
|
804
|
-
output_tokens:
|
|
805
|
-
cache_creation_input_tokens:
|
|
806
|
-
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()
|
|
807
1026
|
});
|
|
808
|
-
var AnthropicContentBlockSchema =
|
|
809
|
-
|
|
810
|
-
type:
|
|
811
|
-
text:
|
|
1027
|
+
var AnthropicContentBlockSchema = z5.discriminatedUnion("type", [
|
|
1028
|
+
z5.object({
|
|
1029
|
+
type: z5.literal("text"),
|
|
1030
|
+
text: z5.string()
|
|
812
1031
|
}),
|
|
813
|
-
|
|
814
|
-
type:
|
|
815
|
-
id:
|
|
816
|
-
name:
|
|
1032
|
+
z5.object({
|
|
1033
|
+
type: z5.literal("tool_use"),
|
|
1034
|
+
id: z5.string(),
|
|
1035
|
+
name: z5.string(),
|
|
817
1036
|
input: JsonValueSchema4
|
|
818
1037
|
}),
|
|
819
|
-
|
|
820
|
-
type:
|
|
821
|
-
thinking:
|
|
822
|
-
signature:
|
|
1038
|
+
z5.object({
|
|
1039
|
+
type: z5.literal("thinking"),
|
|
1040
|
+
thinking: z5.string().optional(),
|
|
1041
|
+
signature: z5.string().optional()
|
|
823
1042
|
}),
|
|
824
|
-
|
|
825
|
-
type:
|
|
826
|
-
data:
|
|
1043
|
+
z5.object({
|
|
1044
|
+
type: z5.literal("redacted_thinking"),
|
|
1045
|
+
data: z5.string().optional()
|
|
827
1046
|
})
|
|
828
1047
|
]);
|
|
829
|
-
var AnthropicMessagesResponseSchema =
|
|
830
|
-
content:
|
|
831
|
-
stop_reason:
|
|
1048
|
+
var AnthropicMessagesResponseSchema = z5.object({
|
|
1049
|
+
content: z5.array(AnthropicContentBlockSchema),
|
|
1050
|
+
stop_reason: z5.string().nullable().optional(),
|
|
832
1051
|
usage: AnthropicUsageSchema
|
|
833
1052
|
});
|
|
834
1053
|
function mapAnthropicResponseToChatCompletionResult(response, latencyMs) {
|
|
835
1054
|
const toolCalls = extractAnthropicToolCalls(response);
|
|
1055
|
+
const thinkingText = extractAnthropicThinkingText(response);
|
|
836
1056
|
const result = {
|
|
837
1057
|
content: extractAnthropicTextContent(response, toolCalls.length > 0),
|
|
838
1058
|
tokenUsage: mapAnthropicUsageToTokenUsage(response.usage),
|
|
@@ -841,6 +1061,9 @@ function mapAnthropicResponseToChatCompletionResult(response, latencyMs) {
|
|
|
841
1061
|
if (toolCalls.length > 0) {
|
|
842
1062
|
result.toolCalls = toolCalls;
|
|
843
1063
|
}
|
|
1064
|
+
if (thinkingText.length > 0) {
|
|
1065
|
+
result.thinkingText = thinkingText;
|
|
1066
|
+
}
|
|
844
1067
|
if (response.stop_reason !== null && response.stop_reason !== void 0) {
|
|
845
1068
|
result.stopReason = normalizeAnthropicStopReason(response.stop_reason);
|
|
846
1069
|
}
|
|
@@ -853,13 +1076,16 @@ function extractAnthropicToolCalls(response) {
|
|
|
853
1076
|
input: contentBlock.input
|
|
854
1077
|
}));
|
|
855
1078
|
}
|
|
1079
|
+
function extractAnthropicThinkingText(response) {
|
|
1080
|
+
return response.content.filter((contentBlock) => contentBlock.type === "thinking").map((contentBlock) => contentBlock.thinking ?? "").join("");
|
|
1081
|
+
}
|
|
856
1082
|
function extractAnthropicTextContent(response, allowEmptyText) {
|
|
857
1083
|
const textBlocks = response.content.filter(
|
|
858
1084
|
(contentBlock) => contentBlock.type === "text"
|
|
859
1085
|
);
|
|
860
1086
|
if (textBlocks.length === 0 && !allowEmptyText) {
|
|
861
|
-
throw new
|
|
862
|
-
code:
|
|
1087
|
+
throw new AppError10({
|
|
1088
|
+
code: AppErrorCode9.ValidationFailed,
|
|
863
1089
|
message: "Anthropic response contained no text content",
|
|
864
1090
|
statusCode: 502
|
|
865
1091
|
});
|
|
@@ -896,58 +1122,68 @@ function mapAnthropicUsageToTokenUsage(usage) {
|
|
|
896
1122
|
}
|
|
897
1123
|
|
|
898
1124
|
// src/anthropic-stream.ts
|
|
899
|
-
import { AppErrorCode as
|
|
1125
|
+
import { AppErrorCode as AppErrorCode10 } from "@assemble-dev/shared-types/errors";
|
|
900
1126
|
import { JsonValueSchema as JsonValueSchema5 } from "@assemble-dev/shared-types/json";
|
|
901
|
-
import { AppError as
|
|
1127
|
+
import { AppError as AppError11 } from "@assemble-dev/shared-utils/errors";
|
|
902
1128
|
|
|
903
1129
|
// src/anthropic-stream-events.ts
|
|
904
|
-
import { z as
|
|
905
|
-
var AnthropicStreamEventSchema =
|
|
906
|
-
|
|
907
|
-
type:
|
|
908
|
-
message:
|
|
909
|
-
usage:
|
|
910
|
-
input_tokens:
|
|
911
|
-
output_tokens:
|
|
912
|
-
cache_creation_input_tokens:
|
|
913
|
-
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()
|
|
914
1140
|
})
|
|
915
1141
|
})
|
|
916
1142
|
}),
|
|
917
|
-
|
|
918
|
-
type:
|
|
919
|
-
index:
|
|
920
|
-
content_block:
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
type:
|
|
924
|
-
|
|
925
|
-
|
|
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()
|
|
926
1160
|
})
|
|
927
1161
|
])
|
|
928
1162
|
}),
|
|
929
|
-
|
|
930
|
-
type:
|
|
931
|
-
index:
|
|
932
|
-
delta:
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
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()
|
|
937
1173
|
})
|
|
938
1174
|
])
|
|
939
1175
|
}),
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
type:
|
|
943
|
-
delta:
|
|
944
|
-
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()
|
|
945
1181
|
}),
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
type:
|
|
950
|
-
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() })
|
|
951
1187
|
})
|
|
952
1188
|
]);
|
|
953
1189
|
|
|
@@ -1033,6 +1269,7 @@ async function* readServerSentEventDataLines(body, abortSignal) {
|
|
|
1033
1269
|
function createAnthropicStreamAccumulator() {
|
|
1034
1270
|
return {
|
|
1035
1271
|
contentText: "",
|
|
1272
|
+
thinkingText: "",
|
|
1036
1273
|
toolCallsByBlockIndex: /* @__PURE__ */ new Map(),
|
|
1037
1274
|
inputTokens: 0,
|
|
1038
1275
|
outputTokens: 0
|
|
@@ -1040,8 +1277,8 @@ function createAnthropicStreamAccumulator() {
|
|
|
1040
1277
|
}
|
|
1041
1278
|
function* applyAnthropicStreamEvent(event, accumulator) {
|
|
1042
1279
|
if (event.type === "error") {
|
|
1043
|
-
throw new
|
|
1044
|
-
code:
|
|
1280
|
+
throw new AppError11({
|
|
1281
|
+
code: AppErrorCode10.InternalServerError,
|
|
1045
1282
|
message: `Anthropic stream reported an error: ${event.error.message}`,
|
|
1046
1283
|
statusCode: 502
|
|
1047
1284
|
});
|
|
@@ -1077,6 +1314,17 @@ function* applyAnthropicContentBlockStart(event, accumulator) {
|
|
|
1077
1314
|
}
|
|
1078
1315
|
return;
|
|
1079
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
|
+
}
|
|
1080
1328
|
accumulator.toolCallsByBlockIndex.set(event.index, {
|
|
1081
1329
|
id: event.content_block.id,
|
|
1082
1330
|
toolName: event.content_block.name,
|
|
@@ -1094,6 +1342,14 @@ function* applyAnthropicContentBlockDelta(event, accumulator) {
|
|
|
1094
1342
|
yield { type: "text_delta", text: event.delta.text };
|
|
1095
1343
|
return;
|
|
1096
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
|
+
}
|
|
1097
1353
|
const toolCallAccumulation = accumulator.toolCallsByBlockIndex.get(
|
|
1098
1354
|
event.index
|
|
1099
1355
|
);
|
|
@@ -1140,6 +1396,9 @@ function buildAnthropicStreamResult(accumulator, latencyMs) {
|
|
|
1140
1396
|
if (accumulator.stopReason !== void 0) {
|
|
1141
1397
|
result.stopReason = accumulator.stopReason;
|
|
1142
1398
|
}
|
|
1399
|
+
if (accumulator.thinkingText.length > 0) {
|
|
1400
|
+
result.thinkingText = accumulator.thinkingText;
|
|
1401
|
+
}
|
|
1143
1402
|
return result;
|
|
1144
1403
|
}
|
|
1145
1404
|
function mapAccumulatedToolCallToModelToolCall(toolCallAccumulation) {
|
|
@@ -1156,8 +1415,8 @@ function parseAnthropicToolInputJson(inputJsonText) {
|
|
|
1156
1415
|
try {
|
|
1157
1416
|
return JsonValueSchema5.parse(JSON.parse(inputJsonText));
|
|
1158
1417
|
} catch (error) {
|
|
1159
|
-
throw new
|
|
1160
|
-
code:
|
|
1418
|
+
throw new AppError11({
|
|
1419
|
+
code: AppErrorCode10.ValidationFailed,
|
|
1161
1420
|
message: "Anthropic tool input was not valid JSON",
|
|
1162
1421
|
statusCode: 502,
|
|
1163
1422
|
cause: error instanceof Error ? error : void 0
|
|
@@ -1169,10 +1428,6 @@ function parseAnthropicToolInputJson(inputJsonText) {
|
|
|
1169
1428
|
var defaultAnthropicBaseUrl = "https://api.anthropic.com";
|
|
1170
1429
|
var defaultAnthropicMaxOutputTokens = 1024;
|
|
1171
1430
|
var anthropicApiVersion = "2023-06-01";
|
|
1172
|
-
var structuredOutputSystemMessage2 = {
|
|
1173
|
-
role: "system",
|
|
1174
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
1175
|
-
};
|
|
1176
1431
|
function anthropic(options) {
|
|
1177
1432
|
return {
|
|
1178
1433
|
name: `anthropic:${options.model}`,
|
|
@@ -1184,9 +1439,12 @@ function anthropic(options) {
|
|
|
1184
1439
|
},
|
|
1185
1440
|
async generateStructuredOutput(request) {
|
|
1186
1441
|
const completion = await executeAnthropicAdapterCall(options, {
|
|
1187
|
-
messages:
|
|
1442
|
+
messages: request.messages,
|
|
1188
1443
|
temperature: request.temperature ?? options.temperature,
|
|
1189
1444
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1445
|
+
structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
|
|
1446
|
+
request.schema
|
|
1447
|
+
),
|
|
1190
1448
|
abortSignal: request.abortSignal
|
|
1191
1449
|
});
|
|
1192
1450
|
return {
|
|
@@ -1302,28 +1560,116 @@ function resolveAnthropicFetch(options) {
|
|
|
1302
1560
|
}
|
|
1303
1561
|
|
|
1304
1562
|
// src/gemini-adapter.ts
|
|
1305
|
-
import { z as
|
|
1306
|
-
import { AppErrorCode as
|
|
1307
|
-
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
|
|
1308
1658
|
var defaultGeminiBaseUrl = "https://generativelanguage.googleapis.com";
|
|
1309
|
-
var GeminiGenerateContentResponseSchema =
|
|
1310
|
-
candidates:
|
|
1311
|
-
|
|
1312
|
-
content:
|
|
1313
|
-
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() }))
|
|
1314
1664
|
})
|
|
1315
1665
|
})
|
|
1316
1666
|
).optional(),
|
|
1317
|
-
usageMetadata:
|
|
1318
|
-
promptTokenCount:
|
|
1319
|
-
candidatesTokenCount:
|
|
1320
|
-
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()
|
|
1321
1671
|
}).optional()
|
|
1322
1672
|
});
|
|
1323
|
-
var structuredOutputSystemMessage3 = {
|
|
1324
|
-
role: "system",
|
|
1325
|
-
content: "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text."
|
|
1326
|
-
};
|
|
1327
1673
|
function gemini(options) {
|
|
1328
1674
|
return {
|
|
1329
1675
|
name: `gemini:${options.model}`,
|
|
@@ -1333,17 +1679,16 @@ function gemini(options) {
|
|
|
1333
1679
|
messages: request.messages,
|
|
1334
1680
|
temperature: request.temperature ?? options.temperature,
|
|
1335
1681
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1336
|
-
requireJsonResponse: false,
|
|
1337
1682
|
abortSignal: request.abortSignal,
|
|
1338
1683
|
extraBody: request.extraBody
|
|
1339
1684
|
});
|
|
1340
1685
|
},
|
|
1341
1686
|
async generateStructuredOutput(request) {
|
|
1342
1687
|
const completion = await executeGeminiAdapterCall(options, {
|
|
1343
|
-
messages:
|
|
1688
|
+
messages: request.messages,
|
|
1344
1689
|
temperature: request.temperature ?? options.temperature,
|
|
1345
1690
|
maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
|
|
1346
|
-
|
|
1691
|
+
responseSchema: buildGeminiResponseSchema(request.schema),
|
|
1347
1692
|
abortSignal: request.abortSignal
|
|
1348
1693
|
});
|
|
1349
1694
|
return {
|
|
@@ -1430,8 +1775,8 @@ function ensureGeminiRequestHasNoTools(request) {
|
|
|
1430
1775
|
if (request.tools === void 0 || request.tools.length === 0) {
|
|
1431
1776
|
return;
|
|
1432
1777
|
}
|
|
1433
|
-
throw new
|
|
1434
|
-
code:
|
|
1778
|
+
throw new AppError12({
|
|
1779
|
+
code: AppErrorCode11.ValidationFailed,
|
|
1435
1780
|
message: "gemini adapter does not support model-driven tool use; remove tools from the request or use another adapter",
|
|
1436
1781
|
statusCode: 400
|
|
1437
1782
|
});
|
|
@@ -1468,8 +1813,9 @@ function buildGeminiGenerationConfig(input) {
|
|
|
1468
1813
|
if (input.maxOutputTokens !== void 0) {
|
|
1469
1814
|
generationConfig.maxOutputTokens = input.maxOutputTokens;
|
|
1470
1815
|
}
|
|
1471
|
-
if (input.
|
|
1816
|
+
if (input.responseSchema !== void 0) {
|
|
1472
1817
|
generationConfig.responseMimeType = "application/json";
|
|
1818
|
+
generationConfig.responseSchema = input.responseSchema;
|
|
1473
1819
|
}
|
|
1474
1820
|
if (Object.keys(generationConfig).length === 0) {
|
|
1475
1821
|
return void 0;
|
|
@@ -1479,8 +1825,8 @@ function buildGeminiGenerationConfig(input) {
|
|
|
1479
1825
|
function extractGeminiCandidateText(response) {
|
|
1480
1826
|
const firstCandidate = response.candidates?.[0];
|
|
1481
1827
|
if (firstCandidate === void 0) {
|
|
1482
|
-
throw new
|
|
1483
|
-
code:
|
|
1828
|
+
throw new AppError12({
|
|
1829
|
+
code: AppErrorCode11.ValidationFailed,
|
|
1484
1830
|
message: "Gemini response contained no candidates",
|
|
1485
1831
|
statusCode: 502
|
|
1486
1832
|
});
|
|
@@ -1499,8 +1845,8 @@ function mapGeminiUsageToTokenUsage(response) {
|
|
|
1499
1845
|
}
|
|
1500
1846
|
|
|
1501
1847
|
// src/mock-adapter.ts
|
|
1502
|
-
import { AppErrorCode as
|
|
1503
|
-
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";
|
|
1504
1850
|
var mockStreamChunkCount = 3;
|
|
1505
1851
|
function mockModel(options = {}) {
|
|
1506
1852
|
const remainingScriptedResponses = [...options.scriptedResponses ?? []];
|
|
@@ -1580,8 +1926,8 @@ function resolveNextMockResponseText(request, remainingScriptedResponses, respon
|
|
|
1580
1926
|
if (respond !== void 0) {
|
|
1581
1927
|
return respond(request);
|
|
1582
1928
|
}
|
|
1583
|
-
throw new
|
|
1584
|
-
code:
|
|
1929
|
+
throw new AppError13({
|
|
1930
|
+
code: AppErrorCode12.BadRequest,
|
|
1585
1931
|
message: "Mock model has no scripted responses left and no respond function was provided",
|
|
1586
1932
|
statusCode: 400
|
|
1587
1933
|
});
|
|
@@ -1669,14 +2015,14 @@ function calculateModelCallCostUsd(pricing, tokenUsage) {
|
|
|
1669
2015
|
}
|
|
1670
2016
|
|
|
1671
2017
|
// src/anthropic-batch.ts
|
|
1672
|
-
import { AppErrorCode as
|
|
2018
|
+
import { AppErrorCode as AppErrorCode14 } from "@assemble-dev/shared-types/errors";
|
|
1673
2019
|
import { JsonValueSchema as JsonValueSchema7 } from "@assemble-dev/shared-types/json";
|
|
1674
|
-
import { AppError as
|
|
2020
|
+
import { AppError as AppError15 } from "@assemble-dev/shared-utils/errors";
|
|
1675
2021
|
|
|
1676
2022
|
// src/anthropic-batch-structured.ts
|
|
1677
|
-
import { AppErrorCode as
|
|
2023
|
+
import { AppErrorCode as AppErrorCode13 } from "@assemble-dev/shared-types/errors";
|
|
1678
2024
|
import { JsonValueSchema as JsonValueSchema6 } from "@assemble-dev/shared-types/json";
|
|
1679
|
-
import { AppError as
|
|
2025
|
+
import { AppError as AppError14 } from "@assemble-dev/shared-utils/errors";
|
|
1680
2026
|
var anthropicBatchJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
|
|
1681
2027
|
function parseAnthropicBatchStructuredContent(content, schema) {
|
|
1682
2028
|
const jsonValue = parseStructuredJsonText(
|
|
@@ -1685,8 +2031,8 @@ function parseAnthropicBatchStructuredContent(content, schema) {
|
|
|
1685
2031
|
);
|
|
1686
2032
|
const validationResult = schema.safeParse(jsonValue);
|
|
1687
2033
|
if (!validationResult.success) {
|
|
1688
|
-
throw new
|
|
1689
|
-
code:
|
|
2034
|
+
throw new AppError14({
|
|
2035
|
+
code: AppErrorCode13.ValidationFailed,
|
|
1690
2036
|
message: "Anthropic batch structured output did not match the expected schema",
|
|
1691
2037
|
statusCode: 422,
|
|
1692
2038
|
details: { validationErrorMessage: validationResult.error.message }
|
|
@@ -1703,11 +2049,25 @@ function mapBatchMessageRequestToApiBatchRequest(request) {
|
|
|
1703
2049
|
messages: request.messages.filter((message) => message.role !== "system").map(mapChatMessageToBatchApiRequestMessage)
|
|
1704
2050
|
};
|
|
1705
2051
|
applyBatchSystemField(params, request);
|
|
2052
|
+
applyBatchStructuredOutputConfig(params, request);
|
|
1706
2053
|
if (request.temperature !== void 0) {
|
|
1707
2054
|
params.temperature = request.temperature;
|
|
1708
2055
|
}
|
|
1709
2056
|
return { custom_id: request.customId, params };
|
|
1710
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
|
+
}
|
|
1711
2071
|
function applyBatchSystemField(params, request) {
|
|
1712
2072
|
const systemText = buildBatchSystemText(request);
|
|
1713
2073
|
if (systemText.length === 0) {
|
|
@@ -1723,7 +2083,7 @@ function applyBatchSystemField(params, request) {
|
|
|
1723
2083
|
}
|
|
1724
2084
|
function buildBatchSystemText(request) {
|
|
1725
2085
|
const joinedSystemText = joinBatchSystemMessageText(request.messages);
|
|
1726
|
-
if (request.requireJsonResponse !== true) {
|
|
2086
|
+
if (request.requireJsonResponse !== true || request.structuredOutputJsonSchema !== void 0) {
|
|
1727
2087
|
return joinedSystemText;
|
|
1728
2088
|
}
|
|
1729
2089
|
if (joinedSystemText.length === 0) {
|
|
@@ -1877,55 +2237,55 @@ function buildBatchResultEvent(eventBase, batchId, result, modelName) {
|
|
|
1877
2237
|
}
|
|
1878
2238
|
|
|
1879
2239
|
// src/anthropic-batch-types.ts
|
|
1880
|
-
import { z as
|
|
1881
|
-
var AnthropicMessageBatchResponseSchema =
|
|
1882
|
-
id:
|
|
1883
|
-
processing_status:
|
|
1884
|
-
request_counts:
|
|
1885
|
-
processing:
|
|
1886
|
-
succeeded:
|
|
1887
|
-
errored:
|
|
1888
|
-
canceled:
|
|
1889
|
-
expired:
|
|
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()
|
|
1890
2250
|
}),
|
|
1891
|
-
results_url:
|
|
1892
|
-
created_at:
|
|
1893
|
-
ended_at:
|
|
2251
|
+
results_url: z8.string().nullable(),
|
|
2252
|
+
created_at: z8.string(),
|
|
2253
|
+
ended_at: z8.string().nullable()
|
|
1894
2254
|
});
|
|
1895
|
-
var AnthropicBatchContentBlockSchema =
|
|
1896
|
-
|
|
1897
|
-
type:
|
|
1898
|
-
text:
|
|
2255
|
+
var AnthropicBatchContentBlockSchema = z8.discriminatedUnion("type", [
|
|
2256
|
+
z8.object({
|
|
2257
|
+
type: z8.literal("text"),
|
|
2258
|
+
text: z8.string()
|
|
1899
2259
|
}),
|
|
1900
|
-
|
|
1901
|
-
type:
|
|
1902
|
-
thinking:
|
|
1903
|
-
signature:
|
|
2260
|
+
z8.object({
|
|
2261
|
+
type: z8.literal("thinking"),
|
|
2262
|
+
thinking: z8.string().optional(),
|
|
2263
|
+
signature: z8.string().optional()
|
|
1904
2264
|
}),
|
|
1905
|
-
|
|
1906
|
-
type:
|
|
1907
|
-
data:
|
|
2265
|
+
z8.object({
|
|
2266
|
+
type: z8.literal("redacted_thinking"),
|
|
2267
|
+
data: z8.string().optional()
|
|
1908
2268
|
})
|
|
1909
2269
|
]);
|
|
1910
|
-
var AnthropicBatchResultLineSchema =
|
|
1911
|
-
custom_id:
|
|
1912
|
-
result:
|
|
1913
|
-
|
|
1914
|
-
type:
|
|
1915
|
-
message:
|
|
1916
|
-
content:
|
|
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),
|
|
1917
2277
|
usage: AnthropicUsageSchema
|
|
1918
2278
|
})
|
|
1919
2279
|
}),
|
|
1920
|
-
|
|
1921
|
-
type:
|
|
1922
|
-
error:
|
|
1923
|
-
type:
|
|
1924
|
-
message:
|
|
2280
|
+
z8.object({
|
|
2281
|
+
type: z8.literal("errored"),
|
|
2282
|
+
error: z8.object({
|
|
2283
|
+
type: z8.string(),
|
|
2284
|
+
message: z8.string()
|
|
1925
2285
|
})
|
|
1926
2286
|
}),
|
|
1927
|
-
|
|
1928
|
-
|
|
2287
|
+
z8.object({ type: z8.literal("canceled") }),
|
|
2288
|
+
z8.object({ type: z8.literal("expired") })
|
|
1929
2289
|
])
|
|
1930
2290
|
});
|
|
1931
2291
|
function mapAnthropicMessageBatchResponseToMessageBatch(response) {
|
|
@@ -2075,8 +2435,8 @@ async function parseAnthropicMessageBatchHttpResponse(response) {
|
|
|
2075
2435
|
async function listAnthropicMessageBatchResults(clientContext, batchId) {
|
|
2076
2436
|
const batch = await fetchAnthropicMessageBatch(clientContext, batchId);
|
|
2077
2437
|
if (batch.resultsUrl === null) {
|
|
2078
|
-
throw new
|
|
2079
|
-
code:
|
|
2438
|
+
throw new AppError15({
|
|
2439
|
+
code: AppErrorCode14.Conflict,
|
|
2080
2440
|
message: `Anthropic message batch ${batchId} has no results because processing has not ended`,
|
|
2081
2441
|
statusCode: 409,
|
|
2082
2442
|
details: { batchId, processingStatus: batch.processingStatus }
|
|
@@ -2137,16 +2497,760 @@ async function waitForAnthropicMessageBatchCompletion(clientContext, batchId, wa
|
|
|
2137
2497
|
return batch;
|
|
2138
2498
|
}
|
|
2139
2499
|
function buildAnthropicBatchTimeoutError(batchId, timeoutMs) {
|
|
2140
|
-
return new
|
|
2141
|
-
code:
|
|
2500
|
+
return new AppError15({
|
|
2501
|
+
code: AppErrorCode14.ServiceUnavailable,
|
|
2142
2502
|
message: `Anthropic message batch ${batchId} did not complete within ${String(timeoutMs)}ms`,
|
|
2143
2503
|
statusCode: 504,
|
|
2144
2504
|
details: { batchId, timeoutMs }
|
|
2145
2505
|
});
|
|
2146
2506
|
}
|
|
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
|
|
2547
|
+
};
|
|
2548
|
+
const finishReason = response.choices[0]?.finish_reason;
|
|
2549
|
+
if (toolCalls.length > 0) {
|
|
2550
|
+
result.toolCalls = toolCalls;
|
|
2551
|
+
}
|
|
2552
|
+
if (finishReason !== null && finishReason !== void 0) {
|
|
2553
|
+
result.stopReason = normalizeAzureOpenAiFinishReason(finishReason);
|
|
2554
|
+
}
|
|
2555
|
+
return result;
|
|
2556
|
+
}
|
|
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
|
+
}));
|
|
2564
|
+
}
|
|
2565
|
+
function parseAzureOpenAiToolArgumentsJson(argumentsJsonText) {
|
|
2566
|
+
if (argumentsJsonText.trim().length === 0) {
|
|
2567
|
+
return {};
|
|
2568
|
+
}
|
|
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
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
function normalizeAzureOpenAiFinishReason(finishReason) {
|
|
2581
|
+
if (finishReason === "tool_calls") {
|
|
2582
|
+
return "tool_use";
|
|
2583
|
+
}
|
|
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
|
+
};
|
|
3249
|
+
}
|
|
2147
3250
|
export {
|
|
2148
3251
|
AnthropicMessagesResponseSchema,
|
|
2149
3252
|
AnthropicStreamEventSchema,
|
|
3253
|
+
AzureOpenAiChatCompletionResponseSchema,
|
|
2150
3254
|
ChatMessageCacheControlSchema,
|
|
2151
3255
|
ChatMessageRoleSchema,
|
|
2152
3256
|
ChatMessageSchema,
|
|
@@ -2157,16 +3261,30 @@ export {
|
|
|
2157
3261
|
anthropicBatchJsonOnlySystemInstruction,
|
|
2158
3262
|
anthropicBatchTraceWorkflowName,
|
|
2159
3263
|
applyCacheControlToLastContentBlock,
|
|
3264
|
+
awsSigV4Algorithm,
|
|
3265
|
+
azureOpenai,
|
|
3266
|
+
bedrock,
|
|
3267
|
+
bedrockAnthropicVersion,
|
|
2160
3268
|
buildAnthropicCacheControl,
|
|
2161
3269
|
buildAnthropicRequestBody,
|
|
3270
|
+
buildAnthropicStructuredOutputJsonSchema,
|
|
3271
|
+
buildAzureOpenAiChatCompletionsUrl,
|
|
3272
|
+
buildAzureOpenAiChatRequestBody,
|
|
3273
|
+
buildBedrockInvokeModelRequestBody,
|
|
3274
|
+
buildBedrockInvokeModelUrl,
|
|
3275
|
+
buildGeminiResponseSchema,
|
|
2162
3276
|
buildOpenAiRequestBody,
|
|
3277
|
+
buildOpenAiStructuredOutputJsonSchema,
|
|
2163
3278
|
buildProviderCanceledError,
|
|
2164
3279
|
calculateProviderRetryDelayMs,
|
|
3280
|
+
convertZodSchemaToJsonSchema,
|
|
2165
3281
|
createAnthropicBatchClient,
|
|
2166
3282
|
defaultAnthropicBaseUrl,
|
|
2167
3283
|
defaultAnthropicBatchPollIntervalMs,
|
|
2168
3284
|
defaultAnthropicBatchTimeoutMs,
|
|
2169
3285
|
defaultAnthropicMaxOutputTokens,
|
|
3286
|
+
defaultAzureOpenAiApiVersion,
|
|
3287
|
+
defaultBedrockMaxOutputTokens,
|
|
2170
3288
|
defaultGeminiBaseUrl,
|
|
2171
3289
|
defaultOpenAiBaseUrl,
|
|
2172
3290
|
defaultProviderRetryInitialDelayMs,
|
|
@@ -2177,6 +3295,7 @@ export {
|
|
|
2177
3295
|
executeProviderCallWithModelFallback,
|
|
2178
3296
|
executeProviderCallWithRetry,
|
|
2179
3297
|
extractAnthropicTextContent,
|
|
3298
|
+
extractAnthropicThinkingText,
|
|
2180
3299
|
extractAnthropicToolCalls,
|
|
2181
3300
|
extractMessageText,
|
|
2182
3301
|
gemini,
|
|
@@ -2185,6 +3304,8 @@ export {
|
|
|
2185
3304
|
joinSystemMessageText,
|
|
2186
3305
|
mapAnthropicResponseToChatCompletionResult,
|
|
2187
3306
|
mapAnthropicUsageToTokenUsage,
|
|
3307
|
+
mapAzureOpenAiResponseToChatCompletionResult,
|
|
3308
|
+
mapAzureOpenAiUsageToTokenUsage,
|
|
2188
3309
|
mapChatMessageToAnthropicRequestMessage,
|
|
2189
3310
|
mapChatMessageToOpenAiRequestMessage,
|
|
2190
3311
|
mapModelToolChoiceToAnthropicToolChoice,
|
|
@@ -2194,11 +3315,21 @@ export {
|
|
|
2194
3315
|
mergeProviderRequestExtraBody,
|
|
2195
3316
|
mockModel,
|
|
2196
3317
|
normalizeAnthropicStopReason,
|
|
3318
|
+
normalizeAzureOpenAiFinishReason,
|
|
3319
|
+
openAiStructuredOutputSchemaName,
|
|
2197
3320
|
openai,
|
|
2198
3321
|
parseAnthropicBatchStructuredContent,
|
|
2199
3322
|
parseAnthropicStreamEvents,
|
|
2200
3323
|
parseAnthropicToolInputJson,
|
|
3324
|
+
parseAzureOpenAiStreamEvents,
|
|
3325
|
+
parseAzureOpenAiToolArgumentsJson,
|
|
2201
3326
|
resolveAnthropicPromptCachingSettings,
|
|
3327
|
+
resolveBedrockCredentials,
|
|
2202
3328
|
resolveProviderApiKey,
|
|
2203
|
-
|
|
3329
|
+
sanitizeJsonSchemaForAnthropicStructuredOutput,
|
|
3330
|
+
sanitizeJsonSchemaForGeminiResponseSchema,
|
|
3331
|
+
sanitizeJsonSchemaForOpenAiStructuredOutput,
|
|
3332
|
+
signAwsRequestWithSigV4,
|
|
3333
|
+
streamAnthropicChatCompletion,
|
|
3334
|
+
streamAzureOpenAiChatCompletion
|
|
2204
3335
|
};
|