@velum-labs/routekit-gateway 0.16.8 → 0.17.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/server.js +29 -6
- package/dist/test/anthropic.test.js +54 -0
- package/package.json +5 -5
package/dist/server.js
CHANGED
|
@@ -79,6 +79,23 @@ function writeClaudeSelectionError(res, selection) {
|
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Recent Claude Code versions send adaptive thinking and an effort on every
|
|
84
|
+
* turn, including for picker entries that advertise no reasoning controls.
|
|
85
|
+
* The picker is authoritative here: forwarding that implicit client default
|
|
86
|
+
* would make the router reject an otherwise valid model before it reaches its
|
|
87
|
+
* provider. Keep explicit RouteKit effort variants strict, but remove the
|
|
88
|
+
* unrepresentable native default for a base, non-reasoning route.
|
|
89
|
+
*/
|
|
90
|
+
function withoutUnsupportedClaudeReasoning(body) {
|
|
91
|
+
const { thinking: _thinking, output_config: outputConfig, ...rest } = body;
|
|
92
|
+
if (outputConfig === undefined || outputConfig === null)
|
|
93
|
+
return rest;
|
|
94
|
+
const { effort: _effort, ...remainingOutputConfig } = outputConfig;
|
|
95
|
+
return Object.keys(remainingOutputConfig).length === 0
|
|
96
|
+
? rest
|
|
97
|
+
: { ...rest, output_config: remainingOutputConfig };
|
|
98
|
+
}
|
|
82
99
|
function resolveNativeModelRoute(backend, provider, requested) {
|
|
83
100
|
if (backend.resolveModelRoute === undefined)
|
|
84
101
|
return undefined;
|
|
@@ -529,13 +546,19 @@ export async function startGateway(options) {
|
|
|
529
546
|
// still records attribution and provenance for the rejected request.
|
|
530
547
|
const route = backend.resolveModelRoute?.(resolvedModel, "claude-code");
|
|
531
548
|
const canonicalModel = route?.publicId ?? resolvedModel;
|
|
549
|
+
const normalizedRawBody = selection.status === "resolved" &&
|
|
550
|
+
selection.selection.mode === "auto" &&
|
|
551
|
+
route !== undefined &&
|
|
552
|
+
route?.reasoning?.status !== "supported"
|
|
553
|
+
? withoutUnsupportedClaudeReasoning(rawBody)
|
|
554
|
+
: rawBody;
|
|
532
555
|
const selectedBody = selection.status === "resolved"
|
|
533
|
-
? withClaudeReasoningSelection(canonicalModel ===
|
|
534
|
-
?
|
|
535
|
-
: withModel(
|
|
536
|
-
: canonicalModel ===
|
|
537
|
-
?
|
|
538
|
-
: withModel(
|
|
556
|
+
? withClaudeReasoningSelection(canonicalModel === normalizedRawBody.model || canonicalModel === undefined
|
|
557
|
+
? normalizedRawBody
|
|
558
|
+
: withModel(normalizedRawBody, canonicalModel), selection.selection)
|
|
559
|
+
: canonicalModel === normalizedRawBody.model || canonicalModel === undefined
|
|
560
|
+
? normalizedRawBody
|
|
561
|
+
: withModel(normalizedRawBody, canonicalModel);
|
|
539
562
|
const body = selectedBody;
|
|
540
563
|
const requestedModel = typeof body.model === "string" ? body.model : undefined;
|
|
541
564
|
if (anthropicRelay !== undefined &&
|
|
@@ -742,6 +742,60 @@ test("estimates tokens and serves Anthropic discovery", async () => {
|
|
|
742
742
|
await mock.close();
|
|
743
743
|
}
|
|
744
744
|
});
|
|
745
|
+
test("Claude's implicit thinking default does not reject a base model without reasoning controls", async () => {
|
|
746
|
+
const calls = [];
|
|
747
|
+
const backend = await CatalogBackend.create({
|
|
748
|
+
config: {
|
|
749
|
+
providers: { openai: {} },
|
|
750
|
+
defaultModel: "openai/mock-model"
|
|
751
|
+
},
|
|
752
|
+
sources: {
|
|
753
|
+
openai: {
|
|
754
|
+
sourceId: "openai",
|
|
755
|
+
async discoverModels() {
|
|
756
|
+
return [{ id: "mock-model" }];
|
|
757
|
+
},
|
|
758
|
+
async chat(body) {
|
|
759
|
+
calls.push(body);
|
|
760
|
+
return Response.json({
|
|
761
|
+
id: "chatcmpl_no_reasoning",
|
|
762
|
+
choices: [
|
|
763
|
+
{
|
|
764
|
+
index: 0,
|
|
765
|
+
message: { role: "assistant", content: "OK" },
|
|
766
|
+
finish_reason: "stop"
|
|
767
|
+
}
|
|
768
|
+
],
|
|
769
|
+
usage: { prompt_tokens: 1, completion_tokens: 1 }
|
|
770
|
+
});
|
|
771
|
+
},
|
|
772
|
+
async embeddings() {
|
|
773
|
+
return Response.json({});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
const gateway = await startGateway({ backend });
|
|
779
|
+
try {
|
|
780
|
+
const response = await fetch(`${gateway.url()}/v1/messages`, {
|
|
781
|
+
method: "POST",
|
|
782
|
+
headers: { "content-type": "application/json", "anthropic-version": "2023-06-01" },
|
|
783
|
+
body: JSON.stringify({
|
|
784
|
+
model: "claude-openai/mock-model",
|
|
785
|
+
max_tokens: 32,
|
|
786
|
+
thinking: { type: "adaptive" },
|
|
787
|
+
output_config: { effort: "high" },
|
|
788
|
+
messages: [{ role: "user", content: "hi" }]
|
|
789
|
+
})
|
|
790
|
+
});
|
|
791
|
+
assert.equal(response.status, 200);
|
|
792
|
+
assert.equal(calls.length, 1);
|
|
793
|
+
assert.equal(calls[0]?.reasoning_effort, undefined);
|
|
794
|
+
}
|
|
795
|
+
finally {
|
|
796
|
+
await gateway.close();
|
|
797
|
+
}
|
|
798
|
+
});
|
|
745
799
|
test("Claude picker aliases use the canonical catalog and pooled native relay", async () => {
|
|
746
800
|
const sourceCalls = [];
|
|
747
801
|
const source = (sourceId) => ({
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velum-labs/routekit-gateway",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.17.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/velum-labs/routekit.git",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
"@aws-sdk/client-bedrock": "3.1095.0",
|
|
30
30
|
"@aws-sdk/client-bedrock-runtime": "3.1095.0",
|
|
31
31
|
"zod": "4.4.3",
|
|
32
|
-
"@velum-labs/routekit-contracts": "0.
|
|
33
|
-
"@velum-labs/routekit-registry": "0.
|
|
34
|
-
"@velum-labs/routekit-runtime": "0.
|
|
35
|
-
"@velum-labs/routekit-tracing": "0.
|
|
32
|
+
"@velum-labs/routekit-contracts": "0.17.0",
|
|
33
|
+
"@velum-labs/routekit-registry": "0.17.0",
|
|
34
|
+
"@velum-labs/routekit-runtime": "0.17.0",
|
|
35
|
+
"@velum-labs/routekit-tracing": "0.17.0"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
38
|
"routekit",
|