@jacobbd/relay-ai 0.7.2 → 0.7.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.
@@ -11,7 +11,7 @@ import { join } from "path";
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jacobbd/relay-ai",
14
- version: "0.7.2",
14
+ version: "0.7.3",
15
15
  publishConfig: {
16
16
  access: "public"
17
17
  },
@@ -1900,6 +1900,24 @@ function setServerListenMode(listenMode) {
1900
1900
  };
1901
1901
  writeConfig(config);
1902
1902
  }
1903
+ function getServerAutostart() {
1904
+ return readConfig().server?.autostart ?? false;
1905
+ }
1906
+ function setServerAutostart(autostart) {
1907
+ const config = readConfig();
1908
+ config.server = {
1909
+ ...config.server ?? {},
1910
+ autostart
1911
+ };
1912
+ writeConfig(config);
1913
+ }
1914
+ function resolveServerAutostart(env = process.env) {
1915
+ const envVal = env["RELAY_AI_SERVER_AUTOSTART"]?.trim().toLowerCase();
1916
+ if (envVal !== void 0 && envVal !== "") {
1917
+ return ["1", "true", "yes", "on"].includes(envVal);
1918
+ }
1919
+ return getServerAutostart();
1920
+ }
1903
1921
 
1904
1922
  // src/launch.ts
1905
1923
  import { execSync, spawn } from "child_process";
@@ -4429,6 +4447,52 @@ function formatOpenAIModels(models) {
4429
4447
  };
4430
4448
  }
4431
4449
 
4450
+ // src/anthropic-endpoints.ts
4451
+ var MESSAGE_PATH = "/v1/messages";
4452
+ var COUNT_TOKENS_PATH = "/v1/messages/count_tokens";
4453
+ var MODELS_PATH = "/v1/models";
4454
+ function anthropicModelsEndpoint(url) {
4455
+ if (!url) return null;
4456
+ try {
4457
+ const pathname = new URL(url, "http://relay.local").pathname;
4458
+ if (pathname === MODELS_PATH || pathname === `${MODELS_PATH}/`) return "list";
4459
+ if (pathname.startsWith(`${MODELS_PATH}/`)) {
4460
+ const id = decodeURIComponent(pathname.slice(MODELS_PATH.length + 1));
4461
+ if (id) return { id };
4462
+ }
4463
+ } catch {
4464
+ }
4465
+ return null;
4466
+ }
4467
+ function anthropicMessagesEndpoint(url) {
4468
+ if (!url) return null;
4469
+ try {
4470
+ const pathname = new URL(url, "http://relay.local").pathname;
4471
+ if (pathname === MESSAGE_PATH) return "messages";
4472
+ if (pathname === COUNT_TOKENS_PATH) return "count_tokens";
4473
+ } catch {
4474
+ }
4475
+ return null;
4476
+ }
4477
+ var NON_CONTEXT_FIELDS = /* @__PURE__ */ new Set([
4478
+ "model",
4479
+ "stream",
4480
+ "max_tokens",
4481
+ "temperature",
4482
+ "top_p",
4483
+ "top_k",
4484
+ "stop_sequences",
4485
+ "metadata"
4486
+ ]);
4487
+ function estimateAnthropicInputTokens(body) {
4488
+ const contextBody = Object.fromEntries(
4489
+ Object.entries(body).filter(([key]) => !NON_CONTEXT_FIELDS.has(key))
4490
+ );
4491
+ const serialized = JSON.stringify(contextBody);
4492
+ if (!serialized || serialized === "{}") return 0;
4493
+ return Math.max(1, Math.ceil(Buffer.byteLength(serialized, "utf8") / 4));
4494
+ }
4495
+
4432
4496
  // src/upstream-forward.ts
4433
4497
  import { Readable } from "stream";
4434
4498
 
@@ -4646,7 +4710,9 @@ function encodeToolUseId(rawId, thoughtSignature, inline = true) {
4646
4710
  return `${rawId}${TOOL_USE_SIG_SEP}${encoded}`;
4647
4711
  }
4648
4712
  function serializeToolResultContent(content) {
4649
- return typeof content === "string" ? content : JSON.stringify(content);
4713
+ if (typeof content === "string") return content;
4714
+ if (content === void 0) return "";
4715
+ return JSON.stringify(content);
4650
4716
  }
4651
4717
 
4652
4718
  // src/antigravity/request-adapter.ts
@@ -5432,6 +5498,8 @@ function sanitizeMessage(message) {
5432
5498
  }
5433
5499
 
5434
5500
  // src/sdk-adapter.ts
5501
+ var NEEDS_TRAILING_TOOL_NUDGE = /* @__PURE__ */ new Set(["@ai-sdk/alibaba"]);
5502
+ var TRAILING_TOOL_NUDGE_TEXT = "Continue.";
5435
5503
  function anthropicEffortFromRequest(body) {
5436
5504
  const effort = body.output_config?.effort;
5437
5505
  if (typeof effort === "string" && effort.trim()) return effort.trim();
@@ -5492,7 +5560,7 @@ function thinkingToSdkPart(block, npm) {
5492
5560
  }
5493
5561
  return part;
5494
5562
  }
5495
- function translateMessages(messages, npm) {
5563
+ function translateMessages(messages, npm, onDebug) {
5496
5564
  const isGoogle = npm === "@ai-sdk/google";
5497
5565
  const out = [];
5498
5566
  for (const msg of messages) {
@@ -5507,6 +5575,10 @@ function translateMessages(messages, npm) {
5507
5575
  if (p8) parts.push(p8);
5508
5576
  }
5509
5577
  }
5578
+ if (blocks.length && !toolResults.length && !parts.length) {
5579
+ const types = blocks.map((b) => b.type).join(", ");
5580
+ onDebug?.(`sdk: dropped user turn with unrecognized block types: [${types}]`);
5581
+ }
5510
5582
  if (toolResults.length) {
5511
5583
  out.push({
5512
5584
  role: "tool",
@@ -5542,6 +5614,10 @@ function translateMessages(messages, npm) {
5542
5614
  if (parts.length) out.push({ role: "assistant", content: parts });
5543
5615
  }
5544
5616
  }
5617
+ if (NEEDS_TRAILING_TOOL_NUDGE.has(npm) && out[out.length - 1]?.role === "tool") {
5618
+ out.push({ role: "user", content: [{ type: "text", text: TRAILING_TOOL_NUDGE_TEXT }] });
5619
+ onDebug?.("sdk: appended continuation nudge (request ended on tool result)");
5620
+ }
5545
5621
  return out;
5546
5622
  }
5547
5623
  function stripNullInputs(input) {
@@ -5592,7 +5668,7 @@ function translateRequest2(body, npm, options) {
5592
5668
  }
5593
5669
  return {
5594
5670
  system: options?.openAiOAuth ? void 0 : systemText,
5595
- messages: translateMessages(messages, npm),
5671
+ messages: translateMessages(messages, npm, options?.onDebug),
5596
5672
  tools: translateTools3(upstreamTools.length ? upstreamTools : void 0),
5597
5673
  toolChoice: translateToolChoice(body.tool_choice),
5598
5674
  maxOutputTokens: options?.openAiOAuth ? void 0 : body.max_tokens,
@@ -5600,7 +5676,7 @@ function translateRequest2(body, npm, options) {
5600
5676
  providerOptions
5601
5677
  };
5602
5678
  }
5603
- async function writeAnthropicStream(fullStream, modelId, write, log7) {
5679
+ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedInputTokens = 0) {
5604
5680
  const messageId = "msg_" + Date.now();
5605
5681
  let blockIndex = -1;
5606
5682
  let started = false;
@@ -5608,7 +5684,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5608
5684
  let pendingThinkingSig;
5609
5685
  const idToBlock = /* @__PURE__ */ new Map();
5610
5686
  let finishReason = "end_turn";
5611
- let usage = { input_tokens: 0, output_tokens: 0 };
5687
+ let usage = { input_tokens: estimatedInputTokens, output_tokens: 0 };
5612
5688
  const emit = (event, data) => write(sseChunk(event, data));
5613
5689
  const ensureStart = () => {
5614
5690
  if (started) return;
@@ -5622,7 +5698,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5622
5698
  model: modelId,
5623
5699
  stop_reason: null,
5624
5700
  stop_sequence: null,
5625
- usage: { input_tokens: 0, output_tokens: 0 }
5701
+ usage: { input_tokens: estimatedInputTokens, output_tokens: 0 }
5626
5702
  }
5627
5703
  });
5628
5704
  started = true;
@@ -5721,7 +5797,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5721
5797
  case "finish":
5722
5798
  if (part.totalUsage) {
5723
5799
  usage = {
5724
- input_tokens: part.totalUsage.inputTokens ?? 0,
5800
+ input_tokens: part.totalUsage.inputTokens ?? estimatedInputTokens,
5725
5801
  output_tokens: part.totalUsage.outputTokens ?? 0
5726
5802
  };
5727
5803
  }
@@ -5747,7 +5823,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7) {
5747
5823
  emit("message_delta", { type: "message_delta", delta: { stop_reason: finishReason, stop_sequence: null }, usage });
5748
5824
  emit("message_stop", { type: "message_stop" });
5749
5825
  }
5750
- async function streamAnthropicResponse(model, params, modelId, write, log7) {
5826
+ async function streamAnthropicResponse(model, params, modelId, write, log7, estimatedInputTokens = 0) {
5751
5827
  const result = streamText({ model, ...params, onError: () => {
5752
5828
  } });
5753
5829
  Promise.resolve(result.text).catch(() => {
@@ -5760,7 +5836,13 @@ async function streamAnthropicResponse(model, params, modelId, write, log7) {
5760
5836
  });
5761
5837
  Promise.resolve(result.usage).catch(() => {
5762
5838
  });
5763
- await writeAnthropicStream(result.fullStream, modelId, write, log7);
5839
+ await writeAnthropicStream(
5840
+ result.fullStream,
5841
+ modelId,
5842
+ write,
5843
+ log7,
5844
+ estimatedInputTokens
5845
+ );
5764
5846
  }
5765
5847
  async function generateAnthropicResponse(model, params, modelId, options) {
5766
5848
  let text4;
@@ -5965,6 +6047,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5965
6047
  const params = translateRequest2(anthropicBody, route.npm, {
5966
6048
  openAiOAuth,
5967
6049
  maxTools: maxToolsForNpm(route.npm),
6050
+ onDebug: (msg) => plog(() => msg),
5968
6051
  reasoningMetadata: {
5969
6052
  providerId: route.providerId,
5970
6053
  apiBaseUrl: route.baseURL,
@@ -5998,7 +6081,14 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
5998
6081
  "Cache-Control": "no-cache",
5999
6082
  "Connection": "keep-alive"
6000
6083
  });
6001
- await streamAnthropicResponse(model, params, originalModel, (c) => res.write(c), plog);
6084
+ await streamAnthropicResponse(
6085
+ model,
6086
+ params,
6087
+ originalModel,
6088
+ (c) => res.write(c),
6089
+ plog,
6090
+ estimateAnthropicInputTokens(anthropicBody)
6091
+ );
6002
6092
  res.end();
6003
6093
  } else {
6004
6094
  const anthropicResponse = await generateAnthropicResponse(
@@ -9870,6 +9960,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
9870
9960
  const params = translateRequest2(body, model.npm, {
9871
9961
  defaultEffort: anthropicEffortFromRequest(body) ? void 0 : model.defaultEffort,
9872
9962
  openAiOAuth: model.npm === "@ai-sdk/openai" && model.authType === "oauth",
9963
+ onDebug: plog,
9873
9964
  reasoningMetadata: {
9874
9965
  providerId: model.providerId,
9875
9966
  apiBaseUrl: model.apiBaseUrl,
@@ -9890,7 +9981,14 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
9890
9981
  "Cache-Control": "no-cache",
9891
9982
  "Connection": "keep-alive"
9892
9983
  });
9893
- await streamAnthropicResponse(languageModel, params, responseModelId, (chunk) => res.write(chunk));
9984
+ await streamAnthropicResponse(
9985
+ languageModel,
9986
+ params,
9987
+ responseModelId,
9988
+ (chunk) => res.write(chunk),
9989
+ void 0,
9990
+ estimateAnthropicInputTokens(body)
9991
+ );
9894
9992
  res.end();
9895
9993
  } else {
9896
9994
  const anthropicResponse = await generateAnthropicResponse(languageModel, params, responseModelId);
@@ -11818,6 +11916,8 @@ export {
11818
11916
  setServerFreeModelsOnly,
11819
11917
  getServerListenMode,
11820
11918
  setServerListenMode,
11919
+ setServerAutostart,
11920
+ resolveServerAutostart,
11821
11921
  findBinaryOnPath,
11822
11922
  findClaudeBinary,
11823
11923
  launchClaude,
@@ -11891,6 +11991,9 @@ export {
11891
11991
  openAiIdCollisions,
11892
11992
  createGatewayModelCatalog,
11893
11993
  buildDedupedModelRows,
11994
+ anthropicModelsEndpoint,
11995
+ anthropicMessagesEndpoint,
11996
+ estimateAnthropicInputTokens,
11894
11997
  grabRoundTripSignature,
11895
11998
  silenceSdkWarnings,
11896
11999
  parseToolArguments,
@@ -11966,4 +12069,4 @@ export {
11966
12069
  supportsClaudeTransparentMode,
11967
12070
  buildHttpProxyRoutes
11968
12071
  };
11969
- //# sourceMappingURL=chunk-LNQ46VW7.js.map
12072
+ //# sourceMappingURL=chunk-5LPUIWNJ.js.map