@kortyx/openrouter 0.2.5 → 0.2.6

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
@@ -1,7 +1,7 @@
1
1
  // src/index.ts
2
2
  import {
3
3
  ProviderConfigurationError as ProviderConfigurationError3,
4
- ProviderRequestError as ProviderRequestError2
4
+ ProviderRequestError as ProviderRequestError3
5
5
  } from "@kortyx/core/errors";
6
6
 
7
7
  // src/jev.ts
@@ -5374,13 +5374,27 @@ function createOpenRouterClient(settings, apiKey) {
5374
5374
 
5375
5375
  // src/errors.ts
5376
5376
  import {
5377
+ errorProperty,
5378
+ FAILURE_LIMITS,
5379
+ isControlFlowError,
5377
5380
  normalizeProviderError,
5378
- ProviderConfigurationError
5381
+ ProviderConfigurationError,
5382
+ ProviderRequestError
5379
5383
  } from "@kortyx/core/errors";
5380
5384
  import {
5381
5385
  ProviderConfigurationError as ProviderConfigurationError2,
5382
- ProviderRequestError
5386
+ ProviderRequestError as ProviderRequestError2
5383
5387
  } from "@kortyx/core/errors";
5388
+ var rawEnvelope = (error) => {
5389
+ const raw = errorProperty(error, "rawValue");
5390
+ if (typeof raw !== "string") return raw;
5391
+ if (raw.length > FAILURE_LIMITS.bytes) return void 0;
5392
+ try {
5393
+ return JSON.parse(raw);
5394
+ } catch {
5395
+ return void 0;
5396
+ }
5397
+ };
5384
5398
  var requireApiKey = (apiKey) => {
5385
5399
  if (!apiKey || apiKey.trim().length === 0) {
5386
5400
  throw new ProviderConfigurationError(
@@ -5389,7 +5403,115 @@ var requireApiKey = (apiKey) => {
5389
5403
  }
5390
5404
  return apiKey;
5391
5405
  };
5392
- var toProviderRequestError = (action, error) => normalizeProviderError("openrouter", action, error);
5406
+ var parameterNames = (request) => {
5407
+ const parameters = {
5408
+ temperature: request.temperature,
5409
+ top_p: request.topP,
5410
+ top_k: request.topK,
5411
+ frequency_penalty: request.frequencyPenalty,
5412
+ presence_penalty: request.presencePenalty,
5413
+ repetition_penalty: request.repetitionPenalty,
5414
+ min_p: request.minP,
5415
+ top_a: request.topA,
5416
+ stop: request.stop,
5417
+ seed: request.seed,
5418
+ max_tokens: request.maxTokens,
5419
+ max_completion_tokens: request.maxCompletionTokens,
5420
+ reasoning: request.reasoning,
5421
+ response_format: request.responseFormat,
5422
+ tools: request.tools,
5423
+ tool_choice: request.toolChoice,
5424
+ logprobs: request.logprobs,
5425
+ top_logprobs: request.topLogprobs,
5426
+ logit_bias: request.logitBias,
5427
+ parallel_tool_calls: request.parallelToolCalls,
5428
+ prediction: request.prediction,
5429
+ modalities: request.modalities,
5430
+ image_config: request.imageConfig,
5431
+ reasoning_effort: request.reasoningEffort
5432
+ };
5433
+ return Object.entries(parameters).filter(([, value]) => value !== void 0).map(([name]) => name);
5434
+ };
5435
+ var toProviderRequestError = (action, error, request, context) => {
5436
+ if (request && !isControlFlowError(error)) {
5437
+ const envelope = rawEnvelope(error);
5438
+ const detail = errorProperty(error, "error") ?? errorProperty(envelope, "error") ?? error;
5439
+ const metadata2 = errorProperty(detail, "metadata");
5440
+ const routing = errorProperty(metadata2, "routing_funnel") ?? errorProperty(
5441
+ errorProperty(error, "openrouterMetadata") ?? errorProperty(error, "openrouter_metadata") ?? errorProperty(envelope, "openrouter_metadata"),
5442
+ "routing_funnel"
5443
+ );
5444
+ const failedRoutingStep = errorProperty(routing, "failed_routing_step") ?? errorProperty(metadata2, "failed_routing_step");
5445
+ const rawStatus = errorProperty(error, "statusCode") ?? errorProperty(detail, "code");
5446
+ const status = typeof rawStatus === "number" || typeof rawStatus === "string" ? Number(rawStatus) : void 0;
5447
+ const message = errorProperty(detail, "message") ?? errorProperty(error, "message");
5448
+ const parameterFilter = typeof failedRoutingStep === "string" && failedRoutingStep.trim().toLowerCase() === "filter by parameters";
5449
+ const parameterMessage = typeof message === "string" && /^no endpoints found that can handle the requested parameters\.?$/i.test(
5450
+ message.trim()
5451
+ );
5452
+ if (status === 404 && (parameterFilter || failedRoutingStep === void 0 && parameterMessage)) {
5453
+ const requestedParameters = parameterNames(request);
5454
+ const requireParameters = request.provider?.requireParameters ?? false;
5455
+ const requireParametersSource = context?.requireParametersSource ?? "unknown";
5456
+ const featureNames = /* @__PURE__ */ new Set(["response_format", "tools", "tool_choice"]);
5457
+ const featureParameters = requestedParameters.filter(
5458
+ (name2) => featureNames.has(name2)
5459
+ );
5460
+ const otherParameters = requestedParameters.filter(
5461
+ (name2) => !featureNames.has(name2)
5462
+ );
5463
+ const usesPreset = Boolean(
5464
+ request.model?.includes("@preset/") || request.models?.some((model) => model.includes("@preset/"))
5465
+ );
5466
+ const automaticFeatures = [
5467
+ ...request.tools?.length ? ["tool calling"] : [],
5468
+ ...request.responseFormat?.type === "json_schema" ? ["structured output"] : []
5469
+ ];
5470
+ const origin = requireParametersSource === "automatic" ? `enabled automatically by Kortyx for ${automaticFeatures.join(" and ")}` : requireParametersSource === "explicit" ? "configured explicitly by the caller" : requireParametersSource === "default" ? "not explicitly set; provider default" : "setting origin unavailable";
5471
+ const guidance = "Remove unsupported optional parameters only if your application can omit them, or select a compatible model/provider route.";
5472
+ const override = requireParametersSource === "automatic" ? " To override strict parameter routing, set providerOptions.openrouter.provider.requireParameters=false; unsupported controls, including tools/structured output, may then be ignored." : "";
5473
+ const preset = usesPreset ? " Preset settings may also affect routing; this list only covers parameters sent by Kortyx." : "";
5474
+ const diagnostic = `OpenRouter found no endpoint supporting the requested parameters. require_parameters=${requireParameters} (${origin}). Tool/output parameters: ${featureParameters.join(", ") || "none"}. Other requested parameters: ${otherParameters.join(", ") || "none"}. ${guidance}${override}${preset}`;
5475
+ return new ProviderRequestError(
5476
+ typeof message === "string" ? message : diagnostic,
5477
+ {
5478
+ code: "PROVIDER_PARAMETER_UNSUPPORTED",
5479
+ source: "openrouter",
5480
+ operation: action,
5481
+ status: 404,
5482
+ retryable: false,
5483
+ safeMessage: diagnostic,
5484
+ details: {
5485
+ requestedParameters,
5486
+ requireParameters,
5487
+ requireParametersSource,
5488
+ featureParameters,
5489
+ otherParameters,
5490
+ usesPreset,
5491
+ ...parameterFilter ? { failedRoutingStep: "Filter by Parameters" } : {}
5492
+ },
5493
+ cause: error
5494
+ }
5495
+ );
5496
+ }
5497
+ }
5498
+ const name = errorProperty(error, "name");
5499
+ const originalMessage = errorProperty(error, "message");
5500
+ if (name === "ResponseValidationError" || context?.streamStarted && (name === "SDKValidationError" || name === "ZodError")) {
5501
+ return new ProviderRequestError(
5502
+ typeof originalMessage === "string" ? originalMessage : "OpenRouter SDK response validation failed.",
5503
+ {
5504
+ code: "PROVIDER_INVALID_RESPONSE",
5505
+ source: "openrouter",
5506
+ operation: action,
5507
+ retryable: false,
5508
+ safeMessage: "OpenRouter returned a response that did not match its SDK schema. Inspect the original SDK error for details.",
5509
+ cause: error
5510
+ }
5511
+ );
5512
+ }
5513
+ return normalizeProviderError("openrouter", action, error);
5514
+ };
5393
5515
 
5394
5516
  // src/messages.ts
5395
5517
  var isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -5454,9 +5576,10 @@ var toTool = (tool) => ({
5454
5576
  strict: false
5455
5577
  }
5456
5578
  });
5457
- var createChatRequest = (modelId, messages, options, stream) => {
5579
+ var prepareChatRequest = (modelId, messages, options, stream) => {
5458
5580
  const extra = getOpenRouterOptions(options);
5459
5581
  const needsRequiredParameters = Boolean(options.tools?.length) || options.responseFormat?.type === "json" && options.responseFormat.schema !== void 0;
5582
+ const requireParametersSource = extra.provider?.requireParameters !== void 0 ? "explicit" : needsRequiredParameters ? "automatic" : "default";
5460
5583
  const provider = {
5461
5584
  ...extra.provider,
5462
5585
  ...needsRequiredParameters && extra.provider?.requireParameters === void 0 ? { requireParameters: true } : {}
@@ -5468,7 +5591,7 @@ var createChatRequest = (modelId, messages, options, stream) => {
5468
5591
  } : {},
5469
5592
  ...options.reasoning.includeThoughts ? { summary: "auto" } : {}
5470
5593
  } : extra.reasoning;
5471
- return {
5594
+ const request = {
5472
5595
  ...extra,
5473
5596
  model: modelId,
5474
5597
  messages: toMessages(messages),
@@ -5490,6 +5613,7 @@ var createChatRequest = (modelId, messages, options, stream) => {
5490
5613
  ...options.tools?.length ? { tools: options.tools.map(toTool) } : {},
5491
5614
  ...Object.keys(provider).length > 0 ? { provider } : {}
5492
5615
  };
5616
+ return { request, context: { requireParametersSource } };
5493
5617
  };
5494
5618
 
5495
5619
  // src/models.ts
@@ -5589,7 +5713,7 @@ var toOutput = (questions, response) => {
5589
5713
  for (const [name, question] of Object.entries(questions)) {
5590
5714
  const answer = response.answers[name];
5591
5715
  if (!answer || answer.type !== question.type)
5592
- throw new ProviderRequestError(
5716
+ throw new ProviderRequestError2(
5593
5717
  `OpenRouter returned no compatible Jev answer for question "${name}".`
5594
5718
  );
5595
5719
  switch (answer.type) {
@@ -5603,7 +5727,7 @@ var toOutput = (questions, response) => {
5603
5727
  output[name] = answer.noul;
5604
5728
  break;
5605
5729
  default:
5606
- throw new ProviderRequestError(
5730
+ throw new ProviderRequestError2(
5607
5731
  `OpenRouter returned an unsupported Jev answer for question "${name}".`
5608
5732
  );
5609
5733
  }
@@ -5779,7 +5903,7 @@ var warningsFor = (options) => {
5779
5903
  var resultFrom = (modelId, response, warnings) => {
5780
5904
  const choice = response.choices[0];
5781
5905
  if (!choice)
5782
- throw new ProviderRequestError(
5906
+ throw new ProviderRequestError2(
5783
5907
  "OpenRouter returned a chat completion without a choice."
5784
5908
  );
5785
5909
  const toolCalls = mapToolCalls(choice.message.toolCalls);
@@ -5818,18 +5942,23 @@ var createChatModel = (modelId, settings, options) => {
5818
5942
  return {
5819
5943
  supportsToolStreaming: true,
5820
5944
  async invoke(messages) {
5945
+ let request;
5946
+ let context;
5821
5947
  try {
5822
- const response = await getClient().chat(
5823
- createChatRequest(modelId, messages, options, false),
5824
- options.abortSignal
5825
- );
5948
+ ({ request, context } = prepareChatRequest(
5949
+ modelId,
5950
+ messages,
5951
+ options,
5952
+ false
5953
+ ));
5954
+ const response = await getClient().chat(request, options.abortSignal);
5826
5955
  if (isAsyncIterable(response))
5827
- throw new ProviderRequestError(
5956
+ throw new ProviderRequestError2(
5828
5957
  "OpenRouter unexpectedly returned a stream for a non-streaming request."
5829
5958
  );
5830
5959
  return resultFrom(modelId, response, warnings);
5831
5960
  } catch (error) {
5832
- throw toProviderRequestError("invoke content", error);
5961
+ throw toProviderRequestError("invoke content", error, request, context);
5833
5962
  }
5834
5963
  },
5835
5964
  async *stream(messages) {
@@ -5850,15 +5979,21 @@ var createChatModel = (modelId, settings, options) => {
5850
5979
  }
5851
5980
  let lastChunk;
5852
5981
  let partialUsage;
5982
+ let request;
5983
+ let context;
5853
5984
  try {
5854
- const response = await getClient().chat(
5855
- createChatRequest(modelId, messages, options, true),
5856
- options.abortSignal
5857
- );
5985
+ ({ request, context } = prepareChatRequest(
5986
+ modelId,
5987
+ messages,
5988
+ options,
5989
+ true
5990
+ ));
5991
+ const response = await getClient().chat(request, options.abortSignal);
5858
5992
  if (!isAsyncIterable(response))
5859
- throw new ProviderRequestError(
5993
+ throw new ProviderRequestError2(
5860
5994
  "OpenRouter unexpectedly returned a non-streaming response for a streaming request."
5861
5995
  );
5996
+ context.streamStarted = true;
5862
5997
  const calls = /* @__PURE__ */ new Map();
5863
5998
  const reasoningDetails = [];
5864
5999
  let finishReason;
@@ -5866,9 +6001,12 @@ var createChatModel = (modelId, settings, options) => {
5866
6001
  lastChunk = chunk;
5867
6002
  partialUsage = mapUsage2(chunk.usage) ?? partialUsage;
5868
6003
  if (chunk.error)
5869
- throw new ProviderRequestError(chunk.error.message, {
5870
- ...chunk.error.metadata ? { details: chunk.error.metadata } : {}
5871
- });
6004
+ throw toProviderRequestError(
6005
+ "stream content",
6006
+ chunk.error,
6007
+ request,
6008
+ context
6009
+ );
5872
6010
  for (const choice of chunk.choices) {
5873
6011
  finishReason = choice.finishReason ?? finishReason;
5874
6012
  if (choice.delta.content)
@@ -5895,12 +6033,12 @@ var createChatModel = (modelId, settings, options) => {
5895
6033
  }
5896
6034
  }
5897
6035
  if (!lastChunk || !finishReason)
5898
- throw new ProviderRequestError(
6036
+ throw new ProviderRequestError2(
5899
6037
  "OpenRouter stream ended without a finish reason."
5900
6038
  );
5901
6039
  const toolCalls = calls.size ? [...calls.entries()].sort(([left], [right]) => left - right).map(([, call]) => {
5902
6040
  if (!call.id || !call.name)
5903
- throw new ProviderRequestError(
6041
+ throw new ProviderRequestError2(
5904
6042
  "OpenRouter returned an incomplete streamed tool call."
5905
6043
  );
5906
6044
  return {
@@ -5931,7 +6069,7 @@ var createChatModel = (modelId, settings, options) => {
5931
6069
  yield {
5932
6070
  type: "error",
5933
6071
  error: Object.assign(
5934
- toProviderRequestError("stream content", error),
6072
+ toProviderRequestError("stream content", error, request, context),
5935
6073
  {
5936
6074
  usage: partialUsage
5937
6075
  }
@@ -5980,7 +6118,7 @@ export {
5980
6118
  MODELS,
5981
6119
  PROVIDER_ID,
5982
6120
  ProviderConfigurationError3 as ProviderConfigurationError,
5983
- ProviderRequestError2 as ProviderRequestError,
6121
+ ProviderRequestError3 as ProviderRequestError,
5984
6122
  createOpenRouter,
5985
6123
  createOpenRouter as createProvider,
5986
6124
  jevOutputSchema,