@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/CHANGELOG.md +29 -0
- package/README.md +39 -0
- package/dist/index.cjs +150 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +165 -27
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
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
|
|
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
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
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
|
|
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
|
|
5870
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
6121
|
+
ProviderRequestError3 as ProviderRequestError,
|
|
5984
6122
|
createOpenRouter,
|
|
5985
6123
|
createOpenRouter as createProvider,
|
|
5986
6124
|
jevOutputSchema,
|