@velum-labs/routekit-gateway 0.9.2 → 0.9.3

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/router.js CHANGED
@@ -367,6 +367,9 @@ export class CatalogBackend {
367
367
  nativeBody.reasoning_effort =
368
368
  selection.effort;
369
369
  }
370
+ else {
371
+ delete nativeBody.reasoning_effort;
372
+ }
370
373
  }
371
374
  return entry.source.chat(nativeBody, signal, {
372
375
  ...options,
@@ -437,6 +440,13 @@ export class CatalogBackend {
437
440
  return selection;
438
441
  }
439
442
  const capability = entry.reasoning;
443
+ if (selection.mode === "effort" &&
444
+ selection.effort === "none" &&
445
+ (capability === undefined ||
446
+ capability.status === "unknown" ||
447
+ capability.status === "unsupported")) {
448
+ return { mode: "disabled" };
449
+ }
440
450
  if (capability === undefined || capability.status === "unknown") {
441
451
  return `model "${entry.publicId}" has no discovered reasoning controls`;
442
452
  }
package/dist/server.js CHANGED
@@ -247,6 +247,7 @@ export async function startGateway(options) {
247
247
  : base.data ?? [];
248
248
  writeJson(res, 200, {
249
249
  object: "list",
250
+ default_model: backend.defaultModel,
250
251
  data,
251
252
  models: codexPickerModels(backend, base.data ?? [], merged.models, codexProviderRelay === undefined)
252
253
  });
@@ -263,6 +264,7 @@ export async function startGateway(options) {
263
264
  writeJson(res, 200, {
264
265
  ...modelPayload,
265
266
  object: typeof modelPayload.object === "string" ? modelPayload.object : "list",
267
+ default_model: backend.defaultModel,
266
268
  data,
267
269
  models: codexPickerModels(backend, data.flatMap((entry) => typeof entry.id === "string"
268
270
  ? [{ ...entry, id: entry.id }]
@@ -333,12 +333,16 @@ test("forceModel overrides the requested model on every upstream call", async ()
333
333
  test("lists models from the backend", async () => {
334
334
  const mock = await startMock();
335
335
  const gateway = await startGateway({
336
- backend: new OpenAiBackend({ baseUrl: `${mock.url}/v1` })
336
+ backend: new OpenAiBackend({
337
+ baseUrl: `${mock.url}/v1`,
338
+ defaultModel: "mlx-default"
339
+ })
337
340
  });
338
341
  try {
339
342
  const response = await fetch(`${gateway.url()}/v1/models`);
340
343
  assert.equal(response.status, 200);
341
344
  const json = (await response.json());
345
+ assert.equal(json.default_model, "mlx-default");
342
346
  assert.equal(json.data.length, 1);
343
347
  }
344
348
  finally {
@@ -657,6 +657,7 @@ test("Codex picker aliases use the canonical catalog and pooled native relay", a
657
657
  const catalogResponse = await fetch(`${gateway.url()}/v1/models?client_version=1.0.0`);
658
658
  assert.equal(catalogResponse.headers.get("etag"), null, "a projected managed catalog must not reuse the upstream ETag");
659
659
  const catalog = (await catalogResponse.json());
660
+ assert.equal(catalog.default_model, "codex/gpt-5.5");
660
661
  assert.deepEqual(catalog.data.map((model) => model.id), ["codex/gpt-5.5", "claude-code/claude-sonnet-4-6"]);
661
662
  assert.deepEqual(catalog.models.map(({ slug, display_name }) => [slug, display_name]), [
662
663
  ["gpt-5.5", "GPT-5.5"],
@@ -693,11 +694,13 @@ test("Codex picker aliases use the canonical catalog and pooled native relay", a
693
694
  body: JSON.stringify({
694
695
  model: "claude-code/claude-sonnet-4-6",
695
696
  instructions: "You are Codex, a coding agent based on GPT-5.",
696
- input: "hi"
697
+ input: "hi",
698
+ reasoning: { effort: "none" }
697
699
  })
698
700
  });
699
701
  assert.equal(foreign.status, 200);
700
702
  assert.deepEqual(sourceCalls, ["claude-sonnet-4-6"]);
703
+ assert.equal(sourceBodies[0]?.reasoning_effort, undefined);
701
704
  assert.ok(!JSON.stringify(sourceBodies[0]?.messages).includes("based on GPT-5"), "a stale startup-model identity must not cross into a foreign provider");
702
705
  const unknown = await fetch(`${gateway.url()}/v1/responses`, {
703
706
  method: "POST",
@@ -268,6 +268,67 @@ test("catalog applies configured opaque efforts and rejects unavailable values b
268
268
  assert.equal(calls.length, 1);
269
269
  assert.equal(backend.reasoningCapabilities("openai/opaque")?.provenance, "config");
270
270
  });
271
+ test("catalog treats Codex none as disabled only for models without reasoning controls", async () => {
272
+ const exercise = async (reasoning, effort) => {
273
+ const bodies = [];
274
+ const backend = await CatalogBackend.create({
275
+ config: {
276
+ providers: { openai: {} },
277
+ defaultModel: "openai/model"
278
+ },
279
+ sources: {
280
+ openai: {
281
+ sourceId: "openai",
282
+ async discoverModels() {
283
+ return [{ id: "model", ...(reasoning !== undefined ? { reasoning } : {}) }];
284
+ },
285
+ async chat(body) {
286
+ bodies.push(body);
287
+ return Response.json({});
288
+ },
289
+ async embeddings() {
290
+ return Response.json({});
291
+ }
292
+ }
293
+ }
294
+ });
295
+ return {
296
+ response: await backend.chat({
297
+ model: "openai/model",
298
+ reasoning_effort: effort,
299
+ messages: []
300
+ }),
301
+ bodies
302
+ };
303
+ };
304
+ for (const reasoning of [
305
+ undefined,
306
+ { status: "unknown", provenance: "unknown" },
307
+ { status: "unsupported", provenance: "provider" }
308
+ ]) {
309
+ const normalized = await exercise(reasoning, "none");
310
+ assert.equal(normalized.response.status, 200);
311
+ assert.equal(normalized.bodies.length, 1);
312
+ assert.equal(normalized.bodies[0]?.reasoning_effort, undefined);
313
+ }
314
+ const undiscoveredEffort = await exercise(undefined, "medium");
315
+ assert.equal(undiscoveredEffort.response.status, 400);
316
+ assert.equal(undiscoveredEffort.bodies.length, 0);
317
+ const advertisedNone = await exercise({
318
+ status: "supported",
319
+ efforts: [{ id: "none" }, { id: "high" }],
320
+ provenance: "provider"
321
+ }, "none");
322
+ assert.equal(advertisedNone.response.status, 200);
323
+ assert.equal(advertisedNone.bodies[0]?.reasoning_effort, "none");
324
+ const unsupportedNone = await exercise({
325
+ status: "supported",
326
+ efforts: [{ id: "low" }, { id: "high" }],
327
+ provenance: "provider"
328
+ }, "none");
329
+ assert.equal(unsupportedNone.response.status, 400);
330
+ assert.equal(unsupportedNone.bodies.length, 0);
331
+ });
271
332
  test("unknown models never fall through to the default source", async () => {
272
333
  const backend = await CatalogBackend.create({
273
334
  config: { providers: { openai: {} } },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@velum-labs/routekit-gateway",
3
3
  "private": false,
4
- "version": "0.9.2",
4
+ "version": "0.9.3",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/velum-labs/handoffkit.git",
@@ -27,10 +27,10 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "zod": "4.4.3",
30
- "@velum-labs/routekit-registry": "0.9.2",
31
- "@velum-labs/routekit-contracts": "0.9.2",
32
- "@velum-labs/routekit-tracing": "0.9.2",
33
- "@velum-labs/routekit-runtime": "0.9.2"
30
+ "@velum-labs/routekit-contracts": "0.9.3",
31
+ "@velum-labs/routekit-registry": "0.9.3",
32
+ "@velum-labs/routekit-tracing": "0.9.3",
33
+ "@velum-labs/routekit-runtime": "0.9.3"
34
34
  },
35
35
  "keywords": [
36
36
  "routekit",