anygate 0.5.7 → 0.5.8

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.
@@ -10,7 +10,7 @@ import { join } from "path";
10
10
  // package.json
11
11
  var package_default = {
12
12
  name: "anygate",
13
- version: "0.5.7",
13
+ version: "0.5.8",
14
14
  publishConfig: {
15
15
  access: "public"
16
16
  },
@@ -5081,13 +5081,13 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
5081
5081
  import { randomUUID as randomUUID5 } from "crypto";
5082
5082
 
5083
5083
  // src/gateway/sdk-adapter.ts
5084
- import { streamText, generateText, tool as tool2, jsonSchema as jsonSchema2 } from "ai";
5084
+ import { streamText, generateText, tool as tool3, jsonSchema as jsonSchema3, stepCountIs } from "ai";
5085
5085
 
5086
5086
  // src/agents/shared/tool-search.ts
5087
5087
  var TOOL_SEARCH_TYPE_PREFIX = "tool_search_tool";
5088
- function isToolSearchTool(tool4) {
5089
- if (typeof tool4.type === "string" && tool4.type.startsWith(TOOL_SEARCH_TYPE_PREFIX)) return true;
5090
- const name = tool4.name ?? "";
5088
+ function isToolSearchTool(tool5) {
5089
+ if (typeof tool5.type === "string" && tool5.type.startsWith(TOOL_SEARCH_TYPE_PREFIX)) return true;
5090
+ const name = tool5.name ?? "";
5091
5091
  return name.includes("tool_search") || name === "ToolSearch";
5092
5092
  }
5093
5093
  function extractReferencedToolNames(messages) {
@@ -5126,20 +5126,207 @@ function resolveUpstreamTools(tools, messages) {
5126
5126
  if (!tools?.length) return [];
5127
5127
  const referenced = extractReferencedToolNames(messages);
5128
5128
  const upstream = [];
5129
- for (const tool4 of tools) {
5130
- if (isToolSearchTool(tool4)) {
5131
- upstream.push(tool4);
5129
+ for (const tool5 of tools) {
5130
+ if (isToolSearchTool(tool5)) {
5131
+ upstream.push(tool5);
5132
5132
  continue;
5133
5133
  }
5134
- if (tool4.defer_loading === true) {
5135
- if (referenced.has(tool4.name)) upstream.push(tool4);
5134
+ if (tool5.defer_loading === true) {
5135
+ if (referenced.has(tool5.name)) upstream.push(tool5);
5136
5136
  continue;
5137
5137
  }
5138
- upstream.push(tool4);
5138
+ upstream.push(tool5);
5139
5139
  }
5140
5140
  return upstream;
5141
5141
  }
5142
5142
 
5143
+ // src/gateway/web-search/tool.ts
5144
+ import { tool as tool2, jsonSchema as jsonSchema2 } from "ai";
5145
+
5146
+ // src/gateway/web-search/constants.ts
5147
+ var WEB_SEARCH_ENV = {
5148
+ enabled: "ANYGATE_WEB_SEARCH",
5149
+ provider: "ANYGATE_WEB_SEARCH_PROVIDER",
5150
+ searxngUrl: "ANYGATE_SEARXNG_URL",
5151
+ apiKey: "ANYGATE_SEARCH_API_KEY",
5152
+ maxResults: "ANYGATE_WEB_SEARCH_MAX_RESULTS"
5153
+ };
5154
+ var DEFAULT_MAX_RESULTS = 5;
5155
+ var MAX_WEB_SEARCH_STEPS = 5;
5156
+
5157
+ // src/gateway/web-search/duckduckgo.ts
5158
+ var DDG_HTML_URL = "https://html.duckduckgo.com/html/";
5159
+ function decodeDdgUrl(href) {
5160
+ try {
5161
+ const url = new URL(href, DDG_HTML_URL);
5162
+ const uddg = url.searchParams.get("uddg");
5163
+ if (uddg) return decodeURIComponent(uddg);
5164
+ return url.href;
5165
+ } catch {
5166
+ return href;
5167
+ }
5168
+ }
5169
+ function stripHtml(s) {
5170
+ return s.replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#39;/g, "'").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/\s+/g, " ").trim();
5171
+ }
5172
+ function parseDdgHtml(html, max) {
5173
+ const results = [];
5174
+ const linkRe = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi;
5175
+ let m;
5176
+ while ((m = linkRe.exec(html)) !== null && results.length < max) {
5177
+ const href = decodeDdgUrl(m[1]);
5178
+ const title = stripHtml(m[2]);
5179
+ const after = html.slice(m.index + m[0].length);
5180
+ const snipMatch = after.match(/class="result__snippet"[^>]*>([\s\S]*?)<\/a>/i);
5181
+ const snippet = snipMatch ? stripHtml(snipMatch[1]) : "";
5182
+ if (title && href) results.push({ title, url: href, snippet });
5183
+ }
5184
+ return results;
5185
+ }
5186
+ async function searchDuckDuckGo(query, opts) {
5187
+ const max = opts?.maxResults ?? 5;
5188
+ const body = new URLSearchParams({ q: query }).toString();
5189
+ const res = await fetch(DDG_HTML_URL, {
5190
+ method: "POST",
5191
+ headers: {
5192
+ "Content-Type": "application/x-www-form-urlencoded",
5193
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
5194
+ },
5195
+ body
5196
+ });
5197
+ if (!res.ok) throw new Error(`DuckDuckGo search failed: ${res.status}`);
5198
+ const html = await res.text();
5199
+ return parseDdgHtml(html, max);
5200
+ }
5201
+
5202
+ // src/gateway/web-search/searxng.ts
5203
+ async function searchSearXNG(query, baseUrl, opts) {
5204
+ const max = opts?.maxResults ?? 5;
5205
+ const url = new URL("/search", baseUrl.replace(/\/+$/, ""));
5206
+ url.searchParams.set("format", "json");
5207
+ url.searchParams.set("q", query);
5208
+ const res = await fetch(url, { headers: { Accept: "application/json" } });
5209
+ if (!res.ok) throw new Error(`SearXNG search failed: ${res.status}`);
5210
+ const data = await res.json();
5211
+ return (Array.isArray(data) ? data : []).filter((r) => r.url && r.title).slice(0, max).map((r) => ({ title: r.title ?? "", url: r.url ?? "", snippet: r.content ?? "" }));
5212
+ }
5213
+
5214
+ // src/gateway/web-search/brave.ts
5215
+ async function searchBrave(query, apiKey, opts) {
5216
+ const max = opts?.maxResults ?? 5;
5217
+ const url = new URL("https://api.search.brave.com/res/v1/web/search");
5218
+ url.searchParams.set("q", query);
5219
+ url.searchParams.set("count", String(max));
5220
+ if (opts?.allowedDomains?.length) url.searchParams.set("site", opts.allowedDomains.join(","));
5221
+ const res = await fetch(url, {
5222
+ headers: { Accept: "application/json", "X-Subscription-Token": apiKey }
5223
+ });
5224
+ if (!res.ok) throw new Error(`Brave search failed: ${res.status}`);
5225
+ const data = await res.json();
5226
+ return (data.web?.results ?? []).filter((r) => r.url && r.title).slice(0, max).map((r) => ({ title: r.title ?? "", url: r.url ?? "", snippet: r.description ?? "" }));
5227
+ }
5228
+
5229
+ // src/gateway/web-search/tavily.ts
5230
+ async function searchTavily(query, apiKey, opts) {
5231
+ const max = opts?.maxResults ?? 5;
5232
+ const res = await fetch("https://api.tavily.com/search", {
5233
+ method: "POST",
5234
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
5235
+ body: JSON.stringify({
5236
+ query,
5237
+ max_results: max,
5238
+ include_domains: opts?.allowedDomains,
5239
+ exclude_domains: opts?.blockedDomains
5240
+ })
5241
+ });
5242
+ if (!res.ok) throw new Error(`Tavily search failed: ${res.status}`);
5243
+ const data = await res.json();
5244
+ return (data.results ?? []).filter((r) => r.url && r.title).slice(0, max).map((r) => ({ title: r.title ?? "", url: r.url ?? "", snippet: r.content ?? "" }));
5245
+ }
5246
+
5247
+ // src/gateway/web-search/index.ts
5248
+ var PROVIDERS = ["duckduckgo", "searxng", "brave", "tavily"];
5249
+ function resolveWebSearchConfig(env = process.env) {
5250
+ const enabled = (env[WEB_SEARCH_ENV.enabled] ?? "on").toLowerCase() !== "off";
5251
+ const providerRaw = (env[WEB_SEARCH_ENV.provider] ?? "duckduckgo").toLowerCase();
5252
+ const provider = PROVIDERS.includes(providerRaw) ? providerRaw : "duckduckgo";
5253
+ const maxRaw = parseInt(env[WEB_SEARCH_ENV.maxResults] ?? "", 10);
5254
+ const maxResults = Number.isFinite(maxRaw) && maxRaw > 0 ? maxRaw : DEFAULT_MAX_RESULTS;
5255
+ return {
5256
+ enabled,
5257
+ provider,
5258
+ searxngUrl: env[WEB_SEARCH_ENV.searxngUrl] || void 0,
5259
+ apiKey: env[WEB_SEARCH_ENV.apiKey] || void 0,
5260
+ maxResults
5261
+ };
5262
+ }
5263
+ async function searchWeb(query, opts, config) {
5264
+ const cfg = config ?? resolveWebSearchConfig();
5265
+ if (!cfg.enabled) return [];
5266
+ const searchOpts = { maxResults: cfg.maxResults, ...opts };
5267
+ switch (cfg.provider) {
5268
+ case "searxng":
5269
+ if (!cfg.searxngUrl) throw new Error("ANYGATE_SEARXNG_URL is required for the searxng web search provider");
5270
+ return searchSearXNG(query, cfg.searxngUrl, searchOpts);
5271
+ case "brave":
5272
+ if (!cfg.apiKey) throw new Error("ANYGATE_SEARCH_API_KEY is required for the brave web search provider");
5273
+ return searchBrave(query, cfg.apiKey, searchOpts);
5274
+ case "tavily":
5275
+ if (!cfg.apiKey) throw new Error("ANYGATE_SEARCH_API_KEY is required for the tavily web search provider");
5276
+ return searchTavily(query, cfg.apiKey, searchOpts);
5277
+ case "duckduckgo":
5278
+ default:
5279
+ return searchDuckDuckGo(query, searchOpts);
5280
+ }
5281
+ }
5282
+ function formatSearchResults(results) {
5283
+ if (!results.length) return "No web search results were found.";
5284
+ const lines = results.map((r, i) => `${i + 1}. ${r.title}
5285
+ URL: ${r.url}
5286
+ ${r.snippet}`);
5287
+ return `Web search results:
5288
+ ${lines.join("\n")}`;
5289
+ }
5290
+
5291
+ // src/gateway/web-search/tool.ts
5292
+ var WEB_SEARCH_INPUT_SCHEMA = {
5293
+ type: "object",
5294
+ properties: {
5295
+ query: { type: "string", description: "The search query to use." },
5296
+ allowed_domains: {
5297
+ type: "array",
5298
+ items: { type: "string" },
5299
+ description: "Only include search results from these domains."
5300
+ },
5301
+ blocked_domains: {
5302
+ type: "array",
5303
+ items: { type: "string" },
5304
+ description: "Never include search results from these domains."
5305
+ },
5306
+ max_uses: { type: "integer", description: "Maximum number of searches the model may perform." }
5307
+ },
5308
+ required: ["query"]
5309
+ };
5310
+ function isWebSearchTool(t) {
5311
+ if (typeof t.name === "string" && /web[_-]?search/i.test(t.name)) return true;
5312
+ if (typeof t.type === "string" && t.type.startsWith("web_search")) return true;
5313
+ return false;
5314
+ }
5315
+ function makeWebSearchTool(name) {
5316
+ return tool2({
5317
+ description: "Search the web for current information. Use this whenever the user asks about recent events, current facts, news, or anything that may need up-to-date information beyond your training data.",
5318
+ inputSchema: jsonSchema2(WEB_SEARCH_INPUT_SCHEMA),
5319
+ execute: async (input) => {
5320
+ const params = input;
5321
+ const results = await searchWeb(params.query, {
5322
+ allowedDomains: params.allowed_domains,
5323
+ blockedDomains: params.blocked_domains
5324
+ });
5325
+ return formatSearchResults(results);
5326
+ }
5327
+ });
5328
+ }
5329
+
5143
5330
  // src/gateway/context-fit.ts
5144
5331
  var CHARS_PER_TOKEN = 4;
5145
5332
  var RESERVE_TOKENS = 256;
@@ -5352,8 +5539,13 @@ function translateTools3(anthropicTools) {
5352
5539
  if (!anthropicTools?.length) return void 0;
5353
5540
  const tools = {};
5354
5541
  for (const t of anthropicTools) {
5355
- if (!t.name || !t.input_schema) continue;
5356
- tools[t.name] = tool2({ description: t.description ?? "", inputSchema: jsonSchema2(t.input_schema) });
5542
+ if (!t.name) continue;
5543
+ if (isWebSearchTool(t)) {
5544
+ tools[t.name] = makeWebSearchTool(t.name);
5545
+ continue;
5546
+ }
5547
+ if (!t.input_schema) continue;
5548
+ tools[t.name] = tool3({ description: t.description ?? "", inputSchema: jsonSchema3(t.input_schema) });
5357
5549
  }
5358
5550
  return Object.keys(tools).length ? tools : void 0;
5359
5551
  }
@@ -5398,6 +5590,7 @@ function translateRequest2(body, npm, options) {
5398
5590
  if (options?.maxTools !== void 0 && upstreamTools.length > options.maxTools) {
5399
5591
  upstreamTools = upstreamTools.slice(0, options.maxTools);
5400
5592
  }
5593
+ const webSearchTool = upstreamTools.find((t) => isWebSearchTool(t));
5401
5594
  const effort = anthropicEffortFromRequest(body) ?? options?.defaultEffort;
5402
5595
  let providerOptions = deepMergeProviderOptions(
5403
5596
  thinkingProviderOptions(npm),
@@ -5415,16 +5608,18 @@ function translateRequest2(body, npm, options) {
5415
5608
  toolChoice: translateToolChoice(body.tool_choice),
5416
5609
  maxOutputTokens: options?.openAiOAuth ? void 0 : maxOutput,
5417
5610
  temperature: body.temperature,
5418
- providerOptions
5611
+ providerOptions,
5612
+ webSearchToolName: webSearchTool?.name
5419
5613
  };
5420
5614
  }
5421
- async function writeAnthropicStream(fullStream, modelId, write, log7) {
5615
+ async function writeAnthropicStream(fullStream, modelId, write, log7, hiddenToolName) {
5422
5616
  const messageId = "msg_" + Date.now();
5423
5617
  let blockIndex = -1;
5424
5618
  let started = false;
5425
5619
  let openType = null;
5426
5620
  let pendingThinkingSig;
5427
5621
  const idToBlock = /* @__PURE__ */ new Map();
5622
+ const hiddenIds = /* @__PURE__ */ new Set();
5428
5623
  let finishReason = "end_turn";
5429
5624
  let usage = { input_tokens: 0, output_tokens: 0 };
5430
5625
  const emit = (event, data) => write(sseChunk(event, data));
@@ -5499,6 +5694,10 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5499
5694
  case "text-end":
5500
5695
  break;
5501
5696
  case "tool-input-start": {
5697
+ if (part.toolName && part.toolName === hiddenToolName) {
5698
+ hiddenIds.add(part.id ?? "");
5699
+ break;
5700
+ }
5502
5701
  const sig = grabRoundTripSignature(part);
5503
5702
  openBlock("tool", {
5504
5703
  type: "tool_use",
@@ -5510,6 +5709,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5510
5709
  break;
5511
5710
  }
5512
5711
  case "tool-input-delta":
5712
+ if (hiddenIds.has(part.id ?? "")) break;
5513
5713
  emit("content_block_delta", {
5514
5714
  type: "content_block_delta",
5515
5715
  index: idToBlock.get(part.id ?? "") ?? blockIndex,
@@ -5519,6 +5719,10 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5519
5719
  case "tool-input-end":
5520
5720
  break;
5521
5721
  case "tool-call": {
5722
+ if (part.toolName && part.toolName === hiddenToolName || hiddenIds.has(part.toolCallId ?? "")) {
5723
+ hiddenIds.add(part.toolCallId ?? "");
5724
+ break;
5725
+ }
5522
5726
  finishReason = "tool_use";
5523
5727
  if (!idToBlock.has(part.toolCallId ?? "") && openType !== "tool") {
5524
5728
  const sig = grabRoundTripSignature(part);
@@ -5567,7 +5771,12 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5567
5771
  return { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens };
5568
5772
  }
5569
5773
  async function streamAnthropicResponse(model, params, modelId, write, log7) {
5570
- const result = streamText({ model, ...params, onError: () => {
5774
+ const { webSearchToolName, ...callParams } = params;
5775
+ if (webSearchToolName) {
5776
+ log7?.(() => `gateway web_search: executing locally via DuckDuckGo (provider-agnostic)`);
5777
+ }
5778
+ const stopWhen = webSearchToolName ? stepCountIs(MAX_WEB_SEARCH_STEPS) : void 0;
5779
+ const result = streamText({ model, ...callParams, stopWhen, onError: () => {
5571
5780
  } });
5572
5781
  Promise.resolve(result.text).catch(() => {
5573
5782
  });
@@ -5579,21 +5788,23 @@ async function streamAnthropicResponse(model, params, modelId, write, log7) {
5579
5788
  });
5580
5789
  Promise.resolve(result.usage).catch(() => {
5581
5790
  });
5582
- return await writeAnthropicStream(result.fullStream, modelId, write, log7);
5791
+ return await writeAnthropicStream(result.fullStream, modelId, write, log7, webSearchToolName);
5583
5792
  }
5584
5793
  async function generateAnthropicResponse(model, params, modelId, options) {
5585
5794
  let text4;
5586
5795
  let toolCalls;
5587
5796
  let finishReason;
5588
5797
  let usage;
5798
+ const { webSearchToolName, ...callParams } = params;
5799
+ const stopWhen = webSearchToolName ? stepCountIs(MAX_WEB_SEARCH_STEPS) : void 0;
5589
5800
  if (options?.forceStream) {
5590
- const r = streamText({ model, ...params, onError: () => {
5801
+ const r = streamText({ model, ...callParams, stopWhen, onError: () => {
5591
5802
  } });
5592
5803
  Promise.resolve(r.toolResults).catch(() => {
5593
5804
  });
5594
5805
  [text4, toolCalls, finishReason, usage] = await Promise.all([r.text, r.toolCalls, r.finishReason, r.usage]);
5595
5806
  } else {
5596
- const r = await generateText({ model, ...params });
5807
+ const r = await generateText({ model, ...callParams, stopWhen });
5597
5808
  ({ text: text4, toolCalls, finishReason, usage } = r);
5598
5809
  }
5599
5810
  return {
@@ -5603,7 +5814,7 @@ async function generateAnthropicResponse(model, params, modelId, options) {
5603
5814
  model: modelId,
5604
5815
  content: [
5605
5816
  ...text4 ? [{ type: "text", text: text4 }] : [],
5606
- ...toolCalls.map((tc) => ({
5817
+ ...toolCalls.filter((tc) => tc.toolName !== webSearchToolName).map((tc) => ({
5607
5818
  type: "tool_use",
5608
5819
  id: encodeToolUseId(tc.toolCallId, grabRoundTripSignature(tc)),
5609
5820
  name: tc.toolName,
@@ -5860,6 +6071,13 @@ function aliasModelId(realId, providerId) {
5860
6071
  const safeRealId = realId.replace(/[\/\s:()]+/g, "-");
5861
6072
  return `anthropic-${sanitized}__${safeRealId}`;
5862
6073
  }
6074
+ function resolveRoute(byAlias, id, defaultRoute, plog) {
6075
+ const direct = lookupRoute(byAlias, id);
6076
+ if (direct) return direct;
6077
+ quietErrorLog(`resolveRoute: model '${id}' not in catalog, remapping to default route '${defaultRoute.aliasId}'`);
6078
+ plog(() => `resolveRoute: model '${id}' not in catalog, remapping to default route '${defaultRoute.aliasId}'`);
6079
+ return defaultRoute;
6080
+ }
5863
6081
  function lookupRoute(byAlias, id) {
5864
6082
  for (const key of routeLookupIds(id)) {
5865
6083
  const route = byAlias.get(key);
@@ -5900,15 +6118,12 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5900
6118
  const modelPathMatch = req.url.match(/^\/v1\/models\/([^?]+)/);
5901
6119
  if (modelPathMatch) {
5902
6120
  const id = decodeURIComponent(modelPathMatch[1]);
5903
- const route = lookupRoute(byAlias, id);
5904
- if (route) {
5905
- res.writeHead(200, { "Content-Type": "application/json" });
5906
- res.end(JSON.stringify(formatAnthropicModelEntry(route.aliasId, route.displayName, route.contextWindow, route.inputTypes)));
5907
- } else {
5908
- quietErrorLog(`GET /v1/models/${id} - model not found`);
5909
- res.writeHead(404, { "Content-Type": "application/json" });
5910
- res.end(JSON.stringify({ error: { type: "not_found_error", message: `Model '${id}' not found` } }));
6121
+ const route = resolveRoute(byAlias, id, defaultRoute, plog);
6122
+ if (route === defaultRoute && id !== defaultRoute.aliasId) {
6123
+ quietErrorLog(`GET /v1/models/${id} - model not found, returning default route '${defaultRoute.aliasId}'`);
5911
6124
  }
6125
+ res.writeHead(200, { "Content-Type": "application/json" });
6126
+ res.end(JSON.stringify(formatAnthropicModelEntry(route.aliasId, route.displayName, route.contextWindow, route.inputTypes)));
5912
6127
  } else {
5913
6128
  res.writeHead(200, { "Content-Type": "application/json" });
5914
6129
  res.end(modelsPayload);
@@ -5931,7 +6146,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5931
6146
  }
5932
6147
  const originalModel = anthropicBody.model;
5933
6148
  const clientWantsStream = Boolean(anthropicBody.stream);
5934
- const route = lookupRoute(byAlias, originalModel) ?? defaultRoute;
6149
+ const route = resolveRoute(byAlias, originalModel, defaultRoute, plog);
5935
6150
  if (route === defaultRoute && originalModel !== defaultRoute.aliasId) {
5936
6151
  quietErrorLog(`POST /v1/messages - model alias '${originalModel}' not found, falling back to default route '${defaultRoute.aliasId}'`);
5937
6152
  }
@@ -7032,10 +7247,10 @@ function makeRouteResolver(localProviders) {
7032
7247
  return provider && model ? localModelToRoute(provider, model) ?? void 0 : void 0;
7033
7248
  };
7034
7249
  }
7035
- function buildCatalogRoutes(startingRoute, favorites, resolveRoute, max = MAX_MODEL_CATALOG) {
7250
+ function buildCatalogRoutes(startingRoute, favorites, resolveRoute2, max = MAX_MODEL_CATALOG) {
7036
7251
  const droppedFavorites = [];
7037
7252
  const tail = favorites.map((fav) => {
7038
- const route = resolveRoute(fav.providerId, fav.modelId);
7253
+ const route = resolveRoute2(fav.providerId, fav.modelId);
7039
7254
  if (!route) droppedFavorites.push(fav);
7040
7255
  return route;
7041
7256
  }).filter((route) => route !== void 0);
@@ -7260,7 +7475,7 @@ async function askSaveServerPassword() {
7260
7475
  import { createServer as createServer2 } from "http";
7261
7476
 
7262
7477
  // src/gateway/openai-adapter.ts
7263
- import { tool as tool3, jsonSchema as jsonSchema3, streamText as streamText2, generateText as generateText2 } from "ai";
7478
+ import { tool as tool4, jsonSchema as jsonSchema4, streamText as streamText2, generateText as generateText2 } from "ai";
7264
7479
  function translateOpenAiRequest(body) {
7265
7480
  const toolNameById = /* @__PURE__ */ new Map();
7266
7481
  for (const msg of body.messages) {
@@ -7325,10 +7540,10 @@ function translateOpenAiRequest(body) {
7325
7540
  tools = {};
7326
7541
  for (const t of body.tools) {
7327
7542
  if (t.type === "function" && t.function.name) {
7328
- const schema = t.function.parameters ? jsonSchema3(t.function.parameters) : void 0;
7329
- tools[t.function.name] = tool3({
7543
+ const schema = t.function.parameters ? jsonSchema4(t.function.parameters) : void 0;
7544
+ tools[t.function.name] = tool4({
7330
7545
  description: t.function.description ?? "",
7331
- inputSchema: schema ?? jsonSchema3({ type: "object", properties: {} })
7546
+ inputSchema: schema ?? jsonSchema4({ type: "object", properties: {} })
7332
7547
  });
7333
7548
  }
7334
7549
  }
@@ -7628,7 +7843,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
7628
7843
  sendJson(res, 400, { error: { message: "Invalid JSON body" } });
7629
7844
  return;
7630
7845
  }
7631
- const model = lookupModel(res, options.catalog, body.model);
7846
+ const model = lookupModel(res, options.catalog, body.model, plog);
7632
7847
  if (!model) {
7633
7848
  plog(`model not found: ${body.model}`);
7634
7849
  return;
@@ -7760,7 +7975,7 @@ async function handleOpenAIChatCompletions(req, res, options, modelCache, plog)
7760
7975
  sendJson(res, 400, { error: { message: "Invalid JSON body" } });
7761
7976
  return;
7762
7977
  }
7763
- const model = lookupModel(res, options.catalog, body.model);
7978
+ const model = lookupModel(res, options.catalog, body.model, plog);
7764
7979
  if (!model) return;
7765
7980
  if (supportsDirectOpenAIChatCompletions(model)) {
7766
7981
  if (model.completionsUrl && !/^https?:\/\//i.test(model.completionsUrl)) {
@@ -7831,12 +8046,19 @@ data: ${JSON.stringify({ type: "error", error: { type: errorType, message } })}
7831
8046
  }
7832
8047
  }
7833
8048
  }
7834
- function lookupModel(res, catalog, modelId) {
8049
+ function lookupModel(res, catalog, modelId, plog) {
7835
8050
  if (typeof modelId !== "string") {
7836
8051
  sendJson(res, 400, { error: { message: "Request body must include a model string" } });
7837
8052
  return null;
7838
8053
  }
7839
- const model = catalog.get(modelId);
8054
+ let model = catalog.get(modelId);
8055
+ if (!model) {
8056
+ const defaultModel = catalog.list()[0];
8057
+ if (defaultModel) {
8058
+ plog(`model '${modelId}' unknown \u2014 remapping to default '${defaultModel.id}'`);
8059
+ model = defaultModel;
8060
+ }
8061
+ }
7840
8062
  if (!model) {
7841
8063
  sendJson(res, 400, { error: { message: `Unknown model: ${modelId}` } });
7842
8064
  return null;
@@ -11016,4 +11238,4 @@ export {
11016
11238
  quitClaudeAppGracefully,
11017
11239
  launchOrRestartClaudeApp
11018
11240
  };
11019
- //# sourceMappingURL=chunk-BCPX3QLK.js.map
11241
+ //# sourceMappingURL=chunk-APLGXZWQ.js.map