@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.cjs CHANGED
@@ -44,11 +44,13 @@ __export(index_exports, {
44
44
  buildAzureOpenAiChatRequestBody: () => buildAzureOpenAiChatRequestBody,
45
45
  buildBedrockInvokeModelRequestBody: () => buildBedrockInvokeModelRequestBody,
46
46
  buildBedrockInvokeModelUrl: () => buildBedrockInvokeModelUrl,
47
+ buildCloudflareWorkersAiChatCompletionsUrl: () => buildCloudflareWorkersAiChatCompletionsUrl,
47
48
  buildGeminiResponseSchema: () => buildGeminiResponseSchema,
48
49
  buildOpenAiRequestBody: () => buildOpenAiRequestBody,
49
50
  buildOpenAiStructuredOutputJsonSchema: () => buildOpenAiStructuredOutputJsonSchema,
50
51
  buildProviderCanceledError: () => buildProviderCanceledError,
51
52
  calculateProviderRetryDelayMs: () => calculateProviderRetryDelayMs,
53
+ cloudflareWorkersAi: () => cloudflareWorkersAi,
52
54
  convertZodSchemaToJsonSchema: () => convertZodSchemaToJsonSchema,
53
55
  createAnthropicBatchClient: () => createAnthropicBatchClient,
54
56
  defaultAnthropicBaseUrl: () => defaultAnthropicBaseUrl,
@@ -57,8 +59,10 @@ __export(index_exports, {
57
59
  defaultAnthropicMaxOutputTokens: () => defaultAnthropicMaxOutputTokens,
58
60
  defaultAzureOpenAiApiVersion: () => defaultAzureOpenAiApiVersion,
59
61
  defaultBedrockMaxOutputTokens: () => defaultBedrockMaxOutputTokens,
62
+ defaultCloudflareApiBaseUrl: () => defaultCloudflareApiBaseUrl,
60
63
  defaultGeminiBaseUrl: () => defaultGeminiBaseUrl,
61
64
  defaultOpenAiBaseUrl: () => defaultOpenAiBaseUrl,
65
+ defaultOpenRouterBaseUrl: () => defaultOpenRouterBaseUrl,
62
66
  defaultProviderRetryInitialDelayMs: () => defaultProviderRetryInitialDelayMs,
63
67
  defaultProviderRetryMaxAttempts: () => defaultProviderRetryMaxAttempts,
64
68
  defaultProviderRetryMaxDelayMs: () => defaultProviderRetryMaxDelayMs,
@@ -72,7 +76,9 @@ __export(index_exports, {
72
76
  extractMessageText: () => extractMessageText,
73
77
  gemini: () => gemini,
74
78
  isAbortError: () => isAbortError,
79
+ isOpenAiReasoningModel: () => isOpenAiReasoningModel,
75
80
  isRetryableProviderAppError: () => isRetryableProviderAppError,
81
+ isZodV4Schema: () => isZodV4Schema,
76
82
  joinSystemMessageText: () => joinSystemMessageText,
77
83
  mapAnthropicResponseToChatCompletionResult: () => mapAnthropicResponseToChatCompletionResult,
78
84
  mapAnthropicUsageToTokenUsage: () => mapAnthropicUsageToTokenUsage,
@@ -90,20 +96,25 @@ __export(index_exports, {
90
96
  normalizeAzureOpenAiFinishReason: () => normalizeAzureOpenAiFinishReason,
91
97
  openAiStructuredOutputSchemaName: () => openAiStructuredOutputSchemaName,
92
98
  openai: () => openai,
99
+ openrouter: () => openrouter,
93
100
  parseAnthropicBatchStructuredContent: () => parseAnthropicBatchStructuredContent,
94
101
  parseAnthropicStreamEvents: () => parseAnthropicStreamEvents,
95
102
  parseAnthropicToolInputJson: () => parseAnthropicToolInputJson,
96
103
  parseAzureOpenAiStreamEvents: () => parseAzureOpenAiStreamEvents,
97
104
  parseAzureOpenAiToolArgumentsJson: () => parseAzureOpenAiToolArgumentsJson,
105
+ prependStructuredOutputJsonOnlySystemMessage: () => prependStructuredOutputJsonOnlySystemMessage,
98
106
  resolveAnthropicPromptCachingSettings: () => resolveAnthropicPromptCachingSettings,
99
107
  resolveBedrockCredentials: () => resolveBedrockCredentials,
108
+ resolveOpenAiStructuredOutputMode: () => resolveOpenAiStructuredOutputMode,
100
109
  resolveProviderApiKey: () => resolveProviderApiKey,
101
110
  sanitizeJsonSchemaForAnthropicStructuredOutput: () => sanitizeJsonSchemaForAnthropicStructuredOutput,
102
111
  sanitizeJsonSchemaForGeminiResponseSchema: () => sanitizeJsonSchemaForGeminiResponseSchema,
103
112
  sanitizeJsonSchemaForOpenAiStructuredOutput: () => sanitizeJsonSchemaForOpenAiStructuredOutput,
104
113
  signAwsRequestWithSigV4: () => signAwsRequestWithSigV4,
105
114
  streamAnthropicChatCompletion: () => streamAnthropicChatCompletion,
106
- streamAzureOpenAiChatCompletion: () => streamAzureOpenAiChatCompletion
115
+ streamAzureOpenAiChatCompletion: () => streamAzureOpenAiChatCompletion,
116
+ structuredOutputJsonOnlySystemInstruction: () => structuredOutputJsonOnlySystemInstruction,
117
+ structuredOutputJsonOnlySystemMessage: () => structuredOutputJsonOnlySystemMessage
107
118
  });
108
119
  module.exports = __toCommonJS(index_exports);
109
120
 
@@ -313,6 +324,9 @@ var openAiUnsupportedSchemaKeywords = [
313
324
  "uniqueItems",
314
325
  "default"
315
326
  ];
327
+ function isZodV4Schema(schema) {
328
+ return "_zod" in schema;
329
+ }
316
330
  function convertZodSchemaToJsonSchema(schema) {
317
331
  try {
318
332
  return import_json2.JsonObjectSchema.parse(import_zod2.z.toJSONSchema(schema, { io: "input" }));
@@ -377,18 +391,72 @@ function listSchemaPropertyNames(node) {
377
391
  return Object.keys(properties);
378
392
  }
379
393
 
394
+ // src/structured-output-fallback.ts
395
+ var structuredOutputJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
396
+ var structuredOutputJsonOnlySystemMessage = {
397
+ role: "system",
398
+ content: structuredOutputJsonOnlySystemInstruction
399
+ };
400
+ function prependStructuredOutputJsonOnlySystemMessage(messages) {
401
+ return [structuredOutputJsonOnlySystemMessage, ...messages];
402
+ }
403
+
380
404
  // src/openai-request-mapping.ts
405
+ var openAiReasoningModelNamePrefixes = [
406
+ "gpt-5",
407
+ "o1",
408
+ "o3",
409
+ "o4"
410
+ ];
411
+ function isOpenAiReasoningModel(model) {
412
+ return openAiReasoningModelNamePrefixes.some(
413
+ (prefix) => matchesOpenAiModelNamePrefix(model, prefix)
414
+ );
415
+ }
416
+ function matchesOpenAiModelNamePrefix(model, prefix) {
417
+ if (!model.startsWith(prefix)) {
418
+ return false;
419
+ }
420
+ const boundaryCharacter = model.charAt(prefix.length);
421
+ return boundaryCharacter === "" || boundaryCharacter === "-" || boundaryCharacter === ".";
422
+ }
423
+ function resolveOpenAiStructuredOutputMode(messages, schema) {
424
+ if (isZodV4Schema(schema)) {
425
+ return {
426
+ messages,
427
+ structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(schema)
428
+ };
429
+ }
430
+ return {
431
+ messages: prependStructuredOutputJsonOnlySystemMessage(messages),
432
+ requireJsonObjectResponse: true
433
+ };
434
+ }
381
435
  function buildOpenAiRequestBody(model, input) {
382
436
  const requestBody = {
383
437
  model,
384
438
  messages: input.messages.map(mapChatMessageToOpenAiRequestMessage)
385
439
  };
386
- if (input.temperature !== void 0) {
440
+ applyOpenAiGenerationSettings(requestBody, model, input);
441
+ applyOpenAiResponseFormat(requestBody, input);
442
+ applyOpenAiToolFields(requestBody, input);
443
+ return requestBody;
444
+ }
445
+ function applyOpenAiGenerationSettings(requestBody, model, input) {
446
+ const reasoningModel = input.reasoningModel ?? isOpenAiReasoningModel(model);
447
+ if (input.temperature !== void 0 && !reasoningModel) {
387
448
  requestBody.temperature = input.temperature;
388
449
  }
389
- if (input.maxOutputTokens !== void 0) {
390
- requestBody.max_tokens = input.maxOutputTokens;
450
+ if (input.maxOutputTokens === void 0) {
451
+ return;
452
+ }
453
+ if (reasoningModel) {
454
+ requestBody.max_completion_tokens = input.maxOutputTokens;
455
+ return;
391
456
  }
457
+ requestBody.max_tokens = input.maxOutputTokens;
458
+ }
459
+ function applyOpenAiResponseFormat(requestBody, input) {
392
460
  if (input.structuredOutputJsonSchema !== void 0) {
393
461
  requestBody.response_format = {
394
462
  type: "json_schema",
@@ -398,9 +466,13 @@ function buildOpenAiRequestBody(model, input) {
398
466
  schema: input.structuredOutputJsonSchema
399
467
  }
400
468
  };
401
- } else if (input.requireJsonObjectResponse === true) {
469
+ return;
470
+ }
471
+ if (input.requireJsonObjectResponse === true) {
402
472
  requestBody.response_format = { type: "json_object" };
403
473
  }
474
+ }
475
+ function applyOpenAiToolFields(requestBody, input) {
404
476
  if (input.tools !== void 0 && input.tools.length > 0) {
405
477
  requestBody.tools = input.tools.map(mapModelToolDefinitionToOpenAiTool);
406
478
  }
@@ -409,7 +481,6 @@ function buildOpenAiRequestBody(model, input) {
409
481
  input.toolChoice
410
482
  );
411
483
  }
412
- return requestBody;
413
484
  }
414
485
  function mapModelToolDefinitionToOpenAiTool(tool) {
415
486
  return {
@@ -770,12 +841,9 @@ function openai(options) {
770
841
  },
771
842
  async generateStructuredOutput(request) {
772
843
  const completion = await executeOpenAiAdapterCall(options, {
773
- messages: request.messages,
844
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
774
845
  temperature: request.temperature ?? options.temperature,
775
846
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
776
- structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
777
- request.schema
778
- ),
779
847
  abortSignal: request.abortSignal
780
848
  });
781
849
  return {
@@ -1543,15 +1611,10 @@ function anthropic(options) {
1543
1611
  );
1544
1612
  },
1545
1613
  async generateStructuredOutput(request) {
1546
- const completion = await executeAnthropicAdapterCall(options, {
1547
- messages: request.messages,
1548
- temperature: request.temperature ?? options.temperature,
1549
- maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1550
- structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
1551
- request.schema
1552
- ),
1553
- abortSignal: request.abortSignal
1554
- });
1614
+ const completion = await executeAnthropicAdapterCall(
1615
+ options,
1616
+ buildAnthropicStructuredOutputInput(options, request)
1617
+ );
1555
1618
  return {
1556
1619
  value: parseStructuredJsonText(completion.content, request.schema),
1557
1620
  tokenUsage: completion.tokenUsage,
@@ -1565,6 +1628,26 @@ function anthropic(options) {
1565
1628
  }
1566
1629
  };
1567
1630
  }
1631
+ function buildAnthropicStructuredOutputInput(options, request) {
1632
+ const baseInput = {
1633
+ messages: request.messages,
1634
+ temperature: request.temperature ?? options.temperature,
1635
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1636
+ abortSignal: request.abortSignal
1637
+ };
1638
+ if (isZodV4Schema(request.schema)) {
1639
+ return {
1640
+ ...baseInput,
1641
+ structuredOutputJsonSchema: buildAnthropicStructuredOutputJsonSchema(
1642
+ request.schema
1643
+ )
1644
+ };
1645
+ }
1646
+ return {
1647
+ ...baseInput,
1648
+ messages: prependStructuredOutputJsonOnlySystemMessage(request.messages)
1649
+ };
1650
+ }
1568
1651
  function buildAnthropicCallInput(options, request) {
1569
1652
  return {
1570
1653
  messages: request.messages,
@@ -1789,13 +1872,10 @@ function gemini(options) {
1789
1872
  });
1790
1873
  },
1791
1874
  async generateStructuredOutput(request) {
1792
- const completion = await executeGeminiAdapterCall(options, {
1793
- messages: request.messages,
1794
- temperature: request.temperature ?? options.temperature,
1795
- maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1796
- responseSchema: buildGeminiResponseSchema(request.schema),
1797
- abortSignal: request.abortSignal
1798
- });
1875
+ const completion = await executeGeminiAdapterCall(
1876
+ options,
1877
+ buildGeminiStructuredOutputInput(options, request)
1878
+ );
1799
1879
  return {
1800
1880
  value: parseStructuredJsonText(completion.content, request.schema),
1801
1881
  tokenUsage: completion.tokenUsage,
@@ -1804,6 +1884,25 @@ function gemini(options) {
1804
1884
  }
1805
1885
  };
1806
1886
  }
1887
+ function buildGeminiStructuredOutputInput(options, request) {
1888
+ const baseInput = {
1889
+ messages: request.messages,
1890
+ temperature: request.temperature ?? options.temperature,
1891
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
1892
+ abortSignal: request.abortSignal
1893
+ };
1894
+ if (isZodV4Schema(request.schema)) {
1895
+ return {
1896
+ ...baseInput,
1897
+ responseSchema: buildGeminiResponseSchema(request.schema)
1898
+ };
1899
+ }
1900
+ return {
1901
+ ...baseInput,
1902
+ messages: prependStructuredOutputJsonOnlySystemMessage(request.messages),
1903
+ requireJsonResponse: true
1904
+ };
1905
+ }
1807
1906
  async function executeGeminiAdapterCall(options, input) {
1808
1907
  return await executeProviderCallWithModelFallback(
1809
1908
  options.fallbackModel,
@@ -1921,6 +2020,8 @@ function buildGeminiGenerationConfig(input) {
1921
2020
  if (input.responseSchema !== void 0) {
1922
2021
  generationConfig.responseMimeType = "application/json";
1923
2022
  generationConfig.responseSchema = input.responseSchema;
2023
+ } else if (input.requireJsonResponse === true) {
2024
+ generationConfig.responseMimeType = "application/json";
1924
2025
  }
1925
2026
  if (Object.keys(generationConfig).length === 0) {
1926
2027
  return void 0;
@@ -2128,7 +2229,7 @@ var import_errors29 = require("@assemble-dev/shared-utils/errors");
2128
2229
  var import_errors26 = require("@assemble-dev/shared-types/errors");
2129
2230
  var import_json7 = require("@assemble-dev/shared-types/json");
2130
2231
  var import_errors27 = require("@assemble-dev/shared-utils/errors");
2131
- var anthropicBatchJsonOnlySystemInstruction = "Respond with a single JSON object that satisfies the caller's requested structure. Output only valid JSON with no surrounding text.";
2232
+ var anthropicBatchJsonOnlySystemInstruction = structuredOutputJsonOnlySystemInstruction;
2132
2233
  function parseAnthropicBatchStructuredContent(content, schema) {
2133
2234
  const jsonValue = parseStructuredJsonText(
2134
2235
  content,
@@ -2935,6 +3036,7 @@ function azureOpenai(options) {
2935
3036
  messages: request.messages,
2936
3037
  temperature: request.temperature ?? options.temperature,
2937
3038
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3039
+ reasoningModel: options.reasoningModel,
2938
3040
  tools: request.tools,
2939
3041
  toolChoice: request.toolChoice,
2940
3042
  abortSignal: request.abortSignal,
@@ -2943,12 +3045,10 @@ function azureOpenai(options) {
2943
3045
  },
2944
3046
  async generateStructuredOutput(request) {
2945
3047
  const completion = await executeAzureOpenAiAdapterCall(options, {
2946
- messages: request.messages,
3048
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
2947
3049
  temperature: request.temperature ?? options.temperature,
2948
3050
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
2949
- structuredOutputJsonSchema: buildOpenAiStructuredOutputJsonSchema(
2950
- request.schema
2951
- ),
3051
+ reasoningModel: options.reasoningModel,
2952
3052
  abortSignal: request.abortSignal
2953
3053
  });
2954
3054
  return {
@@ -3047,6 +3147,7 @@ function buildAzureOpenAiStreamInput(options, request) {
3047
3147
  messages: request.messages,
3048
3148
  temperature: request.temperature ?? options.temperature,
3049
3149
  maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3150
+ reasoningModel: options.reasoningModel,
3050
3151
  tools: request.tools,
3051
3152
  toolChoice: request.toolChoice,
3052
3153
  abortSignal: request.abortSignal,
@@ -3341,6 +3442,9 @@ function buildBedrockInvokeModelRequestBody(input) {
3341
3442
  };
3342
3443
  }
3343
3444
  function buildBedrockStructuredOutputSystemMessage(schema) {
3445
+ if (!isZodV4Schema(schema)) {
3446
+ return structuredOutputJsonOnlySystemMessage;
3447
+ }
3344
3448
  return {
3345
3449
  role: "system",
3346
3450
  content: `Respond with a single JSON object that satisfies the following JSON Schema. Output only valid JSON with no surrounding text.
@@ -3349,6 +3453,425 @@ JSON Schema:
3349
3453
  ${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
3350
3454
  };
3351
3455
  }
3456
+
3457
+ // src/openrouter-adapter.ts
3458
+ var import_zod12 = require("zod");
3459
+ var import_errors36 = require("@assemble-dev/shared-types/errors");
3460
+ var import_json10 = require("@assemble-dev/shared-types/json");
3461
+ var import_errors37 = require("@assemble-dev/shared-utils/errors");
3462
+ var defaultOpenRouterBaseUrl = "https://openrouter.ai/api/v1";
3463
+ var OpenRouterResponseToolCallSchema = import_zod12.z.object({
3464
+ id: import_zod12.z.string(),
3465
+ function: import_zod12.z.object({
3466
+ name: import_zod12.z.string(),
3467
+ arguments: import_zod12.z.string()
3468
+ })
3469
+ });
3470
+ var OpenRouterChatCompletionResponseSchema = import_zod12.z.object({
3471
+ choices: import_zod12.z.array(
3472
+ import_zod12.z.object({
3473
+ message: import_zod12.z.object({
3474
+ content: import_zod12.z.string().nullable(),
3475
+ tool_calls: import_zod12.z.array(OpenRouterResponseToolCallSchema).optional()
3476
+ }),
3477
+ finish_reason: import_zod12.z.string().nullable().optional()
3478
+ })
3479
+ ).min(1),
3480
+ usage: import_zod12.z.object({
3481
+ prompt_tokens: import_zod12.z.number().int().nonnegative(),
3482
+ completion_tokens: import_zod12.z.number().int().nonnegative(),
3483
+ total_tokens: import_zod12.z.number().int().nonnegative()
3484
+ })
3485
+ });
3486
+ function openrouter(options) {
3487
+ return {
3488
+ name: `openrouter:${options.model}`,
3489
+ async generateChatCompletion(request) {
3490
+ return await executeOpenRouterAdapterCall(options, {
3491
+ messages: request.messages,
3492
+ temperature: request.temperature ?? options.temperature,
3493
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3494
+ reasoningModel: options.reasoningModel,
3495
+ tools: request.tools,
3496
+ toolChoice: request.toolChoice,
3497
+ abortSignal: request.abortSignal,
3498
+ extraBody: request.extraBody
3499
+ });
3500
+ },
3501
+ async generateStructuredOutput(request) {
3502
+ const completion = await executeOpenRouterAdapterCall(options, {
3503
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
3504
+ temperature: request.temperature ?? options.temperature,
3505
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3506
+ reasoningModel: options.reasoningModel,
3507
+ abortSignal: request.abortSignal
3508
+ });
3509
+ return {
3510
+ value: parseStructuredJsonText(completion.content, request.schema),
3511
+ tokenUsage: completion.tokenUsage,
3512
+ latencyMs: completion.latencyMs
3513
+ };
3514
+ }
3515
+ };
3516
+ }
3517
+ async function executeOpenRouterAdapterCall(options, input) {
3518
+ return await executeProviderCallWithModelFallback(
3519
+ options.fallbackModel,
3520
+ async () => await executeProviderCallWithRetry(
3521
+ options.retry,
3522
+ async () => await requestOpenRouterChatCompletion(options, input)
3523
+ ),
3524
+ async (fallbackModelId) => await requestOpenRouterChatCompletion(
3525
+ { ...options, model: fallbackModelId },
3526
+ input
3527
+ )
3528
+ );
3529
+ }
3530
+ function buildOpenRouterRequestHeaders(options, apiKey) {
3531
+ const headers = {
3532
+ "Content-Type": "application/json",
3533
+ Authorization: `Bearer ${apiKey}`
3534
+ };
3535
+ if (options.appUrl !== void 0) {
3536
+ headers["HTTP-Referer"] = options.appUrl;
3537
+ }
3538
+ if (options.appName !== void 0) {
3539
+ headers["X-Title"] = options.appName;
3540
+ }
3541
+ return headers;
3542
+ }
3543
+ async function requestOpenRouterChatCompletion(options, input) {
3544
+ const apiKey = resolveProviderApiKey({
3545
+ providerLabel: "OpenRouter",
3546
+ adapterFunctionName: "openrouter()",
3547
+ baseEnvVarName: "OPENROUTER_API_KEY",
3548
+ apiKey: options.apiKey,
3549
+ apiKeyName: options.apiKeyName
3550
+ });
3551
+ const fetchImplementation = options.fetchImplementation ?? fetch;
3552
+ const baseUrl = options.baseURL ?? defaultOpenRouterBaseUrl;
3553
+ const requestBody = mergeProviderRequestExtraBody(
3554
+ buildOpenAiRequestBody(options.model, input),
3555
+ options.extraBody,
3556
+ input.extraBody
3557
+ );
3558
+ const startedAt = Date.now();
3559
+ const response = await executeAbortableProviderFetch(
3560
+ "OpenRouter",
3561
+ async () => await fetchImplementation(`${baseUrl}/chat/completions`, {
3562
+ method: "POST",
3563
+ headers: buildOpenRouterRequestHeaders(options, apiKey),
3564
+ body: JSON.stringify(requestBody),
3565
+ signal: input.abortSignal
3566
+ })
3567
+ );
3568
+ const latencyMs = Date.now() - startedAt;
3569
+ if (!response.ok) {
3570
+ throw buildProviderStatusError("OpenRouter", response.status);
3571
+ }
3572
+ const parseResult = OpenRouterChatCompletionResponseSchema.safeParse(
3573
+ await response.json()
3574
+ );
3575
+ if (!parseResult.success) {
3576
+ throw buildProviderInvalidResponseError("OpenRouter", "chat completion");
3577
+ }
3578
+ return mapOpenRouterResponseToChatCompletionResult(
3579
+ parseResult.data,
3580
+ latencyMs
3581
+ );
3582
+ }
3583
+ function mapOpenRouterResponseToChatCompletionResult(response, latencyMs) {
3584
+ const toolCalls = extractOpenRouterToolCalls(response);
3585
+ const result = {
3586
+ content: extractOpenRouterMessageContent(response, toolCalls.length > 0),
3587
+ tokenUsage: mapOpenRouterUsageToTokenUsage(response),
3588
+ latencyMs
3589
+ };
3590
+ const finishReason = response.choices[0]?.finish_reason;
3591
+ if (toolCalls.length > 0) {
3592
+ result.toolCalls = toolCalls;
3593
+ }
3594
+ if (finishReason !== null && finishReason !== void 0) {
3595
+ result.stopReason = normalizeOpenRouterFinishReason(finishReason);
3596
+ }
3597
+ return result;
3598
+ }
3599
+ function extractOpenRouterToolCalls(response) {
3600
+ const responseToolCalls = response.choices[0]?.message.tool_calls ?? [];
3601
+ return responseToolCalls.map((responseToolCall) => ({
3602
+ id: responseToolCall.id,
3603
+ toolName: responseToolCall.function.name,
3604
+ input: parseOpenRouterToolArgumentsJson(
3605
+ responseToolCall.function.arguments
3606
+ )
3607
+ }));
3608
+ }
3609
+ function parseOpenRouterToolArgumentsJson(argumentsJsonText) {
3610
+ if (argumentsJsonText.trim().length === 0) {
3611
+ return {};
3612
+ }
3613
+ try {
3614
+ return import_json10.JsonValueSchema.parse(JSON.parse(argumentsJsonText));
3615
+ } catch (error) {
3616
+ throw new import_errors37.AppError({
3617
+ code: import_errors36.AppErrorCode.ValidationFailed,
3618
+ message: "OpenRouter tool call arguments were not valid JSON",
3619
+ statusCode: 502,
3620
+ cause: error instanceof Error ? error : void 0
3621
+ });
3622
+ }
3623
+ }
3624
+ function normalizeOpenRouterFinishReason(finishReason) {
3625
+ if (finishReason === "tool_calls") {
3626
+ return "tool_use";
3627
+ }
3628
+ if (finishReason === "length") {
3629
+ return "max_tokens";
3630
+ }
3631
+ return "end_turn";
3632
+ }
3633
+ function extractOpenRouterMessageContent(response, allowEmptyContent) {
3634
+ const content = response.choices[0]?.message.content;
3635
+ if (content === void 0 || content === null) {
3636
+ if (allowEmptyContent) {
3637
+ return "";
3638
+ }
3639
+ throw new import_errors37.AppError({
3640
+ code: import_errors36.AppErrorCode.ValidationFailed,
3641
+ message: "OpenRouter response contained no message content",
3642
+ statusCode: 502
3643
+ });
3644
+ }
3645
+ return content;
3646
+ }
3647
+ function mapOpenRouterUsageToTokenUsage(response) {
3648
+ return {
3649
+ inputTokens: response.usage.prompt_tokens,
3650
+ outputTokens: response.usage.completion_tokens,
3651
+ totalTokens: response.usage.total_tokens
3652
+ };
3653
+ }
3654
+
3655
+ // src/cloudflare-workers-ai-adapter.ts
3656
+ var import_zod13 = require("zod");
3657
+ var import_errors38 = require("@assemble-dev/shared-types/errors");
3658
+ var import_json11 = require("@assemble-dev/shared-types/json");
3659
+ var import_errors39 = require("@assemble-dev/shared-utils/errors");
3660
+ var defaultCloudflareApiBaseUrl = "https://api.cloudflare.com/client/v4";
3661
+ var cloudflareAccountIdEnvVarName = "CLOUDFLARE_ACCOUNT_ID";
3662
+ var CloudflareWorkersAiResponseToolCallSchema = import_zod13.z.object({
3663
+ id: import_zod13.z.string(),
3664
+ function: import_zod13.z.object({
3665
+ name: import_zod13.z.string(),
3666
+ arguments: import_zod13.z.string()
3667
+ })
3668
+ });
3669
+ var CloudflareWorkersAiChatCompletionResponseSchema = import_zod13.z.object({
3670
+ choices: import_zod13.z.array(
3671
+ import_zod13.z.object({
3672
+ message: import_zod13.z.object({
3673
+ content: import_zod13.z.string().nullable(),
3674
+ tool_calls: import_zod13.z.array(CloudflareWorkersAiResponseToolCallSchema).optional()
3675
+ }),
3676
+ finish_reason: import_zod13.z.string().nullable().optional()
3677
+ })
3678
+ ).min(1),
3679
+ usage: import_zod13.z.object({
3680
+ prompt_tokens: import_zod13.z.number().int().nonnegative(),
3681
+ completion_tokens: import_zod13.z.number().int().nonnegative(),
3682
+ total_tokens: import_zod13.z.number().int().nonnegative()
3683
+ }).optional()
3684
+ });
3685
+ function cloudflareWorkersAi(options) {
3686
+ return {
3687
+ name: `cloudflare-workers-ai:${options.model}`,
3688
+ async generateChatCompletion(request) {
3689
+ return await executeCloudflareWorkersAiAdapterCall(options, {
3690
+ messages: request.messages,
3691
+ temperature: request.temperature ?? options.temperature,
3692
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3693
+ tools: request.tools,
3694
+ toolChoice: request.toolChoice,
3695
+ abortSignal: request.abortSignal,
3696
+ extraBody: request.extraBody
3697
+ });
3698
+ },
3699
+ async generateStructuredOutput(request) {
3700
+ const completion = await executeCloudflareWorkersAiAdapterCall(options, {
3701
+ ...resolveOpenAiStructuredOutputMode(request.messages, request.schema),
3702
+ temperature: request.temperature ?? options.temperature,
3703
+ maxOutputTokens: request.maxOutputTokens ?? options.maxOutputTokens,
3704
+ abortSignal: request.abortSignal
3705
+ });
3706
+ return {
3707
+ value: parseStructuredJsonText(completion.content, request.schema),
3708
+ tokenUsage: completion.tokenUsage,
3709
+ latencyMs: completion.latencyMs
3710
+ };
3711
+ }
3712
+ };
3713
+ }
3714
+ function buildCloudflareWorkersAiChatCompletionsUrl(options) {
3715
+ if (options.baseURL !== void 0 && options.baseURL.length > 0) {
3716
+ return `${options.baseURL.replace(/\/+$/, "")}/chat/completions`;
3717
+ }
3718
+ const accountId = resolveCloudflareWorkersAiAccountId(options);
3719
+ return `${defaultCloudflareApiBaseUrl}/accounts/${encodeURIComponent(accountId)}/ai/v1/chat/completions`;
3720
+ }
3721
+ function resolveCloudflareWorkersAiAccountId(options) {
3722
+ if (options.accountId !== void 0 && options.accountId.length > 0) {
3723
+ return options.accountId;
3724
+ }
3725
+ const envAccountId = readEnvironmentVariable2(cloudflareAccountIdEnvVarName);
3726
+ if (envAccountId !== void 0 && envAccountId.length > 0) {
3727
+ return envAccountId;
3728
+ }
3729
+ throw new import_errors39.AppError({
3730
+ code: import_errors38.AppErrorCode.BadRequest,
3731
+ message: "Cloudflare Workers AI account is missing. Pass accountId or baseURL to cloudflareWorkersAi() or set the CLOUDFLARE_ACCOUNT_ID environment variable.",
3732
+ statusCode: 400
3733
+ });
3734
+ }
3735
+ function readEnvironmentVariable2(envVarName) {
3736
+ const parsedEnv = import_zod13.z.object({ [envVarName]: import_zod13.z.string().optional() }).parse(process.env);
3737
+ return parsedEnv[envVarName];
3738
+ }
3739
+ async function executeCloudflareWorkersAiAdapterCall(options, input) {
3740
+ return await executeProviderCallWithModelFallback(
3741
+ options.fallbackModel,
3742
+ async () => await executeProviderCallWithRetry(
3743
+ options.retry,
3744
+ async () => await requestCloudflareWorkersAiChatCompletion(options, input)
3745
+ ),
3746
+ async (fallbackModelId) => await requestCloudflareWorkersAiChatCompletion(
3747
+ { ...options, model: fallbackModelId },
3748
+ input
3749
+ )
3750
+ );
3751
+ }
3752
+ async function requestCloudflareWorkersAiChatCompletion(options, input) {
3753
+ const apiToken = resolveProviderApiKey({
3754
+ providerLabel: "Cloudflare Workers AI",
3755
+ adapterFunctionName: "cloudflareWorkersAi()",
3756
+ baseEnvVarName: "CLOUDFLARE_API_TOKEN",
3757
+ apiKey: options.apiKey,
3758
+ apiKeyName: options.apiKeyName
3759
+ });
3760
+ const fetchImplementation = options.fetchImplementation ?? fetch;
3761
+ const requestUrl = buildCloudflareWorkersAiChatCompletionsUrl(options);
3762
+ const requestBody = mergeProviderRequestExtraBody(
3763
+ buildOpenAiRequestBody(options.model, input),
3764
+ options.extraBody,
3765
+ input.extraBody
3766
+ );
3767
+ const startedAt = Date.now();
3768
+ const response = await executeAbortableProviderFetch(
3769
+ "Cloudflare Workers AI",
3770
+ async () => await fetchImplementation(requestUrl, {
3771
+ method: "POST",
3772
+ headers: {
3773
+ "Content-Type": "application/json",
3774
+ Authorization: `Bearer ${apiToken}`
3775
+ },
3776
+ body: JSON.stringify(requestBody),
3777
+ signal: input.abortSignal
3778
+ })
3779
+ );
3780
+ const latencyMs = Date.now() - startedAt;
3781
+ if (!response.ok) {
3782
+ throw buildProviderStatusError("Cloudflare Workers AI", response.status);
3783
+ }
3784
+ const parseResult = CloudflareWorkersAiChatCompletionResponseSchema.safeParse(
3785
+ await response.json()
3786
+ );
3787
+ if (!parseResult.success) {
3788
+ throw buildProviderInvalidResponseError(
3789
+ "Cloudflare Workers AI",
3790
+ "chat completion"
3791
+ );
3792
+ }
3793
+ return mapCloudflareWorkersAiResponseToChatCompletionResult(
3794
+ parseResult.data,
3795
+ latencyMs
3796
+ );
3797
+ }
3798
+ function mapCloudflareWorkersAiResponseToChatCompletionResult(response, latencyMs) {
3799
+ const toolCalls = extractCloudflareWorkersAiToolCalls(response);
3800
+ const result = {
3801
+ content: extractCloudflareWorkersAiMessageContent(
3802
+ response,
3803
+ toolCalls.length > 0
3804
+ ),
3805
+ tokenUsage: mapCloudflareWorkersAiUsageToTokenUsage(response),
3806
+ latencyMs
3807
+ };
3808
+ const finishReason = response.choices[0]?.finish_reason;
3809
+ if (toolCalls.length > 0) {
3810
+ result.toolCalls = toolCalls;
3811
+ }
3812
+ if (finishReason !== null && finishReason !== void 0) {
3813
+ result.stopReason = normalizeCloudflareWorkersAiFinishReason(finishReason);
3814
+ }
3815
+ return result;
3816
+ }
3817
+ function extractCloudflareWorkersAiToolCalls(response) {
3818
+ const responseToolCalls = response.choices[0]?.message.tool_calls ?? [];
3819
+ return responseToolCalls.map((responseToolCall) => ({
3820
+ id: responseToolCall.id,
3821
+ toolName: responseToolCall.function.name,
3822
+ input: parseCloudflareWorkersAiToolArgumentsJson(
3823
+ responseToolCall.function.arguments
3824
+ )
3825
+ }));
3826
+ }
3827
+ function parseCloudflareWorkersAiToolArgumentsJson(argumentsJsonText) {
3828
+ if (argumentsJsonText.trim().length === 0) {
3829
+ return {};
3830
+ }
3831
+ try {
3832
+ return import_json11.JsonValueSchema.parse(JSON.parse(argumentsJsonText));
3833
+ } catch (error) {
3834
+ throw new import_errors39.AppError({
3835
+ code: import_errors38.AppErrorCode.ValidationFailed,
3836
+ message: "Cloudflare Workers AI tool call arguments were not valid JSON",
3837
+ statusCode: 502,
3838
+ cause: error instanceof Error ? error : void 0
3839
+ });
3840
+ }
3841
+ }
3842
+ function normalizeCloudflareWorkersAiFinishReason(finishReason) {
3843
+ if (finishReason === "tool_calls") {
3844
+ return "tool_use";
3845
+ }
3846
+ if (finishReason === "length") {
3847
+ return "max_tokens";
3848
+ }
3849
+ return "end_turn";
3850
+ }
3851
+ function extractCloudflareWorkersAiMessageContent(response, allowEmptyContent) {
3852
+ const content = response.choices[0]?.message.content;
3853
+ if (content === void 0 || content === null) {
3854
+ if (allowEmptyContent) {
3855
+ return "";
3856
+ }
3857
+ throw new import_errors39.AppError({
3858
+ code: import_errors38.AppErrorCode.ValidationFailed,
3859
+ message: "Cloudflare Workers AI response contained no message content",
3860
+ statusCode: 502
3861
+ });
3862
+ }
3863
+ return content;
3864
+ }
3865
+ function mapCloudflareWorkersAiUsageToTokenUsage(response) {
3866
+ if (response.usage === void 0) {
3867
+ return { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
3868
+ }
3869
+ return {
3870
+ inputTokens: response.usage.prompt_tokens,
3871
+ outputTokens: response.usage.completion_tokens,
3872
+ totalTokens: response.usage.total_tokens
3873
+ };
3874
+ }
3352
3875
  // Annotate the CommonJS export names for ESM import in node:
3353
3876
  0 && (module.exports = {
3354
3877
  AnthropicMessagesResponseSchema,
@@ -3375,11 +3898,13 @@ ${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
3375
3898
  buildAzureOpenAiChatRequestBody,
3376
3899
  buildBedrockInvokeModelRequestBody,
3377
3900
  buildBedrockInvokeModelUrl,
3901
+ buildCloudflareWorkersAiChatCompletionsUrl,
3378
3902
  buildGeminiResponseSchema,
3379
3903
  buildOpenAiRequestBody,
3380
3904
  buildOpenAiStructuredOutputJsonSchema,
3381
3905
  buildProviderCanceledError,
3382
3906
  calculateProviderRetryDelayMs,
3907
+ cloudflareWorkersAi,
3383
3908
  convertZodSchemaToJsonSchema,
3384
3909
  createAnthropicBatchClient,
3385
3910
  defaultAnthropicBaseUrl,
@@ -3388,8 +3913,10 @@ ${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
3388
3913
  defaultAnthropicMaxOutputTokens,
3389
3914
  defaultAzureOpenAiApiVersion,
3390
3915
  defaultBedrockMaxOutputTokens,
3916
+ defaultCloudflareApiBaseUrl,
3391
3917
  defaultGeminiBaseUrl,
3392
3918
  defaultOpenAiBaseUrl,
3919
+ defaultOpenRouterBaseUrl,
3393
3920
  defaultProviderRetryInitialDelayMs,
3394
3921
  defaultProviderRetryMaxAttempts,
3395
3922
  defaultProviderRetryMaxDelayMs,
@@ -3403,7 +3930,9 @@ ${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
3403
3930
  extractMessageText,
3404
3931
  gemini,
3405
3932
  isAbortError,
3933
+ isOpenAiReasoningModel,
3406
3934
  isRetryableProviderAppError,
3935
+ isZodV4Schema,
3407
3936
  joinSystemMessageText,
3408
3937
  mapAnthropicResponseToChatCompletionResult,
3409
3938
  mapAnthropicUsageToTokenUsage,
@@ -3421,18 +3950,23 @@ ${JSON.stringify(convertZodSchemaToJsonSchema(schema))}`
3421
3950
  normalizeAzureOpenAiFinishReason,
3422
3951
  openAiStructuredOutputSchemaName,
3423
3952
  openai,
3953
+ openrouter,
3424
3954
  parseAnthropicBatchStructuredContent,
3425
3955
  parseAnthropicStreamEvents,
3426
3956
  parseAnthropicToolInputJson,
3427
3957
  parseAzureOpenAiStreamEvents,
3428
3958
  parseAzureOpenAiToolArgumentsJson,
3959
+ prependStructuredOutputJsonOnlySystemMessage,
3429
3960
  resolveAnthropicPromptCachingSettings,
3430
3961
  resolveBedrockCredentials,
3962
+ resolveOpenAiStructuredOutputMode,
3431
3963
  resolveProviderApiKey,
3432
3964
  sanitizeJsonSchemaForAnthropicStructuredOutput,
3433
3965
  sanitizeJsonSchemaForGeminiResponseSchema,
3434
3966
  sanitizeJsonSchemaForOpenAiStructuredOutput,
3435
3967
  signAwsRequestWithSigV4,
3436
3968
  streamAnthropicChatCompletion,
3437
- streamAzureOpenAiChatCompletion
3969
+ streamAzureOpenAiChatCompletion,
3970
+ structuredOutputJsonOnlySystemInstruction,
3971
+ structuredOutputJsonOnlySystemMessage
3438
3972
  });