@assemble-dev/providers 0.3.0 → 0.4.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.js CHANGED
@@ -204,6 +204,9 @@ var openAiUnsupportedSchemaKeywords = [
204
204
  "uniqueItems",
205
205
  "default"
206
206
  ];
207
+ function isZodV4Schema(schema) {
208
+ return "_zod" in schema;
209
+ }
207
210
  function convertZodSchemaToJsonSchema(schema) {
208
211
  try {
209
212
  return JsonObjectSchema.parse(z2.toJSONSchema(schema, { io: "input" }));
@@ -268,18 +271,72 @@ function listSchemaPropertyNames(node) {
268
271
  return Object.keys(properties);
269
272
  }
270
273
 
274
+ // src/structured-output-fallback.ts
275
+ var structuredOutputJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
276
+ var structuredOutputJsonOnlySystemMessage = {
277
+ role: "system",
278
+ content: structuredOutputJsonOnlySystemInstruction
279
+ };
280
+ function prependStructuredOutputJsonOnlySystemMessage(messages) {
281
+ return [structuredOutputJsonOnlySystemMessage, ...messages];
282
+ }
283
+
271
284
  // src/openai-request-mapping.ts
285
+ var openAiReasoningModelNamePrefixes = [
286
+ "gpt-5",
287
+ "o1",
288
+ "o3",
289
+ "o4"
290
+ ];
291
+ function isOpenAiReasoningModel(model) {
292
+ return openAiReasoningModelNamePrefixes.some(
293
+ (prefix) => matchesOpenAiModelNamePrefix(model, prefix)
294
+ );
295
+ }
296
+ function matchesOpenAiModelNamePrefix(model, prefix) {
297
+ if (!model.startsWith(prefix)) {
298
+ return false;
299
+ }
300
+ const boundaryCharacter = model.charAt(prefix.length);
301
+ return boundaryCharacter === "" || boundaryCharacter === "-" || boundaryCharacter === ".";
302
+ }
303
+ function resolveOpenAiStructuredOutputMode(messages, schema) {
304
+ if (isZodV4Schema(schema)) {
305
+ return {
306
+ messages,
307
+ structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(schema)
308
+ };
309
+ }
310
+ return {
311
+ messages: prependStructuredOutputJsonOnlySystemMessage(messages),
312
+ requireJsonObjectResponse: true
313
+ };
314
+ }
272
315
  function buildOpenAiRequestBody(model, input) {
273
316
  const requestBody = {
274
317
  model,
275
318
  messages: input.messages.map(mapChatMessageToOpenAiRequestMessage)
276
319
  };
277
- if (input.temperature !== void 0) {
320
+ applyOpenAiGenerationSettings(requestBody, model, input);
321
+ applyOpenAiResponseFormat(requestBody, input);
322
+ applyOpenAiToolFields(requestBody, input);
323
+ return requestBody;
324
+ }
325
+ function applyOpenAiGenerationSettings(requestBody, model, input) {
326
+ const reasoningModel = input.reasoningModel ?? isOpenAiReasoningModel(model);
327
+ if (input.temperature !== void 0 && !reasoningModel) {
278
328
  requestBody.temperature = input.temperature;
279
329
  }
280
- if (input.maxOutputTokens !== void 0) {
281
- requestBody.max_tokens = input.maxOutputTokens;
330
+ if (input.maxOutputTokens === void 0) {
331
+ return;
332
+ }
333
+ if (reasoningModel) {
334
+ requestBody.max_completion_tokens = input.maxOutputTokens;
335
+ return;
282
336
  }
337
+ requestBody.max_tokens = input.maxOutputTokens;
338
+ }
339
+ function applyOpenAiResponseFormat(requestBody, input) {
283
340
  if (input.structuredOutputJsonSchema !== void 0) {
284
341
  requestBody.response_format = {
285
342
  type: "json_schema",
@@ -289,9 +346,13 @@ function buildOpenAiRequestBody(model, input) {
289
346
  schema: input.structuredOutputJsonSchema
290
347
  }
291
348
  };
292
- } else if (input.requireJsonObjectResponse === true) {
349
+ return;
350
+ }
351
+ if (input.requireJsonObjectResponse === true) {
293
352
  requestBody.response_format = { type: "json_object" };
294
353
  }
354
+ }
355
+ function applyOpenAiToolFields(requestBody, input) {
295
356
  if (input.tools !== void 0 && input.tools.length > 0) {
296
357
  requestBody.tools = input.tools.map(mapModelToolDefinitionToOpenAiTool);
297
358
  }
@@ -300,7 +361,6 @@ function buildOpenAiRequestBody(model, input) {
300
361
  input.toolChoice
301
362
  );
302
363
  }
303
- return requestBody;
304
364
  }
305
365
  function mapModelToolDefinitionToOpenAiTool(tool) {
306
366
  return {
@@ -665,12 +725,9 @@ function openai(options) {
665
725
  },
666
726
  async generateStructuredOutput(request) {
667
727
  const completion = await executeOpenAiAdapterCall(options, {
668
- messages: request.messages,
728
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
669
729
  temperature: request.temperature ?? options.temperature,
670
730
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
671
- structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
672
- request.schema
673
- ),
674
731
  abortSignal: request.abortSignal
675
732
  });
676
733
  return {
@@ -1438,15 +1495,10 @@ function anthropic(options) {
1438
1495
  );
1439
1496
  },
1440
1497
  async generateStructuredOutput(request) {
1441
- const completion = await executeAnthropicAdapterCall(options, {
1442
- messages: request.messages,
1443
- temperature: request.temperature ?? options.temperature,
1444
- maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1445
- structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
1446
- request.schema
1447
- ),
1448
- abortSignal: request.abortSignal
1449
- });
1498
+ const completion = await executeAnthropicAdapterCall(
1499
+ options,
1500
+ buildAnthropicStructuredOutputInput(options, request)
1501
+ );
1450
1502
  return {
1451
1503
  value: parseStructuredJsonText(completion.content, request.schema),
1452
1504
  tokenUsage: completion.tokenUsage,
@@ -1460,6 +1512,26 @@ function anthropic(options) {
1460
1512
  }
1461
1513
  };
1462
1514
  }
1515
+ function buildAnthropicStructuredOutputInput(options, request) {
1516
+ const baseInput = {
1517
+ messages: request.messages,
1518
+ temperature: request.temperature ?? options.temperature,
1519
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1520
+ abortSignal: request.abortSignal
1521
+ };
1522
+ if (isZodV4Schema(request.schema)) {
1523
+ return {
1524
+ ...baseInput,
1525
+ structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
1526
+ request.schema
1527
+ )
1528
+ };
1529
+ }
1530
+ return {
1531
+ ...baseInput,
1532
+ messages: prependStructuredOutputJsonOnlySystemMessage(request.messages)
1533
+ };
1534
+ }
1463
1535
  function buildAnthropicCallInput(options, request) {
1464
1536
  return {
1465
1537
  messages: request.messages,
@@ -1684,13 +1756,10 @@ function gemini(options) {
1684
1756
  });
1685
1757
  },
1686
1758
  async generateStructuredOutput(request) {
1687
- const completion = await executeGeminiAdapterCall(options, {
1688
- messages: request.messages,
1689
- temperature: request.temperature ?? options.temperature,
1690
- maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1691
- responseSchema: buildGeminiResponseSchema(request.schema),
1692
- abortSignal: request.abortSignal
1693
- });
1759
+ const completion = await executeGeminiAdapterCall(
1760
+ options,
1761
+ buildGeminiStructuredOutputInput(options, request)
1762
+ );
1694
1763
  return {
1695
1764
  value: parseStructuredJsonText(completion.content, request.schema),
1696
1765
  tokenUsage: completion.tokenUsage,
@@ -1699,6 +1768,25 @@ function gemini(options) {
1699
1768
  }
1700
1769
  };
1701
1770
  }
1771
+ function buildGeminiStructuredOutputInput(options, request) {
1772
+ const baseInput = {
1773
+ messages: request.messages,
1774
+ temperature: request.temperature ?? options.temperature,
1775
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1776
+ abortSignal: request.abortSignal
1777
+ };
1778
+ if (isZodV4Schema(request.schema)) {
1779
+ return {
1780
+ ...baseInput,
1781
+ responseSchema: buildGeminiResponseSchema(request.schema)
1782
+ };
1783
+ }
1784
+ return {
1785
+ ...baseInput,
1786
+ messages: prependStructuredOutputJsonOnlySystemMessage(request.messages),
1787
+ requireJsonResponse: true
1788
+ };
1789
+ }
1702
1790
  async function executeGeminiAdapterCall(options, input) {
1703
1791
  return await executeProviderCallWithModelFallback(
1704
1792
  options.fallbackModel,
@@ -1816,6 +1904,8 @@ function buildGeminiGenerationConfig(input) {
1816
1904
  if (input.responseSchema !== void 0) {
1817
1905
  generationConfig.responseMimeType = "application/json";
1818
1906
  generationConfig.responseSchema = input.responseSchema;
1907
+ } else if (input.requireJsonResponse === true) {
1908
+ generationConfig.responseMimeType = "application/json";
1819
1909
  }
1820
1910
  if (Object.keys(generationConfig).length === 0) {
1821
1911
  return void 0;
@@ -2023,7 +2113,7 @@ import { AppError as AppError15 } from "@assemble-dev/shared-utils/errors";
2023
2113
  import { AppErrorCode as AppErrorCode13 } from "@assemble-dev/shared-types/errors";
2024
2114
  import { JsonValueSchema as JsonValueSchema6 } from "@assemble-dev/shared-types/json";
2025
2115
  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.";
2116
+ var anthropicBatchJsonOnlySystemInstruction = structuredOutputJsonOnlySystemInstruction;
2027
2117
  function parseAnthropicBatchStructuredContent(content, schema) {
2028
2118
  const jsonValue = parseStructuredJsonText(
2029
2119
  content,
@@ -2833,6 +2923,7 @@ function azureOpenai(options) {
2833
2923
  messages: request.messages,
2834
2924
  temperature: request.temperature ?? options.temperature,
2835
2925
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
2926
+ reasoningModel: options.reasoningModel,
2836
2927
  tools: request.tools,
2837
2928
  toolChoice: request.toolChoice,
2838
2929
  abortSignal: request.abortSignal,
@@ -2841,12 +2932,10 @@ function azureOpenai(options) {
2841
2932
  },
2842
2933
  async generateStructuredOutput(request) {
2843
2934
  const completion = await executeAzureOpenAiAdapterCall(options, {
2844
- messages: request.messages,
2935
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
2845
2936
  temperature: request.temperature ?? options.temperature,
2846
2937
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
2847
- structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
2848
- request.schema
2849
- ),
2938
+ reasoningModel: options.reasoningModel,
2850
2939
  abortSignal: request.abortSignal
2851
2940
  });
2852
2941
  return {
@@ -2945,6 +3034,7 @@ function buildAzureOpenAiStreamInput(options, request) {
2945
3034
  messages: request.messages,
2946
3035
  temperature: request.temperature ?? options.temperature,
2947
3036
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3037
+ reasoningModel: options.reasoningModel,
2948
3038
  tools: request.tools,
2949
3039
  toolChoice: request.toolChoice,
2950
3040
  abortSignal: request.abortSignal,
@@ -3239,6 +3329,9 @@ function buildBedrockInvokeModelRequestBody(input) {
3239
3329
  };
3240
3330
  }
3241
3331
  function buildBedrockStructuredOutputSystemMessage(schema) {
3332
+ if (!isZodV4Schema(schema)) {
3333
+ return structuredOutputJsonOnlySystemMessage;
3334
+ }
3242
3335
  return {
3243
3336
  role: "system",
3244
3337
  content: `Respond with a single JSON object that satisfies the following JSON Schema. Output only valid JSON with no surrounding text.
@@ -3247,6 +3340,425 @@ JSON Schema:
3247
3340
  ${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
3248
3341
  };
3249
3342
  }
3343
+
3344
+ // src/openrouter-adapter.ts
3345
+ import { z as z12 } from "zod";
3346
+ import { AppErrorCode as AppErrorCode18 } from "@assemble-dev/shared-types/errors";
3347
+ import { JsonValueSchema as JsonValueSchema9 } from "@assemble-dev/shared-types/json";
3348
+ import { AppError as AppError19 } from "@assemble-dev/shared-utils/errors";
3349
+ var defaultOpenRouterBaseUrl = "https://openrouter.ai/api/v1";
3350
+ var OpenRouterResponseToolCallSchema = z12.object({
3351
+ id: z12.string(),
3352
+ function: z12.object({
3353
+ name: z12.string(),
3354
+ arguments: z12.string()
3355
+ })
3356
+ });
3357
+ var OpenRouterChatCompletionResponseSchema = z12.object({
3358
+ choices: z12.array(
3359
+ z12.object({
3360
+ message: z12.object({
3361
+ content: z12.string().nullable(),
3362
+ tool_calls: z12.array(OpenRouterResponseToolCallSchema).optional()
3363
+ }),
3364
+ finish_reason: z12.string().nullable().optional()
3365
+ })
3366
+ ).min(1),
3367
+ usage: z12.object({
3368
+ prompt_tokens: z12.number().int().nonnegative(),
3369
+ completion_tokens: z12.number().int().nonnegative(),
3370
+ total_tokens: z12.number().int().nonnegative()
3371
+ })
3372
+ });
3373
+ function openrouter(options) {
3374
+ return {
3375
+ name: `openrouter:${options.model}`,
3376
+ async generateChatCompletion(request) {
3377
+ return await executeOpenRouterAdapterCall(options, {
3378
+ messages: request.messages,
3379
+ temperature: request.temperature ?? options.temperature,
3380
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3381
+ reasoningModel: options.reasoningModel,
3382
+ tools: request.tools,
3383
+ toolChoice: request.toolChoice,
3384
+ abortSignal: request.abortSignal,
3385
+ extraBody: request.extraBody
3386
+ });
3387
+ },
3388
+ async generateStructuredOutput(request) {
3389
+ const completion = await executeOpenRouterAdapterCall(options, {
3390
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
3391
+ temperature: request.temperature ?? options.temperature,
3392
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3393
+ reasoningModel: options.reasoningModel,
3394
+ abortSignal: request.abortSignal
3395
+ });
3396
+ return {
3397
+ value: parseStructuredJsonText(completion.content, request.schema),
3398
+ tokenUsage: completion.tokenUsage,
3399
+ latencyMs: completion.latencyMs
3400
+ };
3401
+ }
3402
+ };
3403
+ }
3404
+ async function executeOpenRouterAdapterCall(options, input) {
3405
+ return await executeProviderCallWithModelFallback(
3406
+ options.fallbackModel,
3407
+ async () => await executeProviderCallWithRetry(
3408
+ options.retry,
3409
+ async () => await requestOpenRouterChatCompletion(options, input)
3410
+ ),
3411
+ async (fallbackModelId) => await requestOpenRouterChatCompletion(
3412
+ { ...options, model: fallbackModelId },
3413
+ input
3414
+ )
3415
+ );
3416
+ }
3417
+ function buildOpenRouterRequestHeaders(options, apiKey) {
3418
+ const headers = {
3419
+ "Content-Type": "application/json",
3420
+ Authorization: `Bearer ${apiKey}`
3421
+ };
3422
+ if (options.appUrl !== void 0) {
3423
+ headers["HTTP-Referer"] = options.appUrl;
3424
+ }
3425
+ if (options.appName !== void 0) {
3426
+ headers["X-Title"] = options.appName;
3427
+ }
3428
+ return headers;
3429
+ }
3430
+ async function requestOpenRouterChatCompletion(options, input) {
3431
+ const apiKey = resolveProviderApiKey({
3432
+ providerLabel: "OpenRouter",
3433
+ adapterFunctionName: "openrouter()",
3434
+ baseEnvVarName: "OPENROUTER_API_KEY",
3435
+ apiKey: options.apiKey,
3436
+ apiKeyName: options.apiKeyName
3437
+ });
3438
+ const fetchImplementation = options.fetchImplementation ?? fetch;
3439
+ const baseUrl = options.baseURL ?? defaultOpenRouterBaseUrl;
3440
+ const requestBody = mergeProviderRequestExtraBody(
3441
+ buildOpenAiRequestBody(options.model, input),
3442
+ options.extraBody,
3443
+ input.extraBody
3444
+ );
3445
+ const startedAt = Date.now();
3446
+ const response = await executeAbortableProviderFetch(
3447
+ "OpenRouter",
3448
+ async () => await fetchImplementation(`${baseUrl}/chat/completions`, {
3449
+ method: "POST",
3450
+ headers: buildOpenRouterRequestHeaders(options, apiKey),
3451
+ body: JSON.stringify(requestBody),
3452
+ signal: input.abortSignal
3453
+ })
3454
+ );
3455
+ const latencyMs = Date.now() - startedAt;
3456
+ if (!response.ok) {
3457
+ throw buildProviderStatusError("OpenRouter", response.status);
3458
+ }
3459
+ const parseResult = OpenRouterChatCompletionResponseSchema.safeParse(
3460
+ await response.json()
3461
+ );
3462
+ if (!parseResult.success) {
3463
+ throw buildProviderInvalidResponseError("OpenRouter", "chat completion");
3464
+ }
3465
+ return mapOpenRouterResponseToChatCompletionResult(
3466
+ parseResult.data,
3467
+ latencyMs
3468
+ );
3469
+ }
3470
+ function mapOpenRouterResponseToChatCompletionResult(response, latencyMs) {
3471
+ const toolCalls = extractOpenRouterToolCalls(response);
3472
+ const result = {
3473
+ content: extractOpenRouterMessageContent(response, toolCalls.length > 0),
3474
+ tokenUsage: mapOpenRouterUsageToTokenUsage(response),
3475
+ latencyMs
3476
+ };
3477
+ const finishReason = response.choices[0]?.finish_reason;
3478
+ if (toolCalls.length > 0) {
3479
+ result.toolCalls = toolCalls;
3480
+ }
3481
+ if (finishReason !== null && finishReason !== void 0) {
3482
+ result.stopReason = normalizeOpenRouterFinishReason(finishReason);
3483
+ }
3484
+ return result;
3485
+ }
3486
+ function extractOpenRouterToolCalls(response) {
3487
+ const responseToolCalls = response.choices[0]?.message.tool_calls ?? [];
3488
+ return responseToolCalls.map((responseToolCall) => ({
3489
+ id: responseToolCall.id,
3490
+ toolName: responseToolCall.function.name,
3491
+ input: parseOpenRouterToolArgumentsJson(
3492
+ responseToolCall.function.arguments
3493
+ )
3494
+ }));
3495
+ }
3496
+ function parseOpenRouterToolArgumentsJson(argumentsJsonText) {
3497
+ if (argumentsJsonText.trim().length === 0) {
3498
+ return {};
3499
+ }
3500
+ try {
3501
+ return JsonValueSchema9.parse(JSON.parse(argumentsJsonText));
3502
+ } catch (error) {
3503
+ throw new AppError19({
3504
+ code: AppErrorCode18.ValidationFailed,
3505
+ message: "OpenRouter tool call arguments were not valid JSON",
3506
+ statusCode: 502,
3507
+ cause: error instanceof Error ? error : void 0
3508
+ });
3509
+ }
3510
+ }
3511
+ function normalizeOpenRouterFinishReason(finishReason) {
3512
+ if (finishReason === "tool_calls") {
3513
+ return "tool_use";
3514
+ }
3515
+ if (finishReason === "length") {
3516
+ return "max_tokens";
3517
+ }
3518
+ return "end_turn";
3519
+ }
3520
+ function extractOpenRouterMessageContent(response, allowEmptyContent) {
3521
+ const content = response.choices[0]?.message.content;
3522
+ if (content === void 0 || content === null) {
3523
+ if (allowEmptyContent) {
3524
+ return "";
3525
+ }
3526
+ throw new AppError19({
3527
+ code: AppErrorCode18.ValidationFailed,
3528
+ message: "OpenRouter response contained no message content",
3529
+ statusCode: 502
3530
+ });
3531
+ }
3532
+ return content;
3533
+ }
3534
+ function mapOpenRouterUsageToTokenUsage(response) {
3535
+ return {
3536
+ inputTokens: response.usage.prompt_tokens,
3537
+ outputTokens: response.usage.completion_tokens,
3538
+ totalTokens: response.usage.total_tokens
3539
+ };
3540
+ }
3541
+
3542
+ // src/cloudflare-workers-ai-adapter.ts
3543
+ import { z as z13 } from "zod";
3544
+ import { AppErrorCode as AppErrorCode19 } from "@assemble-dev/shared-types/errors";
3545
+ import { JsonValueSchema as JsonValueSchema10 } from "@assemble-dev/shared-types/json";
3546
+ import { AppError as AppError20 } from "@assemble-dev/shared-utils/errors";
3547
+ var defaultCloudflareApiBaseUrl = "https://api.cloudflare.com/client/v4";
3548
+ var cloudflareAccountIdEnvVarName = "CLOUDFLARE_ACCOUNT_ID";
3549
+ var CloudflareWorkersAiResponseToolCallSchema = z13.object({
3550
+ id: z13.string(),
3551
+ function: z13.object({
3552
+ name: z13.string(),
3553
+ arguments: z13.string()
3554
+ })
3555
+ });
3556
+ var CloudflareWorkersAiChatCompletionResponseSchema = z13.object({
3557
+ choices: z13.array(
3558
+ z13.object({
3559
+ message: z13.object({
3560
+ content: z13.string().nullable(),
3561
+ tool_calls: z13.array(CloudflareWorkersAiResponseToolCallSchema).optional()
3562
+ }),
3563
+ finish_reason: z13.string().nullable().optional()
3564
+ })
3565
+ ).min(1),
3566
+ usage: z13.object({
3567
+ prompt_tokens: z13.number().int().nonnegative(),
3568
+ completion_tokens: z13.number().int().nonnegative(),
3569
+ total_tokens: z13.number().int().nonnegative()
3570
+ }).optional()
3571
+ });
3572
+ function cloudflareWorkersAi(options) {
3573
+ return {
3574
+ name: `cloudflare-workers-ai:${options.model}`,
3575
+ async generateChatCompletion(request) {
3576
+ return await executeCloudflareWorkersAiAdapterCall(options, {
3577
+ messages: request.messages,
3578
+ temperature: request.temperature ?? options.temperature,
3579
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3580
+ tools: request.tools,
3581
+ toolChoice: request.toolChoice,
3582
+ abortSignal: request.abortSignal,
3583
+ extraBody: request.extraBody
3584
+ });
3585
+ },
3586
+ async generateStructuredOutput(request) {
3587
+ const completion = await executeCloudflareWorkersAiAdapterCall(options, {
3588
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
3589
+ temperature: request.temperature ?? options.temperature,
3590
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3591
+ abortSignal: request.abortSignal
3592
+ });
3593
+ return {
3594
+ value: parseStructuredJsonText(completion.content, request.schema),
3595
+ tokenUsage: completion.tokenUsage,
3596
+ latencyMs: completion.latencyMs
3597
+ };
3598
+ }
3599
+ };
3600
+ }
3601
+ function buildCloudflareWorkersAiChatCompletionsUrl(options) {
3602
+ if (options.baseURL !== void 0 && options.baseURL.length > 0) {
3603
+ return `${options.baseURL.replace(/\/+$/, "")}/chat/completions`;
3604
+ }
3605
+ const accountId = resolveCloudflareWorkersAiAccountId(options);
3606
+ return `${defaultCloudflareApiBaseUrl}/accounts/${encodeURIComponent(accountId)}/ai/v1/chat/completions`;
3607
+ }
3608
+ function resolveCloudflareWorkersAiAccountId(options) {
3609
+ if (options.accountId !== void 0 && options.accountId.length > 0) {
3610
+ return options.accountId;
3611
+ }
3612
+ const envAccountId = readEnvironmentVariable2(cloudflareAccountIdEnvVarName);
3613
+ if (envAccountId !== void 0 && envAccountId.length > 0) {
3614
+ return envAccountId;
3615
+ }
3616
+ throw new AppError20({
3617
+ code: AppErrorCode19.BadRequest,
3618
+ message: "Cloudflare Workers AI account is missing. Pass accountId or baseURL to cloudflareWorkersAi() or set the CLOUDFLARE_ACCOUNT_ID environment variable.",
3619
+ statusCode: 400
3620
+ });
3621
+ }
3622
+ function readEnvironmentVariable2(envVarName) {
3623
+ const parsedEnv = z13.object({ [envVarName]: z13.string().optional() }).parse(process.env);
3624
+ return parsedEnv[envVarName];
3625
+ }
3626
+ async function executeCloudflareWorkersAiAdapterCall(options, input) {
3627
+ return await executeProviderCallWithModelFallback(
3628
+ options.fallbackModel,
3629
+ async () => await executeProviderCallWithRetry(
3630
+ options.retry,
3631
+ async () => await requestCloudflareWorkersAiChatCompletion(options, input)
3632
+ ),
3633
+ async (fallbackModelId) => await requestCloudflareWorkersAiChatCompletion(
3634
+ { ...options, model: fallbackModelId },
3635
+ input
3636
+ )
3637
+ );
3638
+ }
3639
+ async function requestCloudflareWorkersAiChatCompletion(options, input) {
3640
+ const apiToken = resolveProviderApiKey({
3641
+ providerLabel: "Cloudflare Workers AI",
3642
+ adapterFunctionName: "cloudflareWorkersAi()",
3643
+ baseEnvVarName: "CLOUDFLARE_API_TOKEN",
3644
+ apiKey: options.apiKey,
3645
+ apiKeyName: options.apiKeyName
3646
+ });
3647
+ const fetchImplementation = options.fetchImplementation ?? fetch;
3648
+ const requestUrl = buildCloudflareWorkersAiChatCompletionsUrl(options);
3649
+ const requestBody = mergeProviderRequestExtraBody(
3650
+ buildOpenAiRequestBody(options.model, input),
3651
+ options.extraBody,
3652
+ input.extraBody
3653
+ );
3654
+ const startedAt = Date.now();
3655
+ const response = await executeAbortableProviderFetch(
3656
+ "Cloudflare Workers AI",
3657
+ async () => await fetchImplementation(requestUrl, {
3658
+ method: "POST",
3659
+ headers: {
3660
+ "Content-Type": "application/json",
3661
+ Authorization: `Bearer ${apiToken}`
3662
+ },
3663
+ body: JSON.stringify(requestBody),
3664
+ signal: input.abortSignal
3665
+ })
3666
+ );
3667
+ const latencyMs = Date.now() - startedAt;
3668
+ if (!response.ok) {
3669
+ throw buildProviderStatusError("Cloudflare Workers AI", response.status);
3670
+ }
3671
+ const parseResult = CloudflareWorkersAiChatCompletionResponseSchema.safeParse(
3672
+ await response.json()
3673
+ );
3674
+ if (!parseResult.success) {
3675
+ throw buildProviderInvalidResponseError(
3676
+ "Cloudflare Workers AI",
3677
+ "chat completion"
3678
+ );
3679
+ }
3680
+ return mapCloudflareWorkersAiResponseToChatCompletionResult(
3681
+ parseResult.data,
3682
+ latencyMs
3683
+ );
3684
+ }
3685
+ function mapCloudflareWorkersAiResponseToChatCompletionResult(response, latencyMs) {
3686
+ const toolCalls = extractCloudflareWorkersAiToolCalls(response);
3687
+ const result = {
3688
+ content: extractCloudflareWorkersAiMessageContent(
3689
+ response,
3690
+ toolCalls.length > 0
3691
+ ),
3692
+ tokenUsage: mapCloudflareWorkersAiUsageToTokenUsage(response),
3693
+ latencyMs
3694
+ };
3695
+ const finishReason = response.choices[0]?.finish_reason;
3696
+ if (toolCalls.length > 0) {
3697
+ result.toolCalls = toolCalls;
3698
+ }
3699
+ if (finishReason !== null && finishReason !== void 0) {
3700
+ result.stopReason = normalizeCloudflareWorkersAiFinishReason(finishReason);
3701
+ }
3702
+ return result;
3703
+ }
3704
+ function extractCloudflareWorkersAiToolCalls(response) {
3705
+ const responseToolCalls = response.choices[0]?.message.tool_calls ?? [];
3706
+ return responseToolCalls.map((responseToolCall) => ({
3707
+ id: responseToolCall.id,
3708
+ toolName: responseToolCall.function.name,
3709
+ input: parseCloudflareWorkersAiToolArgumentsJson(
3710
+ responseToolCall.function.arguments
3711
+ )
3712
+ }));
3713
+ }
3714
+ function parseCloudflareWorkersAiToolArgumentsJson(argumentsJsonText) {
3715
+ if (argumentsJsonText.trim().length === 0) {
3716
+ return {};
3717
+ }
3718
+ try {
3719
+ return JsonValueSchema10.parse(JSON.parse(argumentsJsonText));
3720
+ } catch (error) {
3721
+ throw new AppError20({
3722
+ code: AppErrorCode19.ValidationFailed,
3723
+ message: "Cloudflare Workers AI tool call arguments were not valid JSON",
3724
+ statusCode: 502,
3725
+ cause: error instanceof Error ? error : void 0
3726
+ });
3727
+ }
3728
+ }
3729
+ function normalizeCloudflareWorkersAiFinishReason(finishReason) {
3730
+ if (finishReason === "tool_calls") {
3731
+ return "tool_use";
3732
+ }
3733
+ if (finishReason === "length") {
3734
+ return "max_tokens";
3735
+ }
3736
+ return "end_turn";
3737
+ }
3738
+ function extractCloudflareWorkersAiMessageContent(response, allowEmptyContent) {
3739
+ const content = response.choices[0]?.message.content;
3740
+ if (content === void 0 || content === null) {
3741
+ if (allowEmptyContent) {
3742
+ return "";
3743
+ }
3744
+ throw new AppError20({
3745
+ code: AppErrorCode19.ValidationFailed,
3746
+ message: "Cloudflare Workers AI response contained no message content",
3747
+ statusCode: 502
3748
+ });
3749
+ }
3750
+ return content;
3751
+ }
3752
+ function mapCloudflareWorkersAiUsageToTokenUsage(response) {
3753
+ if (response.usage === void 0) {
3754
+ return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
3755
+ }
3756
+ return {
3757
+ inputTokens: response.usage.prompt_tokens,
3758
+ outputTokens: response.usage.completion_tokens,
3759
+ totalTokens: response.usage.total_tokens
3760
+ };
3761
+ }
3250
3762
  export {
3251
3763
  AnthropicMessagesResponseSchema,
3252
3764
  AnthropicStreamEventSchema,
@@ -3272,11 +3784,13 @@ export {
3272
3784
  buildAzureOpenAiChatRequestBody,
3273
3785
  buildBedrockInvokeModelRequestBody,
3274
3786
  buildBedrockInvokeModelUrl,
3787
+ buildCloudflareWorkersAiChatCompletionsUrl,
3275
3788
  buildGeminiResponseSchema,
3276
3789
  buildOpenAiRequestBody,
3277
3790
  buildOpenAiStructuredOutputJsonSchema,
3278
3791
  buildProviderCanceledError,
3279
3792
  calculateProviderRetryDelayMs,
3793
+ cloudflareWorkersAi,
3280
3794
  convertZodSchemaToJsonSchema,
3281
3795
  createAnthropicBatchClient,
3282
3796
  defaultAnthropicBaseUrl,
@@ -3285,8 +3799,10 @@ export {
3285
3799
  defaultAnthropicMaxOutputTokens,
3286
3800
  defaultAzureOpenAiApiVersion,
3287
3801
  defaultBedrockMaxOutputTokens,
3802
+ defaultCloudflareApiBaseUrl,
3288
3803
  defaultGeminiBaseUrl,
3289
3804
  defaultOpenAiBaseUrl,
3805
+ defaultOpenRouterBaseUrl,
3290
3806
  defaultProviderRetryInitialDelayMs,
3291
3807
  defaultProviderRetryMaxAttempts,
3292
3808
  defaultProviderRetryMaxDelayMs,
@@ -3300,7 +3816,9 @@ export {
3300
3816
  extractMessageText,
3301
3817
  gemini,
3302
3818
  isAbortError,
3819
+ isOpenAiReasoningModel,
3303
3820
  isRetryableProviderAppError,
3821
+ isZodV4Schema,
3304
3822
  joinSystemMessageText,
3305
3823
  mapAnthropicResponseToChatCompletionResult,
3306
3824
  mapAnthropicUsageToTokenUsage,
@@ -3318,18 +3836,23 @@ export {
3318
3836
  normalizeAzureOpenAiFinishReason,
3319
3837
  openAiStructuredOutputSchemaName,
3320
3838
  openai,
3839
+ openrouter,
3321
3840
  parseAnthropicBatchStructuredContent,
3322
3841
  parseAnthropicStreamEvents,
3323
3842
  parseAnthropicToolInputJson,
3324
3843
  parseAzureOpenAiStreamEvents,
3325
3844
  parseAzureOpenAiToolArgumentsJson,
3845
+ prependStructuredOutputJsonOnlySystemMessage,
3326
3846
  resolveAnthropicPromptCachingSettings,
3327
3847
  resolveBedrockCredentials,
3848
+ resolveOpenAiStructuredOutputMode,
3328
3849
  resolveProviderApiKey,
3329
3850
  sanitizeJsonSchemaForAnthropicStructuredOutput,
3330
3851
  sanitizeJsonSchemaForGeminiResponseSchema,
3331
3852
  sanitizeJsonSchemaForOpenAiStructuredOutput,
3332
3853
  signAwsRequestWithSigV4,
3333
3854
  streamAnthropicChatCompletion,
3334
- streamAzureOpenAiChatCompletion
3855
+ streamAzureOpenAiChatCompletion,
3856
+ structuredOutputJsonOnlySystemInstruction,
3857
+ structuredOutputJsonOnlySystemMessage
3335
3858
  };