@velum-labs/routekit-gateway 0.18.3 → 0.18.4

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.
@@ -13,6 +13,12 @@ export type BedrockProviderSourceOptions = {
13
13
  };
14
14
  export declare const BEDROCK_OPENAI_ALLOWLIST: readonly ["openai.gpt-5.4", "openai.gpt-5.5", "openai.gpt-5.6-sol", "openai.gpt-5.6-terra", "openai.gpt-5.6-luna"];
15
15
  export declare function isBedrockOpenAiModel(modelId: string): boolean;
16
+ /**
17
+ * Codex advertises `web_search_tool_type: "text_and_image"` and sends
18
+ * `search_content_types` on `{ type: "web_search" }`. Bedrock mantle accepts
19
+ * the tool but rejects that field. Native OpenAI Responses is unchanged.
20
+ */
21
+ export declare function sanitizeBedrockMantleRequestBody(body: unknown): unknown;
16
22
  export declare function toBedrockConverseInput(body: unknown): ConverseCommandInput;
17
23
  export declare function fromBedrockConverseOutput(output: ConverseCommandOutput, model: string): Record<string, unknown>;
18
24
  export declare class BedrockProviderSource implements ProviderSource {
@@ -14,6 +14,29 @@ const BEDROCK_OPENAI_MODEL = /^(?:(?:us|eu|global)\.)?openai\.gpt-/;
14
14
  export function isBedrockOpenAiModel(modelId) {
15
15
  return BEDROCK_OPENAI_MODEL.test(modelId);
16
16
  }
17
+ /**
18
+ * Codex advertises `web_search_tool_type: "text_and_image"` and sends
19
+ * `search_content_types` on `{ type: "web_search" }`. Bedrock mantle accepts
20
+ * the tool but rejects that field. Native OpenAI Responses is unchanged.
21
+ */
22
+ export function sanitizeBedrockMantleRequestBody(body) {
23
+ const payload = record(body);
24
+ if (payload === undefined || !Array.isArray(payload.tools))
25
+ return body;
26
+ let changed = false;
27
+ const tools = payload.tools.map((tool) => {
28
+ const entry = record(tool);
29
+ if (entry === undefined)
30
+ return tool;
31
+ const type = typeof entry.type === "string" ? entry.type : "";
32
+ if (!type.startsWith("web_search") || !("search_content_types" in entry))
33
+ return tool;
34
+ changed = true;
35
+ const { search_content_types: _ignored, ...rest } = entry;
36
+ return rest;
37
+ });
38
+ return changed ? { ...payload, tools } : body;
39
+ }
17
40
  function mantleApiKey(env) {
18
41
  const key = env.AWS_BEARER_TOKEN_BEDROCK ?? env.BEDROCK_API_KEY;
19
42
  return typeof key === "string" && key.length > 0 ? key : undefined;
@@ -588,7 +611,7 @@ export class BedrockProviderSource {
588
611
  const backend = this.#mantleBackend();
589
612
  if (backend === undefined)
590
613
  return this.#missingMantleResponse();
591
- return backend.responses(body, signal, options);
614
+ return backend.responses(sanitizeBedrockMantleRequestBody(body), signal, options);
592
615
  }
593
616
  async chat(body, signal, options) {
594
617
  const requestedModel = record(body)?.model;
@@ -596,7 +619,7 @@ export class BedrockProviderSource {
596
619
  const backend = this.#mantleBackend();
597
620
  if (backend === undefined)
598
621
  return this.#missingMantleResponse();
599
- return backend.chat(body, signal, options);
622
+ return backend.chat(sanitizeBedrockMantleRequestBody(body), signal, options);
600
623
  }
601
624
  let input;
602
625
  try {
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
2
2
  import { test } from "node:test";
3
3
  import { ConverseCommand, ConverseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
4
4
  import { ListFoundationModelsCommand } from "@aws-sdk/client-bedrock";
5
- import { BEDROCK_OPENAI_ALLOWLIST, BedrockProviderSource, isBedrockOpenAiModel, toBedrockConverseInput } from "../bedrock-source.js";
5
+ import { BEDROCK_OPENAI_ALLOWLIST, BedrockProviderSource, isBedrockOpenAiModel, sanitizeBedrockMantleRequestBody, toBedrockConverseInput } from "../bedrock-source.js";
6
6
  test("Bedrock discovery includes active Anthropic foundations and paginated backed profiles", async () => {
7
7
  const commands = [];
8
8
  const source = new BedrockProviderSource({
@@ -501,6 +501,56 @@ test("Bedrock routes OpenAI models through mantle and leaves Anthropic on Conver
501
501
  });
502
502
  assert.equal(rejected.status, 501);
503
503
  });
504
+ test("sanitizeBedrockMantleRequestBody strips search_content_types from web_search only", () => {
505
+ const lookup = {
506
+ type: "function",
507
+ name: "lookup",
508
+ parameters: { type: "object" }
509
+ };
510
+ const body = {
511
+ model: "openai.gpt-5.6-sol",
512
+ tools: [
513
+ { type: "web_search", search_content_types: ["text", "image"], external_web_access: true },
514
+ lookup
515
+ ]
516
+ };
517
+ assert.deepEqual(sanitizeBedrockMantleRequestBody(body), {
518
+ model: "openai.gpt-5.6-sol",
519
+ tools: [{ type: "web_search", external_web_access: true }, lookup]
520
+ });
521
+ const noTools = { model: "openai.gpt-5.6-sol", input: "hi" };
522
+ assert.equal(sanitizeBedrockMantleRequestBody(noTools), noTools);
523
+ const untouched = { model: "openai.gpt-5.6-sol", tools: [{ type: "web_search" }] };
524
+ assert.equal(sanitizeBedrockMantleRequestBody(untouched), untouched);
525
+ });
526
+ test("Bedrock mantle Responses drops Codex search_content_types before egress", async () => {
527
+ const forwarded = [];
528
+ const source = new BedrockProviderSource({
529
+ env: { AWS_BEARER_TOKEN_BEDROCK: "bedrock-key", AWS_REGION: "us-east-1" },
530
+ controlClient: { send: async () => ({}) },
531
+ runtimeClient: { send: async () => ({}) },
532
+ mantleBackend: {
533
+ chat: async () => Response.json({}),
534
+ responses: async (body) => {
535
+ forwarded.push(body);
536
+ return Response.json({ id: "resp" });
537
+ }
538
+ }
539
+ });
540
+ const response = await source.responses({
541
+ model: "openai.gpt-5.6-sol",
542
+ input: "hi",
543
+ tools: [{ type: "web_search", search_content_types: ["text", "image"] }]
544
+ });
545
+ assert.equal(response.status, 200);
546
+ assert.deepEqual(forwarded, [
547
+ {
548
+ model: "openai.gpt-5.6-sol",
549
+ input: "hi",
550
+ tools: [{ type: "web_search" }]
551
+ }
552
+ ]);
553
+ });
504
554
  test("Bedrock OpenAI chat without a mantle key returns 400 and does not call Converse", async () => {
505
555
  const runtimeCalls = [];
506
556
  const source = new BedrockProviderSource({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@velum-labs/routekit-gateway",
3
3
  "private": false,
4
- "version": "0.18.3",
4
+ "version": "0.18.4",
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.18.3",
33
- "@velum-labs/routekit-registry": "0.18.3",
34
- "@velum-labs/routekit-runtime": "0.18.3",
35
- "@velum-labs/routekit-tracing": "0.18.3"
32
+ "@velum-labs/routekit-contracts": "0.18.4",
33
+ "@velum-labs/routekit-registry": "0.18.4",
34
+ "@velum-labs/routekit-runtime": "0.18.4",
35
+ "@velum-labs/routekit-tracing": "0.18.4"
36
36
  },
37
37
  "keywords": [
38
38
  "routekit",