@jeffreycao/copilot-api 1.9.8 → 1.9.11

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.
@@ -1024,6 +1024,37 @@ embeddingRoutes.post("/", async (c) => {
1024
1024
  }
1025
1025
  });
1026
1026
 
1027
+ //#endregion
1028
+ //#region src/lib/provider-model.ts
1029
+ const parseProviderModelAlias = (model) => {
1030
+ const separatorIndex = model.indexOf("/");
1031
+ if (separatorIndex <= 0 || separatorIndex === model.length - 1) return null;
1032
+ const provider = model.slice(0, separatorIndex).trim();
1033
+ const providerModel = model.slice(separatorIndex + 1).trim();
1034
+ if (!provider || !providerModel) return null;
1035
+ return {
1036
+ model: providerModel,
1037
+ provider
1038
+ };
1039
+ };
1040
+ const createFallbackModel = (modelId) => ({
1041
+ capabilities: {
1042
+ family: "provider",
1043
+ limits: {},
1044
+ object: "model_capabilities",
1045
+ supports: {},
1046
+ tokenizer: "o200k_base",
1047
+ type: "chat"
1048
+ },
1049
+ id: modelId,
1050
+ model_picker_enabled: false,
1051
+ name: modelId,
1052
+ object: "model",
1053
+ preview: false,
1054
+ vendor: "provider",
1055
+ version: "unknown"
1056
+ });
1057
+
1027
1058
  //#endregion
1028
1059
  //#region src/lib/tokenizer.ts
1029
1060
  const ENCODING_MAP = {
@@ -1243,57 +1274,6 @@ const getTokenCount = async (payload, model) => {
1243
1274
  };
1244
1275
  };
1245
1276
 
1246
- //#endregion
1247
- //#region src/lib/models.ts
1248
- const findEndpointModel = (sdkModelId) => {
1249
- const models = state.models?.data ?? [];
1250
- const exactMatch = models.find((m) => m.id === sdkModelId);
1251
- if (exactMatch) return exactMatch;
1252
- const normalized = _normalizeSdkModelId(sdkModelId);
1253
- if (!normalized) return;
1254
- const modelName = `claude-${normalized.family}-${normalized.version}`;
1255
- const model = models.find((m) => m.id === modelName);
1256
- if (model) return model;
1257
- };
1258
- /**
1259
- * Normalizes an SDK model ID to extract the model family and version.
1260
- * this method from github copilot extension
1261
- * Examples:
1262
- * - "claude-opus-4-5-20251101" -> { family: "opus", version: "4.5" }
1263
- * - "claude-3-5-sonnet-20241022" -> { family: "sonnet", version: "3.5" }
1264
- * - "claude-sonnet-4-20250514" -> { family: "sonnet", version: "4" }
1265
- * - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
1266
- * - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
1267
- */
1268
- const _normalizeSdkModelId = (sdkModelId) => {
1269
- const withoutDate = sdkModelId.toLowerCase().replace(/-\d{8}$/, "");
1270
- const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
1271
- if (pattern1) return {
1272
- family: pattern1[1],
1273
- version: `${pattern1[2]}.${pattern1[3]}`
1274
- };
1275
- const pattern2 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
1276
- if (pattern2) return {
1277
- family: pattern2[3],
1278
- version: `${pattern2[1]}.${pattern2[2]}`
1279
- };
1280
- const pattern3 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
1281
- if (pattern3) return {
1282
- family: pattern3[1],
1283
- version: `${pattern3[2]}.${pattern3[3]}`
1284
- };
1285
- const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
1286
- if (pattern4) return {
1287
- family: pattern4[1],
1288
- version: pattern4[2]
1289
- };
1290
- const pattern5 = withoutDate.match(/^claude-(\d+)-(\w+)$/);
1291
- if (pattern5) return {
1292
- family: pattern5[2],
1293
- version: pattern5[1]
1294
- };
1295
- };
1296
-
1297
1277
  //#endregion
1298
1278
  //#region src/routes/messages/utils.ts
1299
1279
  function mapOpenAIStopReasonToAnthropic(finishReason) {
@@ -1308,7 +1288,7 @@ function mapOpenAIStopReasonToAnthropic(finishReason) {
1308
1288
 
1309
1289
  //#endregion
1310
1290
  //#region src/routes/messages/non-stream-translation.ts
1311
- const THINKING_TEXT = "Thinking...";
1291
+ const THINKING_TEXT$1 = "Thinking...";
1312
1292
  const RICH_TOOL_RESULT_MOVED_TEXT = "Rich tool result content was moved to a user message because this upstream does not support it in tool messages.";
1313
1293
  const COPILOT_TOOL_CONTENT_SUPPORT_TYPE = ["array", "image"];
1314
1294
  function translateToOpenAI(payload, options = {}) {
@@ -1445,8 +1425,8 @@ function handleAssistantMessage(message, modelId, capabilities) {
1445
1425
  }];
1446
1426
  const toolUseBlocks = message.content.filter((block) => block.type === "tool_use");
1447
1427
  let thinkingBlocks = message.content.filter((block) => block.type === "thinking");
1448
- if (modelId.startsWith("claude")) thinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT && b.signature && !b.signature.includes("@"));
1449
- const thinkingContents = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT).map((b) => b.thinking);
1428
+ if (modelId.startsWith("claude")) thinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT$1 && b.signature && !b.signature.includes("@"));
1429
+ const thinkingContents = thinkingBlocks.filter((b) => b.thinking && b.thinking !== THINKING_TEXT$1).map((b) => b.thinking);
1450
1430
  const allThinkingContent = thinkingContents.length > 0 ? thinkingContents.join("\n\n") : void 0;
1451
1431
  const signature = thinkingBlocks.find((b) => b.signature)?.signature;
1452
1432
  return toolUseBlocks.length > 0 ? [{
@@ -1496,7 +1476,7 @@ function mapContent(content, options = {}) {
1496
1476
  });
1497
1477
  break;
1498
1478
  }
1499
- if (contentParts.length === 0) return null;
1479
+ if (contentParts.length === 0) return "";
1500
1480
  return contentParts;
1501
1481
  }
1502
1482
  function createDocumentTextPart() {
@@ -1607,7 +1587,7 @@ function getAnthropicThinkBlocks(reasoningText, reasoningOpaque) {
1607
1587
  }];
1608
1588
  if (reasoningOpaque && reasoningOpaque.length > 0) return [{
1609
1589
  type: "thinking",
1610
- thinking: THINKING_TEXT,
1590
+ thinking: THINKING_TEXT$1,
1611
1591
  signature: reasoningOpaque
1612
1592
  }];
1613
1593
  return [];
@@ -1622,8 +1602,106 @@ function getAnthropicToolUseBlocks(toolCalls) {
1622
1602
  }));
1623
1603
  }
1624
1604
 
1605
+ //#endregion
1606
+ //#region src/routes/provider/messages/count-tokens-handler.ts
1607
+ const logger$5 = createHandlerLogger("provider-count-tokens-handler");
1608
+ async function handleProviderCountTokens(c) {
1609
+ const provider = c.req.param("provider");
1610
+ const payload = await c.req.json();
1611
+ return await handleProviderCountTokensForProvider(c, {
1612
+ payload,
1613
+ provider
1614
+ });
1615
+ }
1616
+ async function handleProviderCountTokensForProvider(c, options) {
1617
+ const { payload: anthropicPayload, provider } = options;
1618
+ const modelId = anthropicPayload.model.trim();
1619
+ const providerConfig = getProviderConfig(provider);
1620
+ if (!providerConfig) return c.json({ error: {
1621
+ message: `Provider '${provider}' not found or disabled`,
1622
+ type: "invalid_request_error"
1623
+ } }, 404);
1624
+ const modelConfig = providerConfig.models?.[modelId];
1625
+ const translationOptions = providerConfig.type === "openai-compatible" ? {
1626
+ supportPdf: modelConfig?.supportPdf,
1627
+ toolContentSupportType: modelConfig?.toolContentSupportType ?? []
1628
+ } : void 0;
1629
+ const openAIPayload = translateToOpenAI(anthropicPayload, translationOptions);
1630
+ const selectedModel = createFallbackModel(modelId);
1631
+ const tokenCount = await getTokenCount(openAIPayload, selectedModel);
1632
+ const finalTokenCount = tokenCount.input + tokenCount.output;
1633
+ logger$5.debug("provider.count_tokens.success", {
1634
+ provider,
1635
+ model: anthropicPayload.model,
1636
+ input_tokens: finalTokenCount
1637
+ });
1638
+ return c.json({ input_tokens: finalTokenCount });
1639
+ }
1640
+
1641
+ //#endregion
1642
+ //#region src/lib/models.ts
1643
+ const findEndpointModel = (sdkModelId) => {
1644
+ const models = state.models?.data ?? [];
1645
+ const exactMatch = models.find((m) => m.id === sdkModelId);
1646
+ if (exactMatch) return exactMatch;
1647
+ const normalized = _normalizeSdkModelId(sdkModelId);
1648
+ if (!normalized) return;
1649
+ const modelName = `claude-${normalized.family}-${normalized.version}`;
1650
+ const model = models.find((m) => m.id === modelName);
1651
+ if (model) return model;
1652
+ };
1653
+ /**
1654
+ * Normalizes an SDK model ID to extract the model family and version.
1655
+ * this method from github copilot extension
1656
+ * Examples:
1657
+ * - "claude-opus-4-5-20251101" -> { family: "opus", version: "4.5" }
1658
+ * - "claude-3-5-sonnet-20241022" -> { family: "sonnet", version: "3.5" }
1659
+ * - "claude-sonnet-4-20250514" -> { family: "sonnet", version: "4" }
1660
+ * - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
1661
+ * - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
1662
+ */
1663
+ const _normalizeSdkModelId = (sdkModelId) => {
1664
+ const withoutDate = sdkModelId.toLowerCase().replace(/-\d{8}$/, "");
1665
+ const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
1666
+ if (pattern1) return {
1667
+ family: pattern1[1],
1668
+ version: `${pattern1[2]}.${pattern1[3]}`
1669
+ };
1670
+ const pattern2 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
1671
+ if (pattern2) return {
1672
+ family: pattern2[3],
1673
+ version: `${pattern2[1]}.${pattern2[2]}`
1674
+ };
1675
+ const pattern3 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
1676
+ if (pattern3) return {
1677
+ family: pattern3[1],
1678
+ version: `${pattern3[2]}.${pattern3[3]}`
1679
+ };
1680
+ const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
1681
+ if (pattern4) return {
1682
+ family: pattern4[1],
1683
+ version: pattern4[2]
1684
+ };
1685
+ const pattern5 = withoutDate.match(/^claude-(\d+)-(\w+)$/);
1686
+ if (pattern5) return {
1687
+ family: pattern5[2],
1688
+ version: pattern5[1]
1689
+ };
1690
+ };
1691
+
1625
1692
  //#endregion
1626
1693
  //#region src/routes/messages/count-tokens-handler.ts
1694
+ const resolveCountTokensModel = (modelId, findModel = findEndpointModel) => {
1695
+ const selectedModel = findModel(modelId);
1696
+ if (selectedModel) return {
1697
+ fallback: false,
1698
+ model: selectedModel
1699
+ };
1700
+ return {
1701
+ fallback: true,
1702
+ model: createFallbackModel(modelId.trim())
1703
+ };
1704
+ };
1627
1705
  /**
1628
1706
  * Forwards token counting to Anthropic's real /v1/messages/count_tokens endpoint.
1629
1707
  * Returns the result on success, or null to fall through to estimation.
@@ -1662,2346 +1740,2334 @@ async function countTokensViaAnthropic(c, payload) {
1662
1740
  * endpoint for accurate counts. Otherwise falls back to GPT tokenizer estimation.
1663
1741
  */
1664
1742
  async function handleCountTokens(c) {
1665
- try {
1666
- const anthropicPayload = await c.req.json();
1667
- const anthropicResult = await countTokensViaAnthropic(c, anthropicPayload);
1668
- if (anthropicResult) return anthropicResult;
1669
- const anthropicBeta = c.req.header("anthropic-beta");
1670
- const openAIPayload = translateToOpenAI(anthropicPayload);
1671
- const selectedModel = findEndpointModel(anthropicPayload.model);
1672
- anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
1673
- if (!selectedModel) {
1674
- consola.warn("Model not found, returning default token count");
1675
- return c.json({ input_tokens: 1 });
1743
+ const anthropicPayload = await c.req.json();
1744
+ const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
1745
+ if (providerModelAlias) {
1746
+ anthropicPayload.model = providerModelAlias.model;
1747
+ return await handleProviderCountTokensForProvider(c, {
1748
+ payload: anthropicPayload,
1749
+ provider: providerModelAlias.provider
1750
+ });
1751
+ }
1752
+ const anthropicResult = await countTokensViaAnthropic(c, anthropicPayload);
1753
+ if (anthropicResult) return anthropicResult;
1754
+ const anthropicBeta = c.req.header("anthropic-beta");
1755
+ const openAIPayload = translateToOpenAI(anthropicPayload);
1756
+ const requestedModel = anthropicPayload.model;
1757
+ const resolve = resolveCountTokensModel(requestedModel);
1758
+ const selectedModel = resolve.model;
1759
+ anthropicPayload.model = selectedModel.id;
1760
+ if (resolve.fallback) consola.warn(`Model '${requestedModel}' not found, using o200k_base fallback tokenizer`);
1761
+ const tokenCount = await getTokenCount(openAIPayload, selectedModel);
1762
+ if (anthropicPayload.tools && anthropicPayload.tools.length > 0) {
1763
+ let addToolSystemPromptCount = false;
1764
+ if (anthropicBeta) {
1765
+ const toolsLength = anthropicPayload.tools.length;
1766
+ addToolSystemPromptCount = !anthropicPayload.tools.some((tool) => tool.name.startsWith("mcp__") || tool.name === "Skill" && toolsLength === 1);
1676
1767
  }
1677
- const tokenCount = await getTokenCount(openAIPayload, selectedModel);
1678
- if (anthropicPayload.tools && anthropicPayload.tools.length > 0) {
1679
- let addToolSystemPromptCount = false;
1680
- if (anthropicBeta) {
1681
- const toolsLength = anthropicPayload.tools.length;
1682
- addToolSystemPromptCount = !anthropicPayload.tools.some((tool) => tool.name.startsWith("mcp__") || tool.name === "Skill" && toolsLength === 1);
1683
- }
1684
- if (addToolSystemPromptCount) {
1685
- if (anthropicPayload.model.startsWith("claude")) tokenCount.input = tokenCount.input + 346;
1686
- else if (anthropicPayload.model.startsWith("grok")) tokenCount.input = tokenCount.input + 120;
1687
- }
1768
+ if (addToolSystemPromptCount) {
1769
+ if (anthropicPayload.model.startsWith("claude")) tokenCount.input = tokenCount.input + 346;
1770
+ else if (anthropicPayload.model.startsWith("grok")) tokenCount.input = tokenCount.input + 120;
1688
1771
  }
1689
- let finalTokenCount = tokenCount.input + tokenCount.output;
1690
- if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * getClaudeTokenMultiplier());
1691
- consola.info("Token count:", finalTokenCount);
1692
- return c.json({ input_tokens: finalTokenCount });
1693
- } catch (error) {
1694
- consola.error("Error counting tokens:", error);
1695
- return c.json({ input_tokens: 1 });
1696
1772
  }
1773
+ let finalTokenCount = tokenCount.input + tokenCount.output;
1774
+ if (anthropicPayload.model.startsWith("claude")) finalTokenCount = Math.round(finalTokenCount * getClaudeTokenMultiplier());
1775
+ consola.info("Token count:", finalTokenCount);
1776
+ return c.json({ input_tokens: finalTokenCount });
1697
1777
  }
1698
1778
 
1699
1779
  //#endregion
1700
- //#region src/services/copilot/create-responses.ts
1701
- const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType }) => {
1702
- if (!state.copilotToken) throw new Error("Copilot token not found");
1703
- const headers = {
1704
- ...copilotHeaders(state, requestId, vision),
1705
- "x-initiator": initiator
1706
- };
1707
- prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
1708
- prepareForCompact(headers, compactType);
1709
- payload.service_tier = void 0;
1710
- consola.log(`<-- model: ${payload.model}`);
1711
- const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
1712
- method: "POST",
1713
- headers,
1714
- body: JSON.stringify(payload)
1780
+ //#region src/routes/messages/stream-translation.ts
1781
+ function isToolBlockOpen(state$1) {
1782
+ if (!state$1.contentBlockOpen) return false;
1783
+ return Object.values(state$1.toolCalls).some((tc) => tc.anthropicBlockIndex === state$1.contentBlockIndex);
1784
+ }
1785
+ function translateChunkToAnthropicEvents(chunk, state$1) {
1786
+ const events$1 = [];
1787
+ if (chunk.choices.length === 0) {
1788
+ completePendingMessage(state$1, events$1, chunk);
1789
+ return events$1;
1790
+ }
1791
+ const choice = chunk.choices[0];
1792
+ const { delta } = choice;
1793
+ handleMessageStart(state$1, events$1, chunk);
1794
+ handleThinkingText(delta, state$1, events$1);
1795
+ handleContent(delta, state$1, events$1);
1796
+ handleToolCalls(delta, state$1, events$1);
1797
+ handleFinish(choice, state$1, {
1798
+ events: events$1,
1799
+ chunk
1715
1800
  });
1716
- logCopilotRateLimits(response.headers);
1717
- if (!response.ok) {
1718
- consola.error("Failed to create responses", response);
1719
- throw new HTTPError("Failed to create responses", response);
1801
+ return events$1;
1802
+ }
1803
+ function flushPendingAnthropicStreamEvents(state$1) {
1804
+ const events$1 = [];
1805
+ completePendingMessage(state$1, events$1);
1806
+ return events$1;
1807
+ }
1808
+ function completePendingMessage(state$1, events$1, chunk) {
1809
+ if (!state$1.pendingMessageDelta) return;
1810
+ if (chunk?.usage) state$1.pendingMessageDelta.usage = getAnthropicUsageFromOpenAIChunk(chunk);
1811
+ events$1.push(state$1.pendingMessageDelta, { type: "message_stop" });
1812
+ state$1.pendingMessageDelta = void 0;
1813
+ }
1814
+ function handleFinish(choice, state$1, context) {
1815
+ const { events: events$1, chunk } = context;
1816
+ if (choice.finish_reason && choice.finish_reason.length > 0) {
1817
+ if (state$1.contentBlockOpen) {
1818
+ const toolBlockOpen = isToolBlockOpen(state$1);
1819
+ context.events.push({
1820
+ type: "content_block_stop",
1821
+ index: state$1.contentBlockIndex
1822
+ });
1823
+ state$1.contentBlockOpen = false;
1824
+ state$1.contentBlockIndex++;
1825
+ if (!toolBlockOpen) handleReasoningOpaque(choice.delta, events$1, state$1);
1826
+ }
1827
+ state$1.pendingMessageDelta = {
1828
+ type: "message_delta",
1829
+ delta: {
1830
+ stop_reason: mapOpenAIStopReasonToAnthropic(choice.finish_reason),
1831
+ stop_sequence: null
1832
+ },
1833
+ usage: getAnthropicUsageFromOpenAIChunk(chunk)
1834
+ };
1835
+ if (chunk.usage) completePendingMessage(state$1, events$1, chunk);
1720
1836
  }
1721
- if (payload.stream) return events(response);
1722
- return await response.json();
1723
- };
1724
-
1725
- //#endregion
1726
- //#region src/routes/messages/responses-translation.ts
1727
- const MESSAGE_TYPE = "message";
1728
- const COMPACTION_SIGNATURE_PREFIX = "cm1#";
1729
- const COMPACTION_SIGNATURE_SEPARATOR = "@";
1730
- const THINKING_TEXT$1 = "Thinking...";
1731
- const translateAnthropicMessagesToResponsesPayload = (payload) => {
1732
- const input = [];
1733
- const applyPhase = shouldApplyPhase(payload.model);
1734
- for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase));
1735
- const translatedTools = convertAnthropicTools(payload.tools);
1736
- const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
1737
- const { sessionId: promptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
1738
- return {
1739
- model: payload.model,
1740
- input,
1741
- instructions: translateSystemPrompt(payload.system, payload.model),
1742
- temperature: 1,
1743
- top_p: payload.top_p ?? null,
1744
- max_output_tokens: Math.max(payload.max_tokens, 12800),
1745
- tools: translatedTools,
1746
- tool_choice: toolChoice,
1747
- metadata: payload.metadata ? { ...payload.metadata } : null,
1748
- prompt_cache_key: promptCacheKey,
1749
- stream: payload.stream ?? null,
1750
- store: false,
1751
- parallel_tool_calls: true,
1752
- reasoning: {
1753
- effort: getReasoningEffortForModel(payload.model),
1754
- summary: "detailed"
1755
- },
1756
- include: ["reasoning.encrypted_content"]
1837
+ }
1838
+ function getAnthropicUsageFromOpenAIChunk(chunk) {
1839
+ const { cachedTokens, cacheCreationTokens, inputTokens } = getOpenAIChunkUsageTokens(chunk);
1840
+ return {
1841
+ input_tokens: inputTokens,
1842
+ output_tokens: chunk.usage?.completion_tokens ?? 0,
1843
+ ...chunk.usage?.prompt_tokens_details?.cache_creation_input_tokens !== void 0 && { cache_creation_input_tokens: cacheCreationTokens },
1844
+ ...chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: cachedTokens }
1757
1845
  };
1758
- };
1759
- const encodeCompactionCarrierSignature = (compaction) => {
1760
- return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
1761
- };
1762
- const decodeCompactionCarrierSignature = (signature) => {
1763
- if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
1764
- const raw = signature.slice(4);
1765
- const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
1766
- if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
1767
- const encrypted_content = raw.slice(0, separatorIndex);
1768
- const id = raw.slice(separatorIndex + 1);
1769
- if (!encrypted_content) return;
1770
- return {
1771
- id,
1772
- encrypted_content
1773
- };
1774
- }
1775
- };
1776
- const translateMessage = (message, model, applyPhase) => {
1777
- if (message.role === "user") return translateUserMessage(message);
1778
- return translateAssistantMessage(message, model, applyPhase);
1779
- };
1780
- const translateUserMessage = (message) => {
1781
- if (typeof message.content === "string") return [createMessage("user", message.content)];
1782
- if (!Array.isArray(message.content)) return [];
1783
- const items = [];
1784
- const pendingContent = [];
1785
- for (const block of message.content) {
1786
- if (block.type === "tool_result") {
1787
- flushPendingContent(pendingContent, items, { role: "user" });
1788
- items.push(createFunctionCallOutput(block));
1789
- continue;
1790
- }
1791
- const converted = translateUserContentBlock(block);
1792
- if (converted.length > 0) pendingContent.push(...converted);
1793
- }
1794
- flushPendingContent(pendingContent, items, { role: "user" });
1795
- return items;
1796
- };
1797
- const translateAssistantMessage = (message, model, applyPhase) => {
1798
- const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
1799
- if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
1800
- if (!Array.isArray(message.content)) return [];
1801
- const items = [];
1802
- const pendingContent = [];
1803
- for (const block of message.content) {
1804
- if (block.type === "tool_use") {
1805
- flushPendingContent(pendingContent, items, {
1806
- role: "assistant",
1807
- phase: assistantPhase
1808
- });
1809
- items.push(createFunctionToolCall(block));
1810
- continue;
1811
- }
1812
- if (block.type === "thinking" && block.signature) {
1813
- const compactionContent = createCompactionContent(block);
1814
- if (compactionContent) {
1815
- flushPendingContent(pendingContent, items, {
1816
- role: "assistant",
1817
- phase: assistantPhase
1846
+ }
1847
+ function getOpenAIChunkUsageTokens(chunk) {
1848
+ const promptTokens = chunk.usage?.prompt_tokens ?? 0;
1849
+ const cachedTokens = chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0;
1850
+ const cacheCreationTokens = chunk.usage?.prompt_tokens_details?.cache_creation_input_tokens ?? 0;
1851
+ return {
1852
+ cacheCreationTokens,
1853
+ cachedTokens,
1854
+ inputTokens: Math.max(0, promptTokens - cachedTokens - cacheCreationTokens)
1855
+ };
1856
+ }
1857
+ function handleToolCalls(delta, state$1, events$1) {
1858
+ if (delta.tool_calls && delta.tool_calls.length > 0) {
1859
+ closeThinkingBlockIfOpen(state$1, events$1);
1860
+ handleReasoningOpaqueInToolCalls(state$1, events$1, delta);
1861
+ for (const toolCall of delta.tool_calls) {
1862
+ if (toolCall.id && toolCall.function?.name) {
1863
+ if (state$1.contentBlockOpen) {
1864
+ events$1.push({
1865
+ type: "content_block_stop",
1866
+ index: state$1.contentBlockIndex
1867
+ });
1868
+ state$1.contentBlockIndex++;
1869
+ state$1.contentBlockOpen = false;
1870
+ }
1871
+ const anthropicBlockIndex = state$1.contentBlockIndex;
1872
+ state$1.toolCalls[toolCall.index] = {
1873
+ id: toolCall.id,
1874
+ name: toolCall.function.name,
1875
+ anthropicBlockIndex
1876
+ };
1877
+ events$1.push({
1878
+ type: "content_block_start",
1879
+ index: anthropicBlockIndex,
1880
+ content_block: {
1881
+ type: "tool_use",
1882
+ id: toolCall.id,
1883
+ name: toolCall.function.name,
1884
+ input: {}
1885
+ }
1818
1886
  });
1819
- items.push(compactionContent);
1820
- continue;
1887
+ state$1.contentBlockOpen = true;
1821
1888
  }
1822
- if (block.signature.includes("@")) {
1823
- flushPendingContent(pendingContent, items, {
1824
- role: "assistant",
1825
- phase: assistantPhase
1889
+ if (toolCall.function?.arguments) {
1890
+ const toolCallInfo = state$1.toolCalls[toolCall.index];
1891
+ if (toolCallInfo) events$1.push({
1892
+ type: "content_block_delta",
1893
+ index: toolCallInfo.anthropicBlockIndex,
1894
+ delta: {
1895
+ type: "input_json_delta",
1896
+ partial_json: toolCall.function.arguments
1897
+ }
1826
1898
  });
1827
- items.push(createReasoningContent(block));
1828
- continue;
1829
1899
  }
1830
1900
  }
1831
- const converted = translateAssistantContentBlock(block);
1832
- if (converted) pendingContent.push(converted);
1833
1901
  }
1834
- flushPendingContent(pendingContent, items, {
1835
- role: "assistant",
1836
- phase: assistantPhase
1837
- });
1838
- return items;
1839
- };
1840
- const translateUserContentBlock = (block) => {
1841
- switch (block.type) {
1842
- case "text": return [createTextContent(block.text)];
1843
- case "image": return [createImageContent(block)];
1844
- case "document": return [createFileContent(block)];
1845
- default: return [];
1902
+ }
1903
+ function handleReasoningOpaqueInToolCalls(state$1, events$1, delta) {
1904
+ if (state$1.contentBlockOpen && !isToolBlockOpen(state$1)) {
1905
+ events$1.push({
1906
+ type: "content_block_stop",
1907
+ index: state$1.contentBlockIndex
1908
+ });
1909
+ state$1.contentBlockIndex++;
1910
+ state$1.contentBlockOpen = false;
1846
1911
  }
1847
- };
1848
- const translateAssistantContentBlock = (block) => {
1849
- switch (block.type) {
1850
- case "text": return createOutPutTextContent(block.text);
1851
- default: return;
1912
+ handleReasoningOpaque(delta, events$1, state$1);
1913
+ }
1914
+ function handleContent(delta, state$1, events$1) {
1915
+ if (delta.content && delta.content.length > 0) {
1916
+ closeThinkingBlockIfOpen(state$1, events$1);
1917
+ if (isToolBlockOpen(state$1)) {
1918
+ events$1.push({
1919
+ type: "content_block_stop",
1920
+ index: state$1.contentBlockIndex
1921
+ });
1922
+ state$1.contentBlockIndex++;
1923
+ state$1.contentBlockOpen = false;
1924
+ }
1925
+ if (!state$1.contentBlockOpen) {
1926
+ events$1.push({
1927
+ type: "content_block_start",
1928
+ index: state$1.contentBlockIndex,
1929
+ content_block: {
1930
+ type: "text",
1931
+ text: ""
1932
+ }
1933
+ });
1934
+ state$1.contentBlockOpen = true;
1935
+ }
1936
+ events$1.push({
1937
+ type: "content_block_delta",
1938
+ index: state$1.contentBlockIndex,
1939
+ delta: {
1940
+ type: "text_delta",
1941
+ text: delta.content
1942
+ }
1943
+ });
1852
1944
  }
1853
- };
1854
- const flushPendingContent = (pendingContent, target, message) => {
1855
- if (pendingContent.length === 0) return;
1856
- const messageContent = [...pendingContent];
1857
- target.push(createMessage(message.role, messageContent, message.phase));
1858
- pendingContent.length = 0;
1859
- };
1860
- const createMessage = (role, content, phase) => ({
1861
- type: MESSAGE_TYPE,
1862
- role,
1863
- content,
1864
- ...role === "assistant" && phase ? { phase } : {}
1865
- });
1866
- const resolveAssistantPhase = (_model, content, applyPhase) => {
1867
- if (!applyPhase) return;
1868
- if (typeof content === "string") return "final_answer";
1869
- if (!Array.isArray(content)) return;
1870
- if (!content.some((block) => block.type === "text")) return;
1871
- return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
1872
- };
1873
- const shouldApplyPhase = (model) => {
1874
- return getExtraPromptForModel(model).includes("## Intermediary updates");
1875
- };
1876
- const createTextContent = (text) => ({
1877
- type: "input_text",
1878
- text
1879
- });
1880
- const createOutPutTextContent = (text) => ({
1881
- type: "output_text",
1882
- text
1883
- });
1884
- const createImageContent = (block) => ({
1885
- type: "input_image",
1886
- image_url: `data:${block.source.media_type};base64,${block.source.data}`,
1887
- detail: "auto"
1888
- });
1889
- const createFileContent = (block) => ({
1890
- type: "input_file",
1891
- file_data: `data:${block.source.media_type};base64,${block.source.data}`,
1892
- filename: block.title ?? "document.pdf"
1893
- });
1894
- const createReasoningContent = (block) => {
1895
- const { encryptedContent, id } = parseReasoningSignature(block.signature);
1896
- const thinking = block.thinking === THINKING_TEXT$1 ? "" : block.thinking;
1897
- return {
1898
- id,
1899
- type: "reasoning",
1900
- summary: thinking ? [{
1901
- type: "summary_text",
1902
- text: thinking
1903
- }] : [],
1904
- encrypted_content: encryptedContent
1905
- };
1906
- };
1907
- const createCompactionContent = (block) => {
1908
- const compaction = decodeCompactionCarrierSignature(block.signature);
1909
- if (!compaction) return;
1910
- return {
1911
- id: compaction.id,
1912
- type: "compaction",
1913
- encrypted_content: compaction.encrypted_content
1914
- };
1915
- };
1916
- const parseReasoningSignature = (signature) => {
1917
- const splitIndex = signature.lastIndexOf("@");
1918
- if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
1919
- encryptedContent: signature,
1920
- id: ""
1921
- };
1922
- return {
1923
- encryptedContent: signature.slice(0, splitIndex),
1924
- id: signature.slice(splitIndex + 1)
1925
- };
1926
- };
1927
- const createFunctionToolCall = (block) => ({
1928
- type: "function_call",
1929
- call_id: block.id,
1930
- name: block.name,
1931
- arguments: JSON.stringify(block.input),
1932
- status: "completed"
1933
- });
1934
- const createFunctionCallOutput = (block) => ({
1935
- type: "function_call_output",
1936
- call_id: block.tool_use_id,
1937
- output: convertToolResultContent(block.content),
1938
- status: block.is_error ? "incomplete" : "completed"
1939
- });
1940
- const translateSystemPrompt = (system, model) => {
1941
- if (!system) return null;
1942
- const extraPrompt = getExtraPromptForModel(model);
1943
- if (typeof system === "string") return system + extraPrompt;
1944
- const text = system.map((block, index) => {
1945
- if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
1946
- return block.text;
1947
- }).join(" ");
1948
- return text.length > 0 ? text : null;
1949
- };
1950
- const convertAnthropicTools = (tools) => {
1951
- if (!tools || tools.length === 0) return null;
1952
- return tools.map((tool) => ({
1953
- type: "function",
1954
- name: tool.name,
1955
- parameters: normalizeToolSchema(tool.input_schema),
1956
- strict: false,
1957
- ...tool.description ? { description: tool.description } : {}
1958
- }));
1959
- };
1960
- const convertAnthropicToolChoice = (choice) => {
1961
- if (!choice) return "auto";
1962
- switch (choice.type) {
1963
- case "auto": return "auto";
1964
- case "any": return "required";
1965
- case "tool": return choice.name ? {
1966
- type: "function",
1967
- name: choice.name
1968
- } : "auto";
1969
- case "none": return "none";
1970
- default: return "auto";
1971
- }
1972
- };
1973
- const translateResponsesResultToAnthropic = (response) => {
1974
- const contentBlocks = mapOutputToAnthropicContent(response.output);
1975
- const usage = mapResponsesUsage(response);
1976
- let anthropicContent = fallbackContentBlocks(response.output_text);
1977
- if (contentBlocks.length > 0) anthropicContent = contentBlocks;
1978
- const stopReason = mapResponsesStopReason(response);
1979
- return {
1980
- id: response.id,
1981
- type: "message",
1982
- role: "assistant",
1983
- content: anthropicContent,
1984
- model: response.model,
1985
- stop_reason: stopReason,
1986
- stop_sequence: null,
1987
- usage
1988
- };
1989
- };
1990
- const mapOutputToAnthropicContent = (output) => {
1991
- const contentBlocks = [];
1992
- for (const item of output) switch (item.type) {
1993
- case "reasoning": {
1994
- const thinkingText = extractReasoningText(item);
1995
- if (thinkingText.length > 0) contentBlocks.push({
1996
- type: "thinking",
1997
- thinking: thinkingText,
1998
- signature: (item.encrypted_content ?? "") + "@" + item.id
1999
- });
2000
- break;
2001
- }
2002
- case "function_call": {
2003
- const toolUseBlock = createToolUseContentBlock(item);
2004
- if (toolUseBlock) contentBlocks.push(toolUseBlock);
2005
- break;
2006
- }
2007
- case "message": {
2008
- const combinedText = combineMessageTextContent(item.content);
2009
- if (combinedText.length > 0) contentBlocks.push({
2010
- type: "text",
2011
- text: combinedText
2012
- });
2013
- break;
2014
- }
2015
- case "compaction": {
2016
- const compactionBlock = createCompactionThinkingBlock(item);
2017
- if (compactionBlock) contentBlocks.push(compactionBlock);
2018
- break;
2019
- }
2020
- default: {
2021
- const combinedText = combineMessageTextContent(item.content);
2022
- if (combinedText.length > 0) contentBlocks.push({
2023
- type: "text",
2024
- text: combinedText
2025
- });
2026
- }
2027
- }
2028
- return contentBlocks;
2029
- };
2030
- const combineMessageTextContent = (content) => {
2031
- if (!Array.isArray(content)) return "";
2032
- let aggregated = "";
2033
- for (const block of content) {
2034
- if (isResponseOutputText(block)) {
2035
- aggregated += block.text;
2036
- continue;
2037
- }
2038
- if (isResponseOutputRefusal(block)) {
2039
- aggregated += block.refusal;
2040
- continue;
2041
- }
2042
- if (typeof block.text === "string") {
2043
- aggregated += block.text;
2044
- continue;
2045
- }
2046
- if (typeof block.reasoning === "string") {
2047
- aggregated += block.reasoning;
2048
- continue;
2049
- }
2050
- }
2051
- return aggregated;
2052
- };
2053
- const extractReasoningText = (item) => {
2054
- const segments = [];
2055
- const collectFromBlocks = (blocks) => {
2056
- if (!Array.isArray(blocks)) return;
2057
- for (const block of blocks) if (typeof block.text === "string") {
2058
- segments.push(block.text);
2059
- continue;
2060
- }
2061
- };
2062
- if (!item.summary || item.summary.length === 0) return THINKING_TEXT$1;
2063
- collectFromBlocks(item.summary);
2064
- return segments.join("").trim();
2065
- };
2066
- const createToolUseContentBlock = (call) => {
2067
- const toolId = call.call_id;
2068
- if (!call.name || !toolId) return null;
2069
- const input = parseFunctionCallArguments(call.arguments);
2070
- return {
2071
- type: "tool_use",
2072
- id: toolId,
2073
- name: call.name,
2074
- input
2075
- };
2076
- };
2077
- const createCompactionThinkingBlock = (item) => {
2078
- if (!item.id || !item.encrypted_content) return null;
2079
- return {
2080
- type: "thinking",
2081
- thinking: THINKING_TEXT$1,
2082
- signature: encodeCompactionCarrierSignature({
2083
- id: item.id,
2084
- encrypted_content: item.encrypted_content
2085
- })
2086
- };
2087
- };
2088
- const parseFunctionCallArguments = (rawArguments) => {
2089
- if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
2090
- try {
2091
- const parsed = JSON.parse(rawArguments);
2092
- if (Array.isArray(parsed)) return { arguments: parsed };
2093
- if (parsed && typeof parsed === "object") return parsed;
2094
- } catch (error) {
2095
- consola.warn("Failed to parse function call arguments", {
2096
- error,
2097
- rawArguments
2098
- });
2099
- }
2100
- return { raw_arguments: rawArguments };
2101
- };
2102
- const fallbackContentBlocks = (outputText) => {
2103
- if (!outputText) return [];
2104
- return [{
2105
- type: "text",
2106
- text: outputText
2107
- }];
2108
- };
2109
- const mapResponsesStopReason = (response) => {
2110
- const { status, incomplete_details: incompleteDetails } = response;
2111
- if (status === "completed") {
2112
- if (response.output.some((item) => item.type === "function_call")) return "tool_use";
2113
- return "end_turn";
2114
- }
2115
- if (status === "incomplete") {
2116
- if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
2117
- if (incompleteDetails?.reason === "content_filter") return "end_turn";
2118
- }
2119
- return null;
2120
- };
2121
- const mapResponsesUsage = (response) => {
2122
- const inputTokens = response.usage?.input_tokens ?? 0;
2123
- const outputTokens = response.usage?.output_tokens ?? 0;
2124
- const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
2125
- return {
2126
- input_tokens: inputTokens - (inputCachedTokens ?? 0),
2127
- output_tokens: outputTokens,
2128
- ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
2129
- };
2130
- };
2131
- const isRecord = (value) => typeof value === "object" && value !== null;
2132
- const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
2133
- const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
2134
- const convertToolResultContent = (content) => {
2135
- if (typeof content === "string") return content;
2136
- if (Array.isArray(content)) {
2137
- const result = [];
2138
- for (const block of content) switch (block.type) {
2139
- case "text":
2140
- result.push(createTextContent(block.text));
2141
- break;
2142
- case "image":
2143
- result.push(createImageContent(block));
2144
- break;
2145
- case "document":
2146
- result.push(createFileContent(block));
2147
- break;
2148
- case "tool_reference":
2149
- result.push(createTextContent(`Tool ${block.tool_name} loaded`));
2150
- break;
2151
- default: break;
2152
- }
2153
- return result;
2154
- }
2155
- return "";
2156
- };
2157
-
2158
- //#endregion
2159
- //#region src/routes/messages/responses-stream-translation.ts
2160
- const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
2161
- var FunctionCallArgumentsValidationError = class extends Error {
2162
- constructor(message) {
2163
- super(message);
2164
- this.name = "FunctionCallArgumentsValidationError";
2165
- }
2166
- };
2167
- const updateWhitespaceRunState = (previousCount, chunk) => {
2168
- let count = previousCount;
2169
- for (const char of chunk) {
2170
- if (char === "\r" || char === "\n" || char === " ") {
2171
- count += 1;
2172
- if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
2173
- nextCount: count,
2174
- exceeded: true
2175
- };
2176
- continue;
2177
- }
2178
- if (char !== " ") count = 0;
2179
- }
2180
- return {
2181
- nextCount: count,
2182
- exceeded: false
2183
- };
2184
- };
2185
- const createResponsesStreamState = () => ({
2186
- messageStartSent: false,
2187
- messageCompleted: false,
2188
- nextContentBlockIndex: 0,
2189
- blockIndexByKey: /* @__PURE__ */ new Map(),
2190
- openBlocks: /* @__PURE__ */ new Set(),
2191
- blockHasDelta: /* @__PURE__ */ new Set(),
2192
- functionCallStateByOutputIndex: /* @__PURE__ */ new Map()
2193
- });
2194
- const translateResponsesStreamEvent = (rawEvent, state$1) => {
2195
- switch (rawEvent.type) {
2196
- case "response.created": return handleResponseCreated(rawEvent, state$1);
2197
- case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state$1);
2198
- case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state$1);
2199
- case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state$1);
2200
- case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state$1);
2201
- case "response.output_text.done": return handleOutputTextDone(rawEvent, state$1);
2202
- case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state$1);
2203
- case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state$1);
2204
- case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state$1);
2205
- case "response.completed":
2206
- case "response.incomplete": return handleResponseCompleted(rawEvent, state$1);
2207
- case "response.failed": return handleResponseFailed(rawEvent, state$1);
2208
- case "error": return handleErrorEvent(rawEvent, state$1);
2209
- default: return [];
2210
- }
2211
- };
2212
- const handleResponseCreated = (rawEvent, state$1) => {
2213
- return messageStart(state$1, rawEvent.response);
2214
- };
2215
- const handleOutputItemAdded$1 = (rawEvent, state$1) => {
2216
- const events$1 = new Array();
2217
- const functionCallDetails = extractFunctionCallDetails(rawEvent);
2218
- if (!functionCallDetails) return events$1;
2219
- const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
2220
- const blockIndex = openFunctionCallBlock(state$1, {
2221
- outputIndex,
2222
- toolCallId,
2223
- name,
2224
- events: events$1
2225
- });
2226
- if (initialArguments !== void 0 && initialArguments.length > 0) {
1945
+ if (delta.content === "" && delta.reasoning_opaque && delta.reasoning_opaque.length > 0 && state$1.thinkingBlockOpen) {
2227
1946
  events$1.push({
2228
1947
  type: "content_block_delta",
2229
- index: blockIndex,
1948
+ index: state$1.contentBlockIndex,
2230
1949
  delta: {
2231
- type: "input_json_delta",
2232
- partial_json: initialArguments
1950
+ type: "signature_delta",
1951
+ signature: delta.reasoning_opaque
2233
1952
  }
1953
+ }, {
1954
+ type: "content_block_stop",
1955
+ index: state$1.contentBlockIndex
2234
1956
  });
2235
- state$1.blockHasDelta.add(blockIndex);
1957
+ state$1.contentBlockIndex++;
1958
+ state$1.thinkingBlockOpen = false;
2236
1959
  }
2237
- return events$1;
2238
- };
2239
- const handleOutputItemDone$1 = (rawEvent, state$1) => {
2240
- const events$1 = new Array();
2241
- const item = rawEvent.item;
2242
- const itemType = item.type;
2243
- const outputIndex = rawEvent.output_index;
2244
- if (itemType === "compaction") {
2245
- if (!item.id || !item.encrypted_content) return events$1;
2246
- const blockIndex$1 = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2247
- if (!state$1.blockHasDelta.has(blockIndex$1)) events$1.push({
1960
+ }
1961
+ function handleMessageStart(state$1, events$1, chunk) {
1962
+ if (!state$1.messageStartSent) {
1963
+ const { cachedTokens, cacheCreationTokens, inputTokens } = getOpenAIChunkUsageTokens(chunk);
1964
+ events$1.push({
1965
+ type: "message_start",
1966
+ message: {
1967
+ id: chunk.id,
1968
+ type: "message",
1969
+ role: "assistant",
1970
+ content: [],
1971
+ model: chunk.model,
1972
+ stop_reason: null,
1973
+ stop_sequence: null,
1974
+ usage: {
1975
+ input_tokens: inputTokens,
1976
+ output_tokens: 0,
1977
+ ...chunk.usage?.prompt_tokens_details?.cache_creation_input_tokens !== void 0 && { cache_creation_input_tokens: cacheCreationTokens },
1978
+ ...chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: cachedTokens }
1979
+ }
1980
+ }
1981
+ });
1982
+ state$1.messageStartSent = true;
1983
+ }
1984
+ }
1985
+ function handleReasoningOpaque(delta, events$1, state$1) {
1986
+ if (delta.reasoning_opaque && delta.reasoning_opaque.length > 0) {
1987
+ events$1.push({
1988
+ type: "content_block_start",
1989
+ index: state$1.contentBlockIndex,
1990
+ content_block: {
1991
+ type: "thinking",
1992
+ thinking: ""
1993
+ }
1994
+ }, {
2248
1995
  type: "content_block_delta",
2249
- index: blockIndex$1,
1996
+ index: state$1.contentBlockIndex,
2250
1997
  delta: {
2251
1998
  type: "thinking_delta",
2252
1999
  thinking: THINKING_TEXT$1
2253
2000
  }
2254
- });
2255
- events$1.push({
2001
+ }, {
2256
2002
  type: "content_block_delta",
2257
- index: blockIndex$1,
2003
+ index: state$1.contentBlockIndex,
2258
2004
  delta: {
2259
2005
  type: "signature_delta",
2260
- signature: encodeCompactionCarrierSignature({
2261
- id: item.id,
2262
- encrypted_content: item.encrypted_content
2263
- })
2006
+ signature: delta.reasoning_opaque
2264
2007
  }
2008
+ }, {
2009
+ type: "content_block_stop",
2010
+ index: state$1.contentBlockIndex
2265
2011
  });
2266
- state$1.blockHasDelta.add(blockIndex$1);
2267
- return events$1;
2012
+ state$1.contentBlockIndex++;
2268
2013
  }
2269
- if (itemType !== "reasoning") return events$1;
2270
- const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2271
- const signature = (item.encrypted_content ?? "") + "@" + item.id;
2272
- if (signature) {
2273
- if (!item.summary || item.summary.length === 0) events$1.push({
2014
+ }
2015
+ function handleThinkingText(delta, state$1, events$1) {
2016
+ const reasoningText = delta.reasoning_text ?? delta.reasoning_content;
2017
+ if (reasoningText && reasoningText.length > 0) {
2018
+ if (state$1.contentBlockOpen) {
2019
+ delta.content = reasoningText;
2020
+ delta.reasoning_text = void 0;
2021
+ delta.reasoning_content = void 0;
2022
+ return;
2023
+ }
2024
+ if (!state$1.thinkingBlockOpen) {
2025
+ events$1.push({
2026
+ type: "content_block_start",
2027
+ index: state$1.contentBlockIndex,
2028
+ content_block: {
2029
+ type: "thinking",
2030
+ thinking: ""
2031
+ }
2032
+ });
2033
+ state$1.thinkingBlockOpen = true;
2034
+ }
2035
+ events$1.push({
2274
2036
  type: "content_block_delta",
2275
- index: blockIndex,
2037
+ index: state$1.contentBlockIndex,
2276
2038
  delta: {
2277
2039
  type: "thinking_delta",
2278
- thinking: THINKING_TEXT$1
2040
+ thinking: reasoningText
2279
2041
  }
2280
2042
  });
2043
+ }
2044
+ }
2045
+ function closeThinkingBlockIfOpen(state$1, events$1) {
2046
+ if (state$1.thinkingBlockOpen) {
2281
2047
  events$1.push({
2282
2048
  type: "content_block_delta",
2283
- index: blockIndex,
2049
+ index: state$1.contentBlockIndex,
2284
2050
  delta: {
2285
2051
  type: "signature_delta",
2286
- signature
2052
+ signature: ""
2287
2053
  }
2054
+ }, {
2055
+ type: "content_block_stop",
2056
+ index: state$1.contentBlockIndex
2288
2057
  });
2289
- state$1.blockHasDelta.add(blockIndex);
2058
+ state$1.contentBlockIndex++;
2059
+ state$1.thinkingBlockOpen = false;
2290
2060
  }
2291
- return events$1;
2061
+ }
2062
+
2063
+ //#endregion
2064
+ //#region src/services/providers/anthropic-proxy.ts
2065
+ const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
2066
+ const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
2067
+ const STRIPPED_RESPONSE_HEADERS = [
2068
+ "connection",
2069
+ "content-encoding",
2070
+ "content-length",
2071
+ "keep-alive",
2072
+ "proxy-authenticate",
2073
+ "proxy-authorization",
2074
+ "te",
2075
+ "trailer",
2076
+ "transfer-encoding",
2077
+ "upgrade"
2078
+ ];
2079
+ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
2080
+ const authHeaders = {};
2081
+ if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
2082
+ else authHeaders["x-api-key"] = providerConfig.apiKey;
2083
+ const headers = {
2084
+ "content-type": "application/json",
2085
+ accept: "application/json",
2086
+ ...authHeaders
2087
+ };
2088
+ for (const headerName of SHARED_FORWARDABLE_HEADERS) {
2089
+ const headerValue = requestHeaders.get(headerName);
2090
+ if (headerValue) headers[headerName] = headerValue;
2091
+ }
2092
+ if (providerConfig.type !== "anthropic") return headers;
2093
+ for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
2094
+ const headerValue = requestHeaders.get(headerName);
2095
+ if (headerValue) headers[headerName] = headerValue;
2096
+ }
2097
+ return headers;
2098
+ }
2099
+ function createProviderProxyResponse(upstreamResponse) {
2100
+ const headers = new Headers(upstreamResponse.headers);
2101
+ for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
2102
+ return new Response(upstreamResponse.body, {
2103
+ headers,
2104
+ status: upstreamResponse.status,
2105
+ statusText: upstreamResponse.statusText
2106
+ });
2107
+ }
2108
+ async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
2109
+ return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
2110
+ method: "POST",
2111
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2112
+ body: JSON.stringify(payload)
2113
+ });
2114
+ }
2115
+ async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
2116
+ return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
2117
+ method: "POST",
2118
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2119
+ body: JSON.stringify(payload)
2120
+ });
2121
+ }
2122
+ async function forwardProviderModels(providerConfig, requestHeaders) {
2123
+ return await fetch(`${providerConfig.baseUrl}/v1/models`, {
2124
+ method: "GET",
2125
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
2126
+ });
2127
+ }
2128
+
2129
+ //#endregion
2130
+ //#region src/routes/provider/messages/handler.ts
2131
+ const logger$4 = createHandlerLogger("provider-messages-handler");
2132
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
2133
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
2134
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
2135
+ "system",
2136
+ "user",
2137
+ "assistant",
2138
+ "tool"
2139
+ ]);
2140
+ async function handleProviderMessages(c) {
2141
+ const provider = c.req.param("provider");
2142
+ const payload = await c.req.json();
2143
+ return await handleProviderMessagesForProvider(c, {
2144
+ payload,
2145
+ provider
2146
+ });
2147
+ }
2148
+ async function handleProviderMessagesForProvider(c, options) {
2149
+ const { payload, provider } = options;
2150
+ const providerConfig = getProviderConfig(provider);
2151
+ if (!providerConfig) return c.json({ error: {
2152
+ message: `Provider '${provider}' not found or disabled`,
2153
+ type: "invalid_request_error"
2154
+ } }, 404);
2155
+ try {
2156
+ const modelConfig = providerConfig.models?.[payload.model];
2157
+ applyModelDefaults(payload, modelConfig);
2158
+ debugJson(logger$4, "provider.messages.request", {
2159
+ payload,
2160
+ provider
2161
+ });
2162
+ if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
2163
+ modelConfig,
2164
+ payload,
2165
+ provider,
2166
+ providerConfig
2167
+ });
2168
+ applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
2169
+ const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
2170
+ if (!upstreamResponse.ok) {
2171
+ logger$4.error("Failed to create responses", upstreamResponse);
2172
+ throw new HTTPError("Failed to create responses", upstreamResponse);
2173
+ }
2174
+ const contentType = upstreamResponse.headers.get("content-type") ?? "";
2175
+ if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
2176
+ c,
2177
+ payload,
2178
+ provider,
2179
+ providerConfig,
2180
+ upstreamResponse
2181
+ });
2182
+ const jsonBody = await upstreamResponse.json();
2183
+ return respondProviderMessagesJson(c, {
2184
+ body: jsonBody,
2185
+ payload,
2186
+ provider,
2187
+ providerConfig
2188
+ });
2189
+ } catch (error) {
2190
+ logger$4.error("provider.messages.error", {
2191
+ provider,
2192
+ error
2193
+ });
2194
+ throw error;
2195
+ }
2196
+ }
2197
+ const applyModelDefaults = (payload, modelConfig) => {
2198
+ payload.temperature ??= modelConfig?.temperature;
2199
+ payload.top_p ??= modelConfig?.topP;
2200
+ payload.top_k ??= modelConfig?.topK;
2201
+ };
2202
+ const applyMissingExtraBody = (payload, options) => {
2203
+ for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
2292
2204
  };
2293
- const handleFunctionCallArgumentsDelta = (rawEvent, state$1) => {
2294
- const events$1 = new Array();
2295
- const outputIndex = rawEvent.output_index;
2296
- const deltaText = rawEvent.delta;
2297
- if (!deltaText) return events$1;
2298
- const blockIndex = openFunctionCallBlock(state$1, {
2299
- outputIndex,
2300
- events: events$1
2205
+ const handleOpenAICompatibleProviderMessages = async (c, options) => {
2206
+ const { modelConfig, payload, provider, providerConfig } = options;
2207
+ const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
2208
+ debugJson(logger$4, "provider.messages.openai_compatible.request", {
2209
+ payload: openAIPayload,
2210
+ provider
2301
2211
  });
2302
- const functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
2303
- if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state$1, events$1);
2304
- const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
2305
- if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state$1, events$1);
2306
- functionCallState.consecutiveWhitespaceCount = nextCount;
2307
- events$1.push({
2308
- type: "content_block_delta",
2309
- index: blockIndex,
2310
- delta: {
2311
- type: "input_json_delta",
2312
- partial_json: deltaText
2313
- }
2212
+ const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
2213
+ if (!upstreamResponse.ok) {
2214
+ logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
2215
+ throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
2216
+ }
2217
+ const contentType = upstreamResponse.headers.get("content-type") ?? "";
2218
+ if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
2219
+ c,
2220
+ payload,
2221
+ provider,
2222
+ upstreamResponse
2314
2223
  });
2315
- state$1.blockHasDelta.add(blockIndex);
2316
- return events$1;
2317
- };
2318
- const handleFunctionCallArgumentsDone = (rawEvent, state$1) => {
2319
- const events$1 = new Array();
2320
- const outputIndex = rawEvent.output_index;
2321
- const blockIndex = openFunctionCallBlock(state$1, {
2322
- outputIndex,
2323
- events: events$1
2224
+ const jsonBody = await upstreamResponse.json();
2225
+ return respondOpenAICompatibleProviderMessagesJson(c, {
2226
+ body: jsonBody,
2227
+ payload,
2228
+ provider
2324
2229
  });
2325
- const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
2326
- if (!state$1.blockHasDelta.has(blockIndex) && finalArguments) {
2327
- events$1.push({
2328
- type: "content_block_delta",
2329
- index: blockIndex,
2330
- delta: {
2331
- type: "input_json_delta",
2332
- partial_json: finalArguments
2333
- }
2334
- });
2335
- state$1.blockHasDelta.add(blockIndex);
2336
- }
2337
- state$1.functionCallStateByOutputIndex.delete(outputIndex);
2338
- return events$1;
2339
2230
  };
2340
- const handleOutputTextDelta = (rawEvent, state$1) => {
2341
- const events$1 = new Array();
2342
- const outputIndex = rawEvent.output_index;
2343
- const contentIndex = rawEvent.content_index;
2344
- const deltaText = rawEvent.delta;
2345
- if (!deltaText) return events$1;
2346
- const blockIndex = openTextBlockIfNeeded(state$1, {
2347
- outputIndex,
2348
- contentIndex,
2349
- events: events$1
2231
+ const createOpenAICompatiblePayload = (payload, modelConfig) => {
2232
+ const openAIPayload = translateToOpenAI(payload, {
2233
+ supportPdf: modelConfig?.supportPdf,
2234
+ toolContentSupportType: modelConfig?.toolContentSupportType ?? []
2350
2235
  });
2351
- events$1.push({
2352
- type: "content_block_delta",
2353
- index: blockIndex,
2354
- delta: {
2355
- type: "text_delta",
2356
- text: deltaText
2357
- }
2236
+ if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
2237
+ if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
2238
+ normalizeOpenAICompatibleReasoningContent(openAIPayload);
2239
+ applyOpenAICompatibleRequestOverrides(openAIPayload, {
2240
+ extraBody: modelConfig?.extraBody,
2241
+ source: payload
2358
2242
  });
2359
- state$1.blockHasDelta.add(blockIndex);
2360
- return events$1;
2243
+ applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
2244
+ if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
2245
+ if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
2246
+ return openAIPayload;
2361
2247
  };
2362
- const handleReasoningSummaryTextDelta = (rawEvent, state$1) => {
2363
- const outputIndex = rawEvent.output_index;
2364
- const deltaText = rawEvent.delta;
2365
- const events$1 = new Array();
2366
- const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2367
- events$1.push({
2368
- type: "content_block_delta",
2369
- index: blockIndex,
2370
- delta: {
2371
- type: "thinking_delta",
2372
- thinking: deltaText
2373
- }
2374
- });
2375
- state$1.blockHasDelta.add(blockIndex);
2376
- return events$1;
2248
+ const normalizeOpenAICompatibleReasoningContent = (payload) => {
2249
+ for (const message of payload.messages) {
2250
+ if (message.role !== "assistant") continue;
2251
+ if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
2252
+ delete message.reasoning_text;
2253
+ delete message.reasoning_opaque;
2254
+ }
2377
2255
  };
2378
- const handleReasoningSummaryTextDone = (rawEvent, state$1) => {
2379
- const outputIndex = rawEvent.output_index;
2380
- const text = rawEvent.text;
2381
- const events$1 = new Array();
2382
- const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2383
- if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
2384
- type: "content_block_delta",
2385
- index: blockIndex,
2386
- delta: {
2387
- type: "thinking_delta",
2388
- thinking: text
2389
- }
2390
- });
2391
- return events$1;
2256
+ const applyOpenAICompatibleRequestOverrides = (payload, options) => {
2257
+ const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
2258
+ for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
2392
2259
  };
2393
- const handleOutputTextDone = (rawEvent, state$1) => {
2394
- const events$1 = new Array();
2395
- const outputIndex = rawEvent.output_index;
2396
- const contentIndex = rawEvent.content_index;
2397
- const text = rawEvent.text;
2398
- const blockIndex = openTextBlockIfNeeded(state$1, {
2399
- outputIndex,
2400
- contentIndex,
2401
- events: events$1
2402
- });
2403
- if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
2404
- type: "content_block_delta",
2405
- index: blockIndex,
2406
- delta: {
2407
- type: "text_delta",
2408
- text
2409
- }
2410
- });
2411
- return events$1;
2260
+ const applyOpenAICompatibleContextCache = (payload) => {
2261
+ const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
2262
+ for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
2412
2263
  };
2413
- const handleResponseCompleted = (rawEvent, state$1) => {
2414
- const response = rawEvent.response;
2415
- const events$1 = new Array();
2416
- closeAllOpenBlocks(state$1, events$1);
2417
- const anthropic = translateResponsesResultToAnthropic(response);
2418
- events$1.push({
2419
- type: "message_delta",
2420
- delta: {
2421
- stop_reason: anthropic.stop_reason,
2422
- stop_sequence: anthropic.stop_sequence
2423
- },
2424
- usage: anthropic.usage
2425
- }, { type: "message_stop" });
2426
- state$1.messageCompleted = true;
2427
- return events$1;
2264
+ const selectContextCacheMessageIndexes = (messages) => {
2265
+ const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
2266
+ const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
2267
+ const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
2268
+ return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
2428
2269
  };
2429
- const handleResponseFailed = (rawEvent, state$1) => {
2430
- const response = rawEvent.response;
2431
- const events$1 = new Array();
2432
- closeAllOpenBlocks(state$1, events$1);
2433
- const message = response.error?.message ?? "The response failed due to an unknown error.";
2434
- events$1.push(buildErrorEvent(message));
2435
- state$1.messageCompleted = true;
2436
- return events$1;
2270
+ const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
2271
+ const isContextCacheMarkerEligible = (message) => {
2272
+ if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
2273
+ if (typeof message.content === "string") return message.content.length > 0;
2274
+ return Array.isArray(message.content) && message.content.length > 0;
2437
2275
  };
2438
- const handleErrorEvent = (rawEvent, state$1) => {
2439
- const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
2440
- state$1.messageCompleted = true;
2441
- return [buildErrorEvent(message)];
2276
+ const applyContextCacheControl = (message) => {
2277
+ if (!message) return;
2278
+ if (typeof message.content === "string") {
2279
+ message.content = [{
2280
+ type: "text",
2281
+ text: message.content,
2282
+ cache_control: { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL }
2283
+ }];
2284
+ return;
2285
+ }
2286
+ if (!Array.isArray(message.content)) return;
2287
+ const lastPart = message.content.at(-1);
2288
+ if (!lastPart) return;
2289
+ setContextCacheControl(lastPart);
2442
2290
  };
2443
- const handleFunctionCallArgumentsValidationError = (error, state$1, events$1 = []) => {
2444
- const reason = error.message;
2445
- closeAllOpenBlocks(state$1, events$1);
2446
- state$1.messageCompleted = true;
2447
- events$1.push(buildErrorEvent(reason));
2448
- return events$1;
2291
+ const setContextCacheControl = (part) => {
2292
+ part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
2449
2293
  };
2450
- const messageStart = (state$1, response) => {
2451
- state$1.messageStartSent = true;
2452
- const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
2453
- const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
2454
- return [{
2455
- type: "message_start",
2456
- message: {
2457
- id: response.id,
2458
- type: "message",
2459
- role: "assistant",
2460
- content: [],
2461
- model: response.model,
2462
- stop_reason: null,
2463
- stop_sequence: null,
2464
- usage: {
2465
- input_tokens: inputTokens,
2466
- output_tokens: 0,
2467
- cache_read_input_tokens: inputCachedTokens ?? 0
2294
+ const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
2295
+ logger$4.debug("provider.messages.streaming");
2296
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2297
+ return streamSSE(c, async (stream) => {
2298
+ let usage = {};
2299
+ for await (const chunk of events(upstreamResponse)) {
2300
+ logger$4.debug("provider.messages.raw_stream_event:", chunk.data);
2301
+ const eventName = chunk.event;
2302
+ if (eventName === "ping") {
2303
+ await stream.writeSSE({
2304
+ event: "ping",
2305
+ data: "{\"type\":\"ping\"}"
2306
+ });
2307
+ continue;
2308
+ }
2309
+ let data = chunk.data;
2310
+ if (!data) continue;
2311
+ if (chunk.data === "[DONE]") break;
2312
+ const parsed = parseProviderStreamEvent(data, providerConfig);
2313
+ if (parsed) {
2314
+ usage = mergeAnthropicUsage(usage, parsed.usage);
2315
+ data = parsed.data;
2468
2316
  }
2317
+ await stream.writeSSE({
2318
+ event: eventName,
2319
+ data
2320
+ });
2469
2321
  }
2470
- }];
2322
+ recordUsage(usage);
2323
+ });
2471
2324
  };
2472
- const openTextBlockIfNeeded = (state$1, params) => {
2473
- const { outputIndex, contentIndex, events: events$1 } = params;
2474
- const key = getBlockKey(outputIndex, contentIndex);
2475
- let blockIndex = state$1.blockIndexByKey.get(key);
2476
- if (blockIndex === void 0) {
2477
- blockIndex = state$1.nextContentBlockIndex;
2478
- state$1.nextContentBlockIndex += 1;
2479
- state$1.blockIndexByKey.set(key, blockIndex);
2480
- }
2481
- if (!state$1.openBlocks.has(blockIndex)) {
2482
- closeOpenBlocks(state$1, events$1);
2483
- events$1.push({
2484
- type: "content_block_start",
2485
- index: blockIndex,
2486
- content_block: {
2487
- type: "text",
2488
- text: ""
2325
+ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
2326
+ logger$4.debug("provider.messages.openai_compatible.streaming");
2327
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2328
+ return streamSSE(c, async (stream) => {
2329
+ let usage = {};
2330
+ const streamState = {
2331
+ messageStartSent: false,
2332
+ contentBlockIndex: 0,
2333
+ contentBlockOpen: false,
2334
+ toolCalls: {},
2335
+ thinkingBlockOpen: false
2336
+ };
2337
+ for await (const chunk of events(upstreamResponse)) {
2338
+ logger$4.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
2339
+ if (chunk.event === "ping") {
2340
+ await stream.writeSSE({
2341
+ event: "ping",
2342
+ data: "{\"type\":\"ping\"}"
2343
+ });
2344
+ continue;
2489
2345
  }
2490
- });
2491
- state$1.openBlocks.add(blockIndex);
2492
- }
2493
- return blockIndex;
2494
- };
2495
- const openThinkingBlockIfNeeded = (state$1, outputIndex, events$1) => {
2496
- const key = getBlockKey(outputIndex, 0);
2497
- let blockIndex = state$1.blockIndexByKey.get(key);
2498
- if (blockIndex === void 0) {
2499
- blockIndex = state$1.nextContentBlockIndex;
2500
- state$1.nextContentBlockIndex += 1;
2501
- state$1.blockIndexByKey.set(key, blockIndex);
2502
- }
2503
- if (!state$1.openBlocks.has(blockIndex)) {
2504
- closeOpenBlocks(state$1, events$1);
2505
- events$1.push({
2506
- type: "content_block_start",
2507
- index: blockIndex,
2508
- content_block: {
2509
- type: "thinking",
2510
- thinking: ""
2346
+ if (!chunk.data || chunk.data === "[DONE]") {
2347
+ if (chunk.data === "[DONE]") break;
2348
+ continue;
2511
2349
  }
2512
- });
2513
- state$1.openBlocks.add(blockIndex);
2514
- }
2515
- return blockIndex;
2516
- };
2517
- const closeBlockIfOpen = (state$1, blockIndex, events$1) => {
2518
- if (!state$1.openBlocks.has(blockIndex)) return;
2519
- events$1.push({
2520
- type: "content_block_stop",
2521
- index: blockIndex
2350
+ const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
2351
+ if (!parsed) continue;
2352
+ if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
2353
+ const events$1 = translateChunkToAnthropicEvents(parsed, streamState);
2354
+ for (const event of events$1) {
2355
+ const eventData = JSON.stringify(event);
2356
+ debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
2357
+ await stream.writeSSE({
2358
+ event: event.type,
2359
+ data: eventData
2360
+ });
2361
+ }
2362
+ }
2363
+ for (const event of flushPendingAnthropicStreamEvents(streamState)) {
2364
+ const eventData = JSON.stringify(event);
2365
+ debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
2366
+ await stream.writeSSE({
2367
+ event: event.type,
2368
+ data: eventData
2369
+ });
2370
+ }
2371
+ recordUsage(usage);
2522
2372
  });
2523
- state$1.openBlocks.delete(blockIndex);
2524
- state$1.blockHasDelta.delete(blockIndex);
2525
- };
2526
- const closeOpenBlocks = (state$1, events$1) => {
2527
- for (const blockIndex of state$1.openBlocks) closeBlockIfOpen(state$1, blockIndex, events$1);
2528
- };
2529
- const closeAllOpenBlocks = (state$1, events$1) => {
2530
- closeOpenBlocks(state$1, events$1);
2531
- state$1.functionCallStateByOutputIndex.clear();
2532
2373
  };
2533
- const buildErrorEvent = (message) => ({
2534
- type: "error",
2535
- error: {
2536
- type: "api_error",
2537
- message
2374
+ const parseOpenAICompatibleStreamChunk = (data) => {
2375
+ try {
2376
+ return JSON.parse(data);
2377
+ } catch (error) {
2378
+ logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
2379
+ data,
2380
+ error
2381
+ });
2382
+ return null;
2538
2383
  }
2539
- });
2540
- const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
2541
- const openFunctionCallBlock = (state$1, params) => {
2542
- const { outputIndex, toolCallId, name, events: events$1 } = params;
2543
- let functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
2544
- if (!functionCallState) {
2545
- const blockIndex$1 = state$1.nextContentBlockIndex;
2546
- state$1.nextContentBlockIndex += 1;
2547
- const resolvedToolCallId = toolCallId ?? `tool_call_${blockIndex$1}`;
2548
- functionCallState = {
2549
- blockIndex: blockIndex$1,
2550
- toolCallId: resolvedToolCallId,
2551
- name: name ?? "function",
2552
- consecutiveWhitespaceCount: 0
2384
+ };
2385
+ const parseProviderStreamEvent = (data, providerConfig) => {
2386
+ try {
2387
+ const parsed = JSON.parse(data);
2388
+ if (parsed.type === "message_start") {
2389
+ adjustInputTokens(providerConfig, parsed.message.usage);
2390
+ return {
2391
+ data: JSON.stringify(parsed),
2392
+ model: parsed.message.model,
2393
+ usage: normalizeAnthropicUsage(parsed.message.usage)
2394
+ };
2395
+ }
2396
+ if (parsed.type === "message_delta") {
2397
+ adjustInputTokens(providerConfig, parsed.usage);
2398
+ return {
2399
+ data: JSON.stringify(parsed),
2400
+ usage: normalizeAnthropicUsage(parsed.usage)
2401
+ };
2402
+ }
2403
+ return {
2404
+ data: JSON.stringify(parsed),
2405
+ usage: {}
2553
2406
  };
2554
- state$1.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
2555
- }
2556
- const { blockIndex } = functionCallState;
2557
- if (!state$1.openBlocks.has(blockIndex)) {
2558
- closeOpenBlocks(state$1, events$1);
2559
- events$1.push({
2560
- type: "content_block_start",
2561
- index: blockIndex,
2562
- content_block: {
2563
- type: "tool_use",
2564
- id: functionCallState.toolCallId,
2565
- name: functionCallState.name,
2566
- input: {}
2567
- }
2407
+ } catch (error) {
2408
+ logger$4.error("provider.messages.streaming.adjust_tokens_error", {
2409
+ error,
2410
+ originalData: data
2568
2411
  });
2569
- state$1.openBlocks.add(blockIndex);
2412
+ return null;
2570
2413
  }
2571
- return blockIndex;
2572
- };
2573
- const extractFunctionCallDetails = (rawEvent) => {
2574
- const item = rawEvent.item;
2575
- if (item.type !== "function_call") return;
2576
- const outputIndex = rawEvent.output_index;
2577
- const toolCallId = item.call_id;
2578
- const name = item.name;
2579
- const initialArguments = item.arguments;
2580
- return {
2581
- outputIndex,
2582
- toolCallId,
2583
- name,
2584
- initialArguments
2585
- };
2586
- };
2587
-
2588
- //#endregion
2589
- //#region src/routes/responses/utils.ts
2590
- const getResponsesRequestOptions = (payload) => {
2591
- const vision = hasVisionInput(payload);
2592
- const initiator = hasAgentInitiator(payload) ? "agent" : "user";
2593
- return {
2594
- vision,
2595
- initiator
2596
- };
2597
- };
2598
- const hasAgentInitiator = (payload) => {
2599
- const lastItem = getPayloadItems(payload).at(-1);
2600
- if (!lastItem) return false;
2601
- if (!("role" in lastItem) || !lastItem.role) return true;
2602
- return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
2603
- };
2604
- const hasVisionInput = (payload) => {
2605
- return getPayloadItems(payload).some((item) => containsVisionContent(item));
2606
- };
2607
- const resolveResponsesCompactThreshold = (maxPromptTokens) => {
2608
- if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
2609
- return 5e4;
2610
- };
2611
- const createCompactionContextManagement = (compactThreshold) => [{
2612
- type: "compaction",
2613
- compact_threshold: compactThreshold
2614
- }];
2615
- const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
2616
- if (payload.context_management !== void 0) return;
2617
- if (!isResponsesApiContextManagementModel(payload.model)) return;
2618
- payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
2619
- };
2620
- const compactInputByLatestCompaction = (payload) => {
2621
- if (!Array.isArray(payload.input) || payload.input.length === 0) return;
2622
- const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
2623
- if (latestCompactionMessageIndex === void 0) return;
2624
- payload.input = payload.input.slice(latestCompactionMessageIndex);
2625
- };
2626
- const getLatestCompactionMessageIndex = (input) => {
2627
- for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
2628
2414
  };
2629
- const isCompactionInputItem = (value) => {
2630
- return "type" in value && typeof value.type === "string" && value.type === "compaction";
2415
+ const respondProviderMessagesJson = (c, options) => {
2416
+ const { body, payload, provider, providerConfig } = options;
2417
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2418
+ adjustInputTokens(providerConfig, body.usage);
2419
+ recordUsage(normalizeAnthropicUsage(body.usage));
2420
+ debugJson(logger$4, "provider.messages.no_stream result:", body);
2421
+ return c.json(body);
2631
2422
  };
2632
- const getPayloadItems = (payload) => {
2633
- const result = [];
2634
- const { input } = payload;
2635
- if (Array.isArray(input)) result.push(...input);
2636
- return result;
2423
+ const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
2424
+ const { body, payload, provider } = options;
2425
+ createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
2426
+ const anthropicResponse = translateToAnthropic(body);
2427
+ debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
2428
+ return c.json(anthropicResponse);
2637
2429
  };
2638
- const containsVisionContent = (value) => {
2639
- if (!value) return false;
2640
- if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
2641
- if (typeof value !== "object") return false;
2642
- const record = value;
2643
- if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
2644
- if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
2645
- return false;
2430
+ const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
2431
+ endpoint: "provider_messages",
2432
+ model: payload.model,
2433
+ providerName: provider,
2434
+ sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
2435
+ });
2436
+ const adjustInputTokens = (providerConfig, usage) => {
2437
+ if (!providerConfig.adjustInputTokens || !usage) return;
2438
+ usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
2439
+ debugJson(logger$4, "provider.messages.adjusted_usage:", usage);
2646
2440
  };
2647
2441
 
2648
2442
  //#endregion
2649
- //#region src/services/copilot/create-messages.ts
2650
- const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
2651
- const allowedAnthropicBetas = new Set([
2652
- INTERLEAVED_THINKING_BETA,
2653
- "context-management-2025-06-27",
2654
- "advanced-tool-use-2025-11-20"
2655
- ]);
2656
- const buildAnthropicBetaHeader = (anthropicBetaHeader, thinking, _model) => {
2657
- const isAdaptiveThinking = thinking?.type === "adaptive";
2658
- if (anthropicBetaHeader) {
2659
- const uniqueFilteredBetas = [...anthropicBetaHeader.split(",").map((item) => item.trim()).filter((item) => item.length > 0).filter((item) => allowedAnthropicBetas.has(item))];
2660
- if (uniqueFilteredBetas.length > 0) return uniqueFilteredBetas.join(",");
2661
- return;
2662
- }
2663
- if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
2664
- };
2665
- const createMessages = async (payload, anthropicBetaHeader, options) => {
2443
+ //#region src/services/copilot/create-responses.ts
2444
+ const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType }) => {
2666
2445
  if (!state.copilotToken) throw new Error("Copilot token not found");
2667
- const enableVision = payload.messages.some((message) => {
2668
- if (!Array.isArray(message.content)) return false;
2669
- return message.content.some((block) => block.type === "image" || block.type === "tool_result" && Array.isArray(block.content) && block.content.some((inner) => inner.type === "image"));
2670
- });
2671
- let isInitiateRequest = false;
2672
- const lastMessage = payload.messages.at(-1);
2673
- if (lastMessage?.role === "user") isInitiateRequest = Array.isArray(lastMessage.content) ? lastMessage.content.some((block) => block.type !== "tool_result") : true;
2674
2446
  const headers = {
2675
- ...copilotHeaders(state, options.requestId, enableVision),
2676
- "x-initiator": isInitiateRequest ? "user" : "agent"
2447
+ ...copilotHeaders(state, requestId, vision),
2448
+ "x-initiator": initiator
2677
2449
  };
2678
- prepareInteractionHeaders(options.sessionId, Boolean(options.subagentMarker), headers);
2679
- prepareForCompact(headers, options.compactType);
2680
- const { safetyIdentifier, sessionId } = parseUserIdMetadata(payload.metadata?.user_id);
2681
- if (safetyIdentifier && sessionId) prepareMessageProxyHeaders(headers);
2682
- const anthropicBeta = buildAnthropicBetaHeader(anthropicBetaHeader, payload.thinking, payload.model);
2683
- if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
2450
+ prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
2451
+ prepareForCompact(headers, compactType);
2452
+ payload.service_tier = void 0;
2684
2453
  consola.log(`<-- model: ${payload.model}`);
2685
- const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {
2454
+ const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
2686
2455
  method: "POST",
2687
2456
  headers,
2688
2457
  body: JSON.stringify(payload)
2689
2458
  });
2690
2459
  logCopilotRateLimits(response.headers);
2691
2460
  if (!response.ok) {
2692
- consola.error("Failed to create messages", response);
2693
- throw new HTTPError("Failed to create messages", response);
2461
+ consola.error("Failed to create responses", response);
2462
+ throw new HTTPError("Failed to create responses", response);
2694
2463
  }
2695
2464
  if (payload.stream) return events(response);
2696
2465
  return await response.json();
2697
2466
  };
2698
2467
 
2699
2468
  //#endregion
2700
- //#region src/routes/messages/preprocess.ts
2701
- const TOOL_REFERENCE_TURN_BOUNDARY = "Tool loaded.";
2702
- const IDE_EXECUTE_CODE_TOOL = "mcp__ide__executeCode";
2703
- const IDE_GET_DIAGNOSTICS_TOOL = "mcp__ide__getDiagnostics";
2704
- const IDE_GET_DIAGNOSTICS_DESCRIPTION = "Get language diagnostics from VS Code. Returns errors, warnings, information, and hints for files in the workspace.";
2705
- const PDF_FILE_READ_PREFIX = "PDF file read:";
2706
- const getCompactCandidateText = (message) => {
2707
- if (message.role !== "user") return "";
2708
- if (typeof message.content === "string") return message.content;
2709
- return message.content.filter((block) => block.type === "text").map((block) => block.text.startsWith("<system-reminder>") ? "" : block.text).filter((text) => text.length > 0).join("\n\n");
2469
+ //#region src/routes/messages/responses-translation.ts
2470
+ const MESSAGE_TYPE = "message";
2471
+ const COMPACTION_SIGNATURE_PREFIX = "cm1#";
2472
+ const COMPACTION_SIGNATURE_SEPARATOR = "@";
2473
+ const THINKING_TEXT = "Thinking...";
2474
+ const translateAnthropicMessagesToResponsesPayload = (payload) => {
2475
+ const input = [];
2476
+ const applyPhase = shouldApplyPhase(payload.model);
2477
+ for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase));
2478
+ const translatedTools = convertAnthropicTools(payload.tools);
2479
+ const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
2480
+ const { sessionId: promptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
2481
+ return {
2482
+ model: payload.model,
2483
+ input,
2484
+ instructions: translateSystemPrompt(payload.system, payload.model),
2485
+ temperature: 1,
2486
+ top_p: payload.top_p ?? null,
2487
+ max_output_tokens: Math.max(payload.max_tokens, 12800),
2488
+ tools: translatedTools,
2489
+ tool_choice: toolChoice,
2490
+ metadata: payload.metadata ? { ...payload.metadata } : null,
2491
+ prompt_cache_key: promptCacheKey,
2492
+ stream: payload.stream ?? null,
2493
+ store: false,
2494
+ parallel_tool_calls: true,
2495
+ reasoning: {
2496
+ effort: getReasoningEffortForModel(payload.model),
2497
+ summary: "detailed"
2498
+ },
2499
+ include: ["reasoning.encrypted_content"]
2500
+ };
2710
2501
  };
2711
- const isCompactMessage = (lastMessage) => {
2712
- const text = getCompactCandidateText(lastMessage);
2713
- if (!text) return false;
2714
- return text.includes(compactTextOnlyGuard) && text.includes(compactSummaryPromptStart) && compactMessageSections.some((section) => text.includes(section));
2502
+ const encodeCompactionCarrierSignature = (compaction) => {
2503
+ return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
2715
2504
  };
2716
- const isCompactAutoContinueMessage = (lastMessage) => {
2717
- const text = getCompactCandidateText(lastMessage);
2718
- return Boolean(text) && compactAutoContinuePromptStarts.some((promptStart) => text.startsWith(promptStart));
2505
+ const decodeCompactionCarrierSignature = (signature) => {
2506
+ if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
2507
+ const raw = signature.slice(4);
2508
+ const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
2509
+ if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
2510
+ const encrypted_content = raw.slice(0, separatorIndex);
2511
+ const id = raw.slice(separatorIndex + 1);
2512
+ if (!encrypted_content) return;
2513
+ return {
2514
+ id,
2515
+ encrypted_content
2516
+ };
2517
+ }
2719
2518
  };
2720
- const getCompactType = (anthropicPayload) => {
2721
- const lastMessage = anthropicPayload.messages.at(-1);
2722
- if (lastMessage && isCompactMessage(lastMessage)) return COMPACT_REQUEST;
2723
- if (lastMessage && isCompactAutoContinueMessage(lastMessage)) return COMPACT_AUTO_CONTINUE;
2724
- const system = anthropicPayload.system;
2725
- if (typeof system === "string") return compactSystemPromptStarts.some((promptStart) => system.startsWith(promptStart)) ? COMPACT_REQUEST : 0;
2726
- if (!Array.isArray(system)) return 0;
2727
- if (system.some((msg) => typeof msg.text === "string" && compactSystemPromptStarts.some((promptStart) => msg.text.startsWith(promptStart)))) return COMPACT_REQUEST;
2728
- return 0;
2519
+ const translateMessage = (message, model, applyPhase) => {
2520
+ if (message.role === "user") return translateUserMessage(message);
2521
+ return translateAssistantMessage(message, model, applyPhase);
2729
2522
  };
2730
- const mergeContentWithText = (tr, textBlock) => {
2731
- if (typeof tr.content === "string") return {
2732
- ...tr,
2733
- content: `${tr.content}\n\n${textBlock.text}`
2734
- };
2735
- if (hasToolRef(tr)) return tr;
2523
+ const translateUserMessage = (message) => {
2524
+ if (typeof message.content === "string") return [createMessage("user", message.content)];
2525
+ if (!Array.isArray(message.content)) return [];
2526
+ const items = [];
2527
+ const pendingContent = [];
2528
+ for (const block of message.content) {
2529
+ if (block.type === "tool_result") {
2530
+ flushPendingContent(pendingContent, items, { role: "user" });
2531
+ items.push(createFunctionCallOutput(block));
2532
+ continue;
2533
+ }
2534
+ const converted = translateUserContentBlock(block);
2535
+ if (converted.length > 0) pendingContent.push(...converted);
2536
+ }
2537
+ flushPendingContent(pendingContent, items, { role: "user" });
2538
+ return items;
2539
+ };
2540
+ const translateAssistantMessage = (message, model, applyPhase) => {
2541
+ const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
2542
+ if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
2543
+ if (!Array.isArray(message.content)) return [];
2544
+ const items = [];
2545
+ const pendingContent = [];
2546
+ for (const block of message.content) {
2547
+ if (block.type === "tool_use") {
2548
+ flushPendingContent(pendingContent, items, {
2549
+ role: "assistant",
2550
+ phase: assistantPhase
2551
+ });
2552
+ items.push(createFunctionToolCall(block));
2553
+ continue;
2554
+ }
2555
+ if (block.type === "thinking" && block.signature) {
2556
+ const compactionContent = createCompactionContent(block);
2557
+ if (compactionContent) {
2558
+ flushPendingContent(pendingContent, items, {
2559
+ role: "assistant",
2560
+ phase: assistantPhase
2561
+ });
2562
+ items.push(compactionContent);
2563
+ continue;
2564
+ }
2565
+ if (block.signature.includes("@")) {
2566
+ flushPendingContent(pendingContent, items, {
2567
+ role: "assistant",
2568
+ phase: assistantPhase
2569
+ });
2570
+ items.push(createReasoningContent(block));
2571
+ continue;
2572
+ }
2573
+ }
2574
+ const converted = translateAssistantContentBlock(block);
2575
+ if (converted) pendingContent.push(converted);
2576
+ }
2577
+ flushPendingContent(pendingContent, items, {
2578
+ role: "assistant",
2579
+ phase: assistantPhase
2580
+ });
2581
+ return items;
2582
+ };
2583
+ const translateUserContentBlock = (block) => {
2584
+ switch (block.type) {
2585
+ case "text": return [createTextContent(block.text)];
2586
+ case "image": return [createImageContent(block)];
2587
+ case "document": return [createFileContent(block)];
2588
+ default: return [];
2589
+ }
2590
+ };
2591
+ const translateAssistantContentBlock = (block) => {
2592
+ switch (block.type) {
2593
+ case "text": return createOutPutTextContent(block.text);
2594
+ default: return;
2595
+ }
2596
+ };
2597
+ const flushPendingContent = (pendingContent, target, message) => {
2598
+ if (pendingContent.length === 0) return;
2599
+ const messageContent = [...pendingContent];
2600
+ target.push(createMessage(message.role, messageContent, message.phase));
2601
+ pendingContent.length = 0;
2602
+ };
2603
+ const createMessage = (role, content, phase) => ({
2604
+ type: MESSAGE_TYPE,
2605
+ role,
2606
+ content,
2607
+ ...role === "assistant" && phase ? { phase } : {}
2608
+ });
2609
+ const resolveAssistantPhase = (_model, content, applyPhase) => {
2610
+ if (!applyPhase) return;
2611
+ if (typeof content === "string") return "final_answer";
2612
+ if (!Array.isArray(content)) return;
2613
+ if (!content.some((block) => block.type === "text")) return;
2614
+ return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
2615
+ };
2616
+ const shouldApplyPhase = (model) => {
2617
+ return getExtraPromptForModel(model).includes("## Intermediary updates");
2618
+ };
2619
+ const createTextContent = (text) => ({
2620
+ type: "input_text",
2621
+ text
2622
+ });
2623
+ const createOutPutTextContent = (text) => ({
2624
+ type: "output_text",
2625
+ text
2626
+ });
2627
+ const createImageContent = (block) => ({
2628
+ type: "input_image",
2629
+ image_url: `data:${block.source.media_type};base64,${block.source.data}`,
2630
+ detail: "auto"
2631
+ });
2632
+ const createFileContent = (block) => ({
2633
+ type: "input_file",
2634
+ file_data: `data:${block.source.media_type};base64,${block.source.data}`,
2635
+ filename: block.title ?? "document.pdf"
2636
+ });
2637
+ const createReasoningContent = (block) => {
2638
+ const { encryptedContent, id } = parseReasoningSignature(block.signature);
2639
+ const thinking = block.thinking === THINKING_TEXT ? "" : block.thinking;
2736
2640
  return {
2737
- ...tr,
2738
- content: [...tr.content, textBlock]
2641
+ id,
2642
+ type: "reasoning",
2643
+ summary: thinking ? [{
2644
+ type: "summary_text",
2645
+ text: thinking
2646
+ }] : [],
2647
+ encrypted_content: encryptedContent
2739
2648
  };
2740
2649
  };
2741
- const mergeContentWithTexts = (tr, textBlocks) => {
2742
- if (typeof tr.content === "string") {
2743
- const appendedTexts = textBlocks.map((tb) => tb.text).join("\n\n");
2744
- return {
2745
- ...tr,
2746
- content: `${tr.content}\n\n${appendedTexts}`
2747
- };
2748
- }
2749
- if (hasToolRef(tr)) return tr;
2650
+ const createCompactionContent = (block) => {
2651
+ const compaction = decodeCompactionCarrierSignature(block.signature);
2652
+ if (!compaction) return;
2750
2653
  return {
2751
- ...tr,
2752
- content: [...tr.content, ...textBlocks]
2654
+ id: compaction.id,
2655
+ type: "compaction",
2656
+ encrypted_content: compaction.encrypted_content
2753
2657
  };
2754
2658
  };
2755
- const mergeContentWithAttachments = (tr, attachments) => {
2756
- if (typeof tr.content === "string") return {
2757
- ...tr,
2758
- content: [{
2759
- type: "text",
2760
- text: tr.content
2761
- }, ...attachments]
2659
+ const parseReasoningSignature = (signature) => {
2660
+ const splitIndex = signature.lastIndexOf("@");
2661
+ if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
2662
+ encryptedContent: signature,
2663
+ id: ""
2762
2664
  };
2763
2665
  return {
2764
- ...tr,
2765
- content: [...tr.content, ...attachments]
2666
+ encryptedContent: signature.slice(0, splitIndex),
2667
+ id: signature.slice(splitIndex + 1)
2766
2668
  };
2767
2669
  };
2768
- const isAttachmentBlock = (block) => {
2769
- return block.type === "image" || block.type === "document";
2770
- };
2771
- const getMergeableToolResultIndices = (toolResults) => {
2772
- return toolResults.flatMap((block, index) => block.is_error || hasToolRef(block) ? [] : [index]);
2670
+ const createFunctionToolCall = (block) => ({
2671
+ type: "function_call",
2672
+ call_id: block.id,
2673
+ name: block.name,
2674
+ arguments: JSON.stringify(block.input),
2675
+ status: "completed"
2676
+ });
2677
+ const createFunctionCallOutput = (block) => ({
2678
+ type: "function_call_output",
2679
+ call_id: block.tool_use_id,
2680
+ output: convertToolResultContent(block.content),
2681
+ status: block.is_error ? "incomplete" : "completed"
2682
+ });
2683
+ const translateSystemPrompt = (system, model) => {
2684
+ if (!system) return null;
2685
+ const extraPrompt = getExtraPromptForModel(model);
2686
+ if (typeof system === "string") return system + extraPrompt;
2687
+ const text = system.map((block, index) => {
2688
+ if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
2689
+ return block.text;
2690
+ }).join(" ");
2691
+ return text.length > 0 ? text : null;
2773
2692
  };
2774
- const mergeAttachmentsIntoToolResults = (toolResults, attachmentsByToolResultIndex) => {
2775
- if (attachmentsByToolResultIndex.size === 0) return toolResults;
2776
- return toolResults.map((block, index) => {
2777
- const matchedAttachments = attachmentsByToolResultIndex.get(index);
2778
- if (!matchedAttachments) return block;
2779
- const orderedAttachments = [...matchedAttachments].sort((left, right) => left.order - right.order).map(({ attachment }) => attachment);
2780
- return mergeContentWithAttachments(block, orderedAttachments);
2781
- });
2693
+ const convertAnthropicTools = (tools) => {
2694
+ if (!tools || tools.length === 0) return null;
2695
+ return tools.map((tool) => ({
2696
+ type: "function",
2697
+ name: tool.name,
2698
+ parameters: normalizeToolSchema(tool.input_schema),
2699
+ strict: false,
2700
+ ...tool.description ? { description: tool.description } : {}
2701
+ }));
2782
2702
  };
2783
- const assignAttachmentsToToolResults = (target, attachments, options) => {
2784
- const { toolResultIndices } = options;
2785
- const fallbackToolResultIndices = options.fallbackToolResultIndices ?? toolResultIndices;
2786
- if (attachments.length === 0) return;
2787
- if (toolResultIndices.length > 0 && toolResultIndices.length === attachments.length) {
2788
- for (const [index, toolResultIndex] of toolResultIndices.entries()) {
2789
- const currentAttachments$1 = target.get(toolResultIndex);
2790
- if (currentAttachments$1) {
2791
- currentAttachments$1.push(attachments[index]);
2792
- continue;
2793
- }
2794
- target.set(toolResultIndex, [attachments[index]]);
2795
- }
2796
- return;
2797
- }
2798
- const lastToolResultIndex = fallbackToolResultIndices.at(-1);
2799
- if (lastToolResultIndex === void 0) return;
2800
- const currentAttachments = target.get(lastToolResultIndex);
2801
- if (currentAttachments) {
2802
- currentAttachments.push(...attachments);
2803
- return;
2703
+ const convertAnthropicToolChoice = (choice) => {
2704
+ if (!choice) return "auto";
2705
+ switch (choice.type) {
2706
+ case "auto": return "auto";
2707
+ case "any": return "required";
2708
+ case "tool": return choice.name ? {
2709
+ type: "function",
2710
+ name: choice.name
2711
+ } : "auto";
2712
+ case "none": return "none";
2713
+ default: return "auto";
2804
2714
  }
2805
- target.set(lastToolResultIndex, [...attachments]);
2806
2715
  };
2807
- const startsWithPdfFileRead = (toolResult) => {
2808
- if (typeof toolResult.content === "string") return toolResult.content.startsWith(PDF_FILE_READ_PREFIX);
2809
- if (toolResult.content.some((block) => block.type === "document")) return false;
2810
- if (toolResult.content.length === 0) return false;
2811
- const firstBlock = toolResult.content[0];
2812
- if (firstBlock.type !== "text") return false;
2813
- return firstBlock.text.startsWith(PDF_FILE_READ_PREFIX);
2716
+ const translateResponsesResultToAnthropic = (response) => {
2717
+ const contentBlocks = mapOutputToAnthropicContent(response.output);
2718
+ const usage = mapResponsesUsage(response);
2719
+ let anthropicContent = fallbackContentBlocks(response.output_text);
2720
+ if (contentBlocks.length > 0) anthropicContent = contentBlocks;
2721
+ const stopReason = mapResponsesStopReason(response);
2722
+ return {
2723
+ id: response.id,
2724
+ type: "message",
2725
+ role: "assistant",
2726
+ content: anthropicContent,
2727
+ model: response.model,
2728
+ stop_reason: stopReason,
2729
+ stop_sequence: null,
2730
+ usage
2731
+ };
2814
2732
  };
2815
- const collectMergeableUserContent = (content) => {
2816
- const toolResults = [];
2817
- const textBlocks = [];
2818
- const attachments = [];
2819
- for (const [order, block] of content.entries()) {
2820
- if (block.type === "tool_result") {
2821
- toolResults.push(block);
2822
- continue;
2733
+ const mapOutputToAnthropicContent = (output) => {
2734
+ const contentBlocks = [];
2735
+ for (const item of output) switch (item.type) {
2736
+ case "reasoning": {
2737
+ const thinkingText = extractReasoningText(item);
2738
+ if (thinkingText.length > 0) contentBlocks.push({
2739
+ type: "thinking",
2740
+ thinking: thinkingText,
2741
+ signature: (item.encrypted_content ?? "") + "@" + item.id
2742
+ });
2743
+ break;
2823
2744
  }
2824
- if (block.type === "text") {
2825
- textBlocks.push(block);
2826
- continue;
2745
+ case "function_call": {
2746
+ const toolUseBlock = createToolUseContentBlock(item);
2747
+ if (toolUseBlock) contentBlocks.push(toolUseBlock);
2748
+ break;
2827
2749
  }
2828
- if (isAttachmentBlock(block)) {
2829
- attachments.push({
2830
- attachment: block,
2831
- order
2750
+ case "message": {
2751
+ const combinedText = combineMessageTextContent(item.content);
2752
+ if (combinedText.length > 0) contentBlocks.push({
2753
+ type: "text",
2754
+ text: combinedText
2755
+ });
2756
+ break;
2757
+ }
2758
+ case "compaction": {
2759
+ const compactionBlock = createCompactionThinkingBlock(item);
2760
+ if (compactionBlock) contentBlocks.push(compactionBlock);
2761
+ break;
2762
+ }
2763
+ default: {
2764
+ const combinedText = combineMessageTextContent(item.content);
2765
+ if (combinedText.length > 0) contentBlocks.push({
2766
+ type: "text",
2767
+ text: combinedText
2832
2768
  });
2833
- continue;
2834
2769
  }
2835
- return null;
2836
- }
2837
- return {
2838
- toolResults,
2839
- textBlocks,
2840
- attachments
2841
- };
2842
- };
2843
- const mergeAttachmentsForToolResults = (toolResults, attachments) => {
2844
- if (attachments.length === 0) return toolResults;
2845
- const documentBlocks = attachments.filter(({ attachment }) => attachment.type === "document");
2846
- const mergeableToolResultIndices = getMergeableToolResultIndices(toolResults);
2847
- const pdfReadToolResultIndices = mergeableToolResultIndices.filter((index) => startsWithPdfFileRead(toolResults[index]));
2848
- const attachmentsByToolResultIndex = /* @__PURE__ */ new Map();
2849
- let remainingAttachments = attachments;
2850
- let countMatchToolResultIndices = mergeableToolResultIndices;
2851
- if (documentBlocks.length > 0 && pdfReadToolResultIndices.length > 0) {
2852
- const matchedDocumentCount = Math.min(pdfReadToolResultIndices.length, documentBlocks.length);
2853
- const matchedDocuments = documentBlocks.slice(0, matchedDocumentCount);
2854
- const matchedDocumentOrders = new Set(matchedDocuments.map(({ order }) => order));
2855
- const matchedPdfToolResultIndices = pdfReadToolResultIndices.slice(0, matchedDocumentCount);
2856
- const matchedPdfToolResultIndexSet = new Set(matchedPdfToolResultIndices);
2857
- assignAttachmentsToToolResults(attachmentsByToolResultIndex, matchedDocuments, { toolResultIndices: matchedPdfToolResultIndices });
2858
- countMatchToolResultIndices = mergeableToolResultIndices.filter((index) => !matchedPdfToolResultIndexSet.has(index));
2859
- remainingAttachments = attachments.filter(({ attachment, order }) => attachment.type !== "document" || !matchedDocumentOrders.has(order));
2860
- }
2861
- assignAttachmentsToToolResults(attachmentsByToolResultIndex, remainingAttachments, {
2862
- toolResultIndices: countMatchToolResultIndices,
2863
- fallbackToolResultIndices: mergeableToolResultIndices
2864
- });
2865
- return mergeAttachmentsIntoToolResults(toolResults, attachmentsByToolResultIndex);
2866
- };
2867
- const mergeUserMessageContent = (content) => {
2868
- const mergeableContent = collectMergeableUserContent(content);
2869
- if (!mergeableContent) return null;
2870
- const { toolResults, textBlocks, attachments } = mergeableContent;
2871
- if (toolResults.length === 0 || textBlocks.length === 0 && attachments.length === 0) return null;
2872
- const mergedToolResults = textBlocks.length === 0 ? toolResults : mergeToolResult(toolResults, textBlocks);
2873
- return mergeAttachmentsForToolResults(mergedToolResults, attachments);
2874
- };
2875
- const mergeToolResult = (toolResults, textBlocks) => {
2876
- if (toolResults.length === textBlocks.length) return toolResults.map((tr, i) => mergeContentWithText(tr, textBlocks[i]));
2877
- const lastIndex = toolResults.length - 1;
2878
- return toolResults.map((tr, i) => i === lastIndex ? mergeContentWithTexts(tr, textBlocks) : tr);
2879
- };
2880
- const stripToolReferenceTurnBoundary = (anthropicPayload) => {
2881
- for (const msg of anthropicPayload.messages) {
2882
- if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
2883
- if (!msg.content.some((block) => block.type === "tool_result" && hasToolRef(block))) continue;
2884
- msg.content = msg.content.filter((block) => block.type !== "text" || block.text.trim() !== TOOL_REFERENCE_TURN_BOUNDARY);
2885
2770
  }
2771
+ return contentBlocks;
2886
2772
  };
2887
- const mergeToolResultForClaude = (anthropicPayload, options) => {
2888
- const lastMessageIndex = anthropicPayload.messages.length - 1;
2889
- for (const [index, msg] of anthropicPayload.messages.entries()) {
2890
- if (options?.skipLastMessage && index === lastMessageIndex) continue;
2891
- if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
2892
- const mergedContent = mergeUserMessageContent(msg.content);
2893
- if (mergedContent) msg.content = mergedContent;
2773
+ const combineMessageTextContent = (content) => {
2774
+ if (!Array.isArray(content)) return "";
2775
+ let aggregated = "";
2776
+ for (const block of content) {
2777
+ if (isResponseOutputText(block)) {
2778
+ aggregated += block.text;
2779
+ continue;
2780
+ }
2781
+ if (isResponseOutputRefusal(block)) {
2782
+ aggregated += block.refusal;
2783
+ continue;
2784
+ }
2785
+ if (typeof block.text === "string") {
2786
+ aggregated += block.text;
2787
+ continue;
2788
+ }
2789
+ if (typeof block.reasoning === "string") {
2790
+ aggregated += block.reasoning;
2791
+ continue;
2792
+ }
2894
2793
  }
2794
+ return aggregated;
2895
2795
  };
2896
- const sanitizeIdeTools = (payload) => {
2897
- if (!payload.tools || payload.tools.length === 0) return;
2898
- payload.tools = payload.tools.flatMap((tool) => {
2899
- if (tool.name === IDE_EXECUTE_CODE_TOOL && !tool.defer_loading) return [];
2900
- if (tool.name === IDE_GET_DIAGNOSTICS_TOOL) return [{
2901
- ...tool,
2902
- description: IDE_GET_DIAGNOSTICS_DESCRIPTION
2903
- }];
2904
- return [tool];
2905
- });
2906
- };
2907
- const hasToolRef = (block) => {
2908
- return Array.isArray(block.content) && block.content.some((c) => c.type === "tool_reference");
2909
- };
2910
- const stripCacheControl = (payload) => {
2911
- if (Array.isArray(payload.system)) for (const block of payload.system) {
2912
- const systemBlock = block;
2913
- const cacheControl = systemBlock.cache_control;
2914
- if (cacheControl && typeof cacheControl === "object") {
2915
- const { scope,...rest } = cacheControl;
2916
- systemBlock.cache_control = rest;
2796
+ const extractReasoningText = (item) => {
2797
+ const segments = [];
2798
+ const collectFromBlocks = (blocks) => {
2799
+ if (!Array.isArray(blocks)) return;
2800
+ for (const block of blocks) if (typeof block.text === "string") {
2801
+ segments.push(block.text);
2802
+ continue;
2917
2803
  }
2918
- }
2804
+ };
2805
+ if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
2806
+ collectFromBlocks(item.summary);
2807
+ return segments.join("").trim();
2919
2808
  };
2920
- const filterAssistantThinkingBlocks = (payload) => {
2921
- for (const msg of payload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
2922
- if (block.type !== "thinking") return true;
2923
- return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
2924
- });
2809
+ const createToolUseContentBlock = (call) => {
2810
+ const toolId = call.call_id;
2811
+ if (!call.name || !toolId) return null;
2812
+ const input = parseFunctionCallArguments(call.arguments);
2813
+ return {
2814
+ type: "tool_use",
2815
+ id: toolId,
2816
+ name: call.name,
2817
+ input
2818
+ };
2925
2819
  };
2926
- const prepareMessagesApiPayload = (payload, selectedModel) => {
2927
- stripCacheControl(payload);
2928
- filterAssistantThinkingBlocks(payload);
2929
- const hasThinking = Boolean(payload.thinking);
2930
- const toolChoice = payload.tool_choice;
2931
- const disableThink = toolChoice?.type === "any" || toolChoice?.type === "tool";
2932
- if (selectedModel?.capabilities.supports.adaptive_thinking && !disableThink) {
2933
- payload.thinking = { type: "adaptive" };
2934
- if (!hasThinking) payload.thinking.display = "summarized";
2935
- if (payload.model === "claude-opus-4.7") payload.thinking.display = "summarized";
2936
- let effort = getReasoningEffortForModel(payload.model);
2937
- if (effort === "none" || effort === "minimal") effort = "low";
2938
- const reasoningEffort = selectedModel.capabilities.supports.reasoning_effort;
2939
- if (reasoningEffort && !reasoningEffort.includes(effort)) effort = reasoningEffort.at(-1);
2940
- payload.output_config = { effort };
2820
+ const createCompactionThinkingBlock = (item) => {
2821
+ if (!item.id || !item.encrypted_content) return null;
2822
+ return {
2823
+ type: "thinking",
2824
+ thinking: THINKING_TEXT,
2825
+ signature: encodeCompactionCarrierSignature({
2826
+ id: item.id,
2827
+ encrypted_content: item.encrypted_content
2828
+ })
2829
+ };
2830
+ };
2831
+ const parseFunctionCallArguments = (rawArguments) => {
2832
+ if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
2833
+ try {
2834
+ const parsed = JSON.parse(rawArguments);
2835
+ if (Array.isArray(parsed)) return { arguments: parsed };
2836
+ if (parsed && typeof parsed === "object") return parsed;
2837
+ } catch (error) {
2838
+ consola.warn("Failed to parse function call arguments", {
2839
+ error,
2840
+ rawArguments
2841
+ });
2941
2842
  }
2843
+ return { raw_arguments: rawArguments };
2942
2844
  };
2943
-
2944
- //#endregion
2945
- //#region src/routes/messages/stream-translation.ts
2946
- function isToolBlockOpen(state$1) {
2947
- if (!state$1.contentBlockOpen) return false;
2948
- return Object.values(state$1.toolCalls).some((tc) => tc.anthropicBlockIndex === state$1.contentBlockIndex);
2949
- }
2950
- function translateChunkToAnthropicEvents(chunk, state$1) {
2951
- const events$1 = [];
2952
- if (chunk.choices.length === 0) {
2953
- completePendingMessage(state$1, events$1, chunk);
2954
- return events$1;
2845
+ const fallbackContentBlocks = (outputText) => {
2846
+ if (!outputText) return [];
2847
+ return [{
2848
+ type: "text",
2849
+ text: outputText
2850
+ }];
2851
+ };
2852
+ const mapResponsesStopReason = (response) => {
2853
+ const { status, incomplete_details: incompleteDetails } = response;
2854
+ if (status === "completed") {
2855
+ if (response.output.some((item) => item.type === "function_call")) return "tool_use";
2856
+ return "end_turn";
2955
2857
  }
2956
- const choice = chunk.choices[0];
2957
- const { delta } = choice;
2958
- handleMessageStart(state$1, events$1, chunk);
2959
- handleThinkingText(delta, state$1, events$1);
2960
- handleContent(delta, state$1, events$1);
2961
- handleToolCalls(delta, state$1, events$1);
2962
- handleFinish(choice, state$1, {
2963
- events: events$1,
2964
- chunk
2965
- });
2966
- return events$1;
2967
- }
2968
- function flushPendingAnthropicStreamEvents(state$1) {
2969
- const events$1 = [];
2970
- completePendingMessage(state$1, events$1);
2971
- return events$1;
2972
- }
2973
- function completePendingMessage(state$1, events$1, chunk) {
2974
- if (!state$1.pendingMessageDelta) return;
2975
- if (chunk?.usage) state$1.pendingMessageDelta.usage = getAnthropicUsageFromOpenAIChunk(chunk);
2976
- events$1.push(state$1.pendingMessageDelta, { type: "message_stop" });
2977
- state$1.pendingMessageDelta = void 0;
2978
- }
2979
- function handleFinish(choice, state$1, context) {
2980
- const { events: events$1, chunk } = context;
2981
- if (choice.finish_reason && choice.finish_reason.length > 0) {
2982
- if (state$1.contentBlockOpen) {
2983
- const toolBlockOpen = isToolBlockOpen(state$1);
2984
- context.events.push({
2985
- type: "content_block_stop",
2986
- index: state$1.contentBlockIndex
2987
- });
2988
- state$1.contentBlockOpen = false;
2989
- state$1.contentBlockIndex++;
2990
- if (!toolBlockOpen) handleReasoningOpaque(choice.delta, events$1, state$1);
2991
- }
2992
- state$1.pendingMessageDelta = {
2993
- type: "message_delta",
2994
- delta: {
2995
- stop_reason: mapOpenAIStopReasonToAnthropic(choice.finish_reason),
2996
- stop_sequence: null
2997
- },
2998
- usage: getAnthropicUsageFromOpenAIChunk(chunk)
2999
- };
3000
- if (chunk.usage) completePendingMessage(state$1, events$1, chunk);
2858
+ if (status === "incomplete") {
2859
+ if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
2860
+ if (incompleteDetails?.reason === "content_filter") return "end_turn";
3001
2861
  }
3002
- }
3003
- function getAnthropicUsageFromOpenAIChunk(chunk) {
3004
- const { cachedTokens, cacheCreationTokens, inputTokens } = getOpenAIChunkUsageTokens(chunk);
3005
- return {
3006
- input_tokens: inputTokens,
3007
- output_tokens: chunk.usage?.completion_tokens ?? 0,
3008
- ...chunk.usage?.prompt_tokens_details?.cache_creation_input_tokens !== void 0 && { cache_creation_input_tokens: cacheCreationTokens },
3009
- ...chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: cachedTokens }
3010
- };
3011
- }
3012
- function getOpenAIChunkUsageTokens(chunk) {
3013
- const promptTokens = chunk.usage?.prompt_tokens ?? 0;
3014
- const cachedTokens = chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0;
3015
- const cacheCreationTokens = chunk.usage?.prompt_tokens_details?.cache_creation_input_tokens ?? 0;
2862
+ return null;
2863
+ };
2864
+ const mapResponsesUsage = (response) => {
2865
+ const inputTokens = response.usage?.input_tokens ?? 0;
2866
+ const outputTokens = response.usage?.output_tokens ?? 0;
2867
+ const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
3016
2868
  return {
3017
- cacheCreationTokens,
3018
- cachedTokens,
3019
- inputTokens: Math.max(0, promptTokens - cachedTokens - cacheCreationTokens)
2869
+ input_tokens: inputTokens - (inputCachedTokens ?? 0),
2870
+ output_tokens: outputTokens,
2871
+ ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
3020
2872
  };
3021
- }
3022
- function handleToolCalls(delta, state$1, events$1) {
3023
- if (delta.tool_calls && delta.tool_calls.length > 0) {
3024
- closeThinkingBlockIfOpen(state$1, events$1);
3025
- handleReasoningOpaqueInToolCalls(state$1, events$1, delta);
3026
- for (const toolCall of delta.tool_calls) {
3027
- if (toolCall.id && toolCall.function?.name) {
3028
- if (state$1.contentBlockOpen) {
3029
- events$1.push({
3030
- type: "content_block_stop",
3031
- index: state$1.contentBlockIndex
3032
- });
3033
- state$1.contentBlockIndex++;
3034
- state$1.contentBlockOpen = false;
3035
- }
3036
- const anthropicBlockIndex = state$1.contentBlockIndex;
3037
- state$1.toolCalls[toolCall.index] = {
3038
- id: toolCall.id,
3039
- name: toolCall.function.name,
3040
- anthropicBlockIndex
3041
- };
3042
- events$1.push({
3043
- type: "content_block_start",
3044
- index: anthropicBlockIndex,
3045
- content_block: {
3046
- type: "tool_use",
3047
- id: toolCall.id,
3048
- name: toolCall.function.name,
3049
- input: {}
3050
- }
3051
- });
3052
- state$1.contentBlockOpen = true;
3053
- }
3054
- if (toolCall.function?.arguments) {
3055
- const toolCallInfo = state$1.toolCalls[toolCall.index];
3056
- if (toolCallInfo) events$1.push({
3057
- type: "content_block_delta",
3058
- index: toolCallInfo.anthropicBlockIndex,
3059
- delta: {
3060
- type: "input_json_delta",
3061
- partial_json: toolCall.function.arguments
3062
- }
3063
- });
3064
- }
2873
+ };
2874
+ const isRecord = (value) => typeof value === "object" && value !== null;
2875
+ const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
2876
+ const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
2877
+ const convertToolResultContent = (content) => {
2878
+ if (typeof content === "string") return content;
2879
+ if (Array.isArray(content)) {
2880
+ const result = [];
2881
+ for (const block of content) switch (block.type) {
2882
+ case "text":
2883
+ result.push(createTextContent(block.text));
2884
+ break;
2885
+ case "image":
2886
+ result.push(createImageContent(block));
2887
+ break;
2888
+ case "document":
2889
+ result.push(createFileContent(block));
2890
+ break;
2891
+ case "tool_reference":
2892
+ result.push(createTextContent(`Tool ${block.tool_name} loaded`));
2893
+ break;
2894
+ default: break;
3065
2895
  }
2896
+ return result;
3066
2897
  }
3067
- }
3068
- function handleReasoningOpaqueInToolCalls(state$1, events$1, delta) {
3069
- if (state$1.contentBlockOpen && !isToolBlockOpen(state$1)) {
3070
- events$1.push({
3071
- type: "content_block_stop",
3072
- index: state$1.contentBlockIndex
3073
- });
3074
- state$1.contentBlockIndex++;
3075
- state$1.contentBlockOpen = false;
2898
+ return "";
2899
+ };
2900
+
2901
+ //#endregion
2902
+ //#region src/routes/messages/responses-stream-translation.ts
2903
+ const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
2904
+ var FunctionCallArgumentsValidationError = class extends Error {
2905
+ constructor(message) {
2906
+ super(message);
2907
+ this.name = "FunctionCallArgumentsValidationError";
3076
2908
  }
3077
- handleReasoningOpaque(delta, events$1, state$1);
3078
- }
3079
- function handleContent(delta, state$1, events$1) {
3080
- if (delta.content && delta.content.length > 0) {
3081
- closeThinkingBlockIfOpen(state$1, events$1);
3082
- if (isToolBlockOpen(state$1)) {
3083
- events$1.push({
3084
- type: "content_block_stop",
3085
- index: state$1.contentBlockIndex
3086
- });
3087
- state$1.contentBlockIndex++;
3088
- state$1.contentBlockOpen = false;
3089
- }
3090
- if (!state$1.contentBlockOpen) {
3091
- events$1.push({
3092
- type: "content_block_start",
3093
- index: state$1.contentBlockIndex,
3094
- content_block: {
3095
- type: "text",
3096
- text: ""
3097
- }
3098
- });
3099
- state$1.contentBlockOpen = true;
2909
+ };
2910
+ const updateWhitespaceRunState = (previousCount, chunk) => {
2911
+ let count = previousCount;
2912
+ for (const char of chunk) {
2913
+ if (char === "\r" || char === "\n" || char === " ") {
2914
+ count += 1;
2915
+ if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
2916
+ nextCount: count,
2917
+ exceeded: true
2918
+ };
2919
+ continue;
3100
2920
  }
2921
+ if (char !== " ") count = 0;
2922
+ }
2923
+ return {
2924
+ nextCount: count,
2925
+ exceeded: false
2926
+ };
2927
+ };
2928
+ const createResponsesStreamState = () => ({
2929
+ messageStartSent: false,
2930
+ messageCompleted: false,
2931
+ nextContentBlockIndex: 0,
2932
+ blockIndexByKey: /* @__PURE__ */ new Map(),
2933
+ openBlocks: /* @__PURE__ */ new Set(),
2934
+ blockHasDelta: /* @__PURE__ */ new Set(),
2935
+ functionCallStateByOutputIndex: /* @__PURE__ */ new Map()
2936
+ });
2937
+ const translateResponsesStreamEvent = (rawEvent, state$1) => {
2938
+ switch (rawEvent.type) {
2939
+ case "response.created": return handleResponseCreated(rawEvent, state$1);
2940
+ case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state$1);
2941
+ case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state$1);
2942
+ case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state$1);
2943
+ case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state$1);
2944
+ case "response.output_text.done": return handleOutputTextDone(rawEvent, state$1);
2945
+ case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state$1);
2946
+ case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state$1);
2947
+ case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state$1);
2948
+ case "response.completed":
2949
+ case "response.incomplete": return handleResponseCompleted(rawEvent, state$1);
2950
+ case "response.failed": return handleResponseFailed(rawEvent, state$1);
2951
+ case "error": return handleErrorEvent(rawEvent, state$1);
2952
+ default: return [];
2953
+ }
2954
+ };
2955
+ const handleResponseCreated = (rawEvent, state$1) => {
2956
+ return messageStart(state$1, rawEvent.response);
2957
+ };
2958
+ const handleOutputItemAdded$1 = (rawEvent, state$1) => {
2959
+ const events$1 = new Array();
2960
+ const functionCallDetails = extractFunctionCallDetails(rawEvent);
2961
+ if (!functionCallDetails) return events$1;
2962
+ const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
2963
+ const blockIndex = openFunctionCallBlock(state$1, {
2964
+ outputIndex,
2965
+ toolCallId,
2966
+ name,
2967
+ events: events$1
2968
+ });
2969
+ if (initialArguments !== void 0 && initialArguments.length > 0) {
3101
2970
  events$1.push({
3102
2971
  type: "content_block_delta",
3103
- index: state$1.contentBlockIndex,
2972
+ index: blockIndex,
3104
2973
  delta: {
3105
- type: "text_delta",
3106
- text: delta.content
2974
+ type: "input_json_delta",
2975
+ partial_json: initialArguments
3107
2976
  }
3108
2977
  });
2978
+ state$1.blockHasDelta.add(blockIndex);
3109
2979
  }
3110
- if (delta.content === "" && delta.reasoning_opaque && delta.reasoning_opaque.length > 0 && state$1.thinkingBlockOpen) {
3111
- events$1.push({
2980
+ return events$1;
2981
+ };
2982
+ const handleOutputItemDone$1 = (rawEvent, state$1) => {
2983
+ const events$1 = new Array();
2984
+ const item = rawEvent.item;
2985
+ const itemType = item.type;
2986
+ const outputIndex = rawEvent.output_index;
2987
+ if (itemType === "compaction") {
2988
+ if (!item.id || !item.encrypted_content) return events$1;
2989
+ const blockIndex$1 = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2990
+ if (!state$1.blockHasDelta.has(blockIndex$1)) events$1.push({
3112
2991
  type: "content_block_delta",
3113
- index: state$1.contentBlockIndex,
2992
+ index: blockIndex$1,
3114
2993
  delta: {
3115
- type: "signature_delta",
3116
- signature: delta.reasoning_opaque
2994
+ type: "thinking_delta",
2995
+ thinking: THINKING_TEXT
3117
2996
  }
3118
- }, {
3119
- type: "content_block_stop",
3120
- index: state$1.contentBlockIndex
3121
2997
  });
3122
- state$1.contentBlockIndex++;
3123
- state$1.thinkingBlockOpen = false;
3124
- }
3125
- }
3126
- function handleMessageStart(state$1, events$1, chunk) {
3127
- if (!state$1.messageStartSent) {
3128
- const { cachedTokens, cacheCreationTokens, inputTokens } = getOpenAIChunkUsageTokens(chunk);
3129
2998
  events$1.push({
3130
- type: "message_start",
3131
- message: {
3132
- id: chunk.id,
3133
- type: "message",
3134
- role: "assistant",
3135
- content: [],
3136
- model: chunk.model,
3137
- stop_reason: null,
3138
- stop_sequence: null,
3139
- usage: {
3140
- input_tokens: inputTokens,
3141
- output_tokens: 0,
3142
- ...chunk.usage?.prompt_tokens_details?.cache_creation_input_tokens !== void 0 && { cache_creation_input_tokens: cacheCreationTokens },
3143
- ...chunk.usage?.prompt_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: cachedTokens }
3144
- }
2999
+ type: "content_block_delta",
3000
+ index: blockIndex$1,
3001
+ delta: {
3002
+ type: "signature_delta",
3003
+ signature: encodeCompactionCarrierSignature({
3004
+ id: item.id,
3005
+ encrypted_content: item.encrypted_content
3006
+ })
3145
3007
  }
3146
3008
  });
3147
- state$1.messageStartSent = true;
3009
+ state$1.blockHasDelta.add(blockIndex$1);
3010
+ return events$1;
3148
3011
  }
3149
- }
3150
- function handleReasoningOpaque(delta, events$1, state$1) {
3151
- if (delta.reasoning_opaque && delta.reasoning_opaque.length > 0) {
3152
- events$1.push({
3153
- type: "content_block_start",
3154
- index: state$1.contentBlockIndex,
3155
- content_block: {
3156
- type: "thinking",
3157
- thinking: ""
3158
- }
3159
- }, {
3012
+ if (itemType !== "reasoning") return events$1;
3013
+ const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
3014
+ const signature = (item.encrypted_content ?? "") + "@" + item.id;
3015
+ if (signature) {
3016
+ if (!item.summary || item.summary.length === 0) events$1.push({
3160
3017
  type: "content_block_delta",
3161
- index: state$1.contentBlockIndex,
3018
+ index: blockIndex,
3162
3019
  delta: {
3163
3020
  type: "thinking_delta",
3164
3021
  thinking: THINKING_TEXT
3165
3022
  }
3166
- }, {
3167
- type: "content_block_delta",
3168
- index: state$1.contentBlockIndex,
3169
- delta: {
3170
- type: "signature_delta",
3171
- signature: delta.reasoning_opaque
3172
- }
3173
- }, {
3174
- type: "content_block_stop",
3175
- index: state$1.contentBlockIndex
3176
3023
  });
3177
- state$1.contentBlockIndex++;
3178
- }
3179
- }
3180
- function handleThinkingText(delta, state$1, events$1) {
3181
- const reasoningText = delta.reasoning_text ?? delta.reasoning_content;
3182
- if (reasoningText && reasoningText.length > 0) {
3183
- if (state$1.contentBlockOpen) {
3184
- delta.content = reasoningText;
3185
- delta.reasoning_text = void 0;
3186
- delta.reasoning_content = void 0;
3187
- return;
3188
- }
3189
- if (!state$1.thinkingBlockOpen) {
3190
- events$1.push({
3191
- type: "content_block_start",
3192
- index: state$1.contentBlockIndex,
3193
- content_block: {
3194
- type: "thinking",
3195
- thinking: ""
3196
- }
3197
- });
3198
- state$1.thinkingBlockOpen = true;
3199
- }
3200
3024
  events$1.push({
3201
3025
  type: "content_block_delta",
3202
- index: state$1.contentBlockIndex,
3026
+ index: blockIndex,
3203
3027
  delta: {
3204
- type: "thinking_delta",
3205
- thinking: reasoningText
3028
+ type: "signature_delta",
3029
+ signature
3206
3030
  }
3207
3031
  });
3032
+ state$1.blockHasDelta.add(blockIndex);
3208
3033
  }
3209
- }
3210
- function closeThinkingBlockIfOpen(state$1, events$1) {
3211
- if (state$1.thinkingBlockOpen) {
3034
+ return events$1;
3035
+ };
3036
+ const handleFunctionCallArgumentsDelta = (rawEvent, state$1) => {
3037
+ const events$1 = new Array();
3038
+ const outputIndex = rawEvent.output_index;
3039
+ const deltaText = rawEvent.delta;
3040
+ if (!deltaText) return events$1;
3041
+ const blockIndex = openFunctionCallBlock(state$1, {
3042
+ outputIndex,
3043
+ events: events$1
3044
+ });
3045
+ const functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
3046
+ if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state$1, events$1);
3047
+ const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
3048
+ if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state$1, events$1);
3049
+ functionCallState.consecutiveWhitespaceCount = nextCount;
3050
+ events$1.push({
3051
+ type: "content_block_delta",
3052
+ index: blockIndex,
3053
+ delta: {
3054
+ type: "input_json_delta",
3055
+ partial_json: deltaText
3056
+ }
3057
+ });
3058
+ state$1.blockHasDelta.add(blockIndex);
3059
+ return events$1;
3060
+ };
3061
+ const handleFunctionCallArgumentsDone = (rawEvent, state$1) => {
3062
+ const events$1 = new Array();
3063
+ const outputIndex = rawEvent.output_index;
3064
+ const blockIndex = openFunctionCallBlock(state$1, {
3065
+ outputIndex,
3066
+ events: events$1
3067
+ });
3068
+ const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
3069
+ if (!state$1.blockHasDelta.has(blockIndex) && finalArguments) {
3212
3070
  events$1.push({
3213
3071
  type: "content_block_delta",
3214
- index: state$1.contentBlockIndex,
3072
+ index: blockIndex,
3215
3073
  delta: {
3216
- type: "signature_delta",
3217
- signature: ""
3074
+ type: "input_json_delta",
3075
+ partial_json: finalArguments
3218
3076
  }
3219
- }, {
3220
- type: "content_block_stop",
3221
- index: state$1.contentBlockIndex
3222
3077
  });
3223
- state$1.contentBlockIndex++;
3224
- state$1.thinkingBlockOpen = false;
3078
+ state$1.blockHasDelta.add(blockIndex);
3225
3079
  }
3226
- }
3227
-
3228
- //#endregion
3229
- //#region src/routes/messages/api-flows.ts
3230
- const handleWithChatCompletions = async (c, anthropicPayload, options) => {
3231
- const { logger: logger$7, subagentMarker, requestId, sessionId, compactType } = options;
3232
- const openAIPayload = translateToOpenAI(anthropicPayload);
3233
- const recordUsage = createCopilotUsageRecorder({
3234
- endpoint: "chat_completions",
3235
- fallbackSessionId: sessionId,
3236
- model: openAIPayload.model,
3237
- payload: anthropicPayload
3080
+ state$1.functionCallStateByOutputIndex.delete(outputIndex);
3081
+ return events$1;
3082
+ };
3083
+ const handleOutputTextDelta = (rawEvent, state$1) => {
3084
+ const events$1 = new Array();
3085
+ const outputIndex = rawEvent.output_index;
3086
+ const contentIndex = rawEvent.content_index;
3087
+ const deltaText = rawEvent.delta;
3088
+ if (!deltaText) return events$1;
3089
+ const blockIndex = openTextBlockIfNeeded(state$1, {
3090
+ outputIndex,
3091
+ contentIndex,
3092
+ events: events$1
3238
3093
  });
3239
- debugJson(logger$7, "Translated OpenAI request payload:", openAIPayload);
3240
- const response = await createChatCompletions(openAIPayload, {
3241
- subagentMarker,
3242
- requestId,
3243
- sessionId,
3244
- compactType
3094
+ events$1.push({
3095
+ type: "content_block_delta",
3096
+ index: blockIndex,
3097
+ delta: {
3098
+ type: "text_delta",
3099
+ text: deltaText
3100
+ }
3245
3101
  });
3246
- if (isNonStreaming(response)) {
3247
- debugJson(logger$7, "Non-streaming response from Copilot:", response);
3248
- recordUsage(normalizeOpenAIUsage(response.usage));
3249
- const anthropicResponse = translateToAnthropic(response);
3250
- debugJson(logger$7, "Translated Anthropic response:", anthropicResponse);
3251
- return c.json(anthropicResponse);
3252
- }
3253
- logger$7.debug("Streaming response from Copilot");
3254
- return streamSSE(c, async (stream) => {
3255
- let usage = {};
3256
- const streamState = {
3257
- messageStartSent: false,
3258
- contentBlockIndex: 0,
3259
- contentBlockOpen: false,
3260
- toolCalls: {},
3261
- thinkingBlockOpen: false
3262
- };
3263
- for await (const rawEvent of response) {
3264
- debugJson(logger$7, "Copilot raw stream event:", rawEvent);
3265
- if (rawEvent.data === "[DONE]") break;
3266
- if (!rawEvent.data) continue;
3267
- const chunk = JSON.parse(rawEvent.data);
3268
- if (chunk.usage) usage = normalizeOpenAIUsage(chunk.usage);
3269
- const events$1 = translateChunkToAnthropicEvents(chunk, streamState);
3270
- for (const event of events$1) {
3271
- const eventData = JSON.stringify(event);
3272
- debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
3273
- await stream.writeSSE({
3274
- event: event.type,
3275
- data: eventData
3276
- });
3277
- }
3102
+ state$1.blockHasDelta.add(blockIndex);
3103
+ return events$1;
3104
+ };
3105
+ const handleReasoningSummaryTextDelta = (rawEvent, state$1) => {
3106
+ const outputIndex = rawEvent.output_index;
3107
+ const deltaText = rawEvent.delta;
3108
+ const events$1 = new Array();
3109
+ const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
3110
+ events$1.push({
3111
+ type: "content_block_delta",
3112
+ index: blockIndex,
3113
+ delta: {
3114
+ type: "thinking_delta",
3115
+ thinking: deltaText
3278
3116
  }
3279
- for (const event of flushPendingAnthropicStreamEvents(streamState)) {
3280
- const eventData = JSON.stringify(event);
3281
- debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
3282
- await stream.writeSSE({
3283
- event: event.type,
3284
- data: eventData
3285
- });
3117
+ });
3118
+ state$1.blockHasDelta.add(blockIndex);
3119
+ return events$1;
3120
+ };
3121
+ const handleReasoningSummaryTextDone = (rawEvent, state$1) => {
3122
+ const outputIndex = rawEvent.output_index;
3123
+ const text = rawEvent.text;
3124
+ const events$1 = new Array();
3125
+ const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
3126
+ if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
3127
+ type: "content_block_delta",
3128
+ index: blockIndex,
3129
+ delta: {
3130
+ type: "thinking_delta",
3131
+ thinking: text
3286
3132
  }
3287
- recordUsage(usage);
3288
3133
  });
3134
+ return events$1;
3289
3135
  };
3290
- const handleWithResponsesApi = async (c, anthropicPayload, options) => {
3291
- const { logger: logger$7, selectedModel,...requestOptions } = options;
3292
- const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
3293
- const recordUsage = createCopilotUsageRecorder({
3294
- endpoint: "responses",
3295
- fallbackSessionId: requestOptions.sessionId,
3296
- model: responsesPayload.model,
3297
- payload: anthropicPayload
3136
+ const handleOutputTextDone = (rawEvent, state$1) => {
3137
+ const events$1 = new Array();
3138
+ const outputIndex = rawEvent.output_index;
3139
+ const contentIndex = rawEvent.content_index;
3140
+ const text = rawEvent.text;
3141
+ const blockIndex = openTextBlockIfNeeded(state$1, {
3142
+ outputIndex,
3143
+ contentIndex,
3144
+ events: events$1
3298
3145
  });
3299
- applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
3300
- compactInputByLatestCompaction(responsesPayload);
3301
- debugJson(logger$7, "Translated Responses payload:", responsesPayload);
3302
- const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
3303
- const response = await createResponses(responsesPayload, {
3304
- vision,
3305
- initiator,
3306
- ...requestOptions
3146
+ if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
3147
+ type: "content_block_delta",
3148
+ index: blockIndex,
3149
+ delta: {
3150
+ type: "text_delta",
3151
+ text
3152
+ }
3307
3153
  });
3308
- if (responsesPayload.stream && isAsyncIterable$1(response)) {
3309
- logger$7.debug("Streaming response from Copilot (Responses API)");
3310
- return streamSSE(c, async (stream) => {
3311
- const streamState = createResponsesStreamState();
3312
- let usage = {};
3313
- for await (const chunk of response) {
3314
- if (chunk.event === "ping") {
3315
- await stream.writeSSE({
3316
- event: "ping",
3317
- data: "{\"type\":\"ping\"}"
3318
- });
3319
- continue;
3320
- }
3321
- const data = chunk.data;
3322
- if (!data) continue;
3323
- debugLazy(logger$7, () => ["Responses raw stream event:", data]);
3324
- const responseEvent = JSON.parse(data);
3325
- if (responseEvent.type === "response.completed" || responseEvent.type === "response.failed" || responseEvent.type === "response.incomplete") usage = normalizeResponsesUsage(responseEvent.response.usage);
3326
- const events$1 = translateResponsesStreamEvent(responseEvent, streamState);
3327
- for (const event of events$1) {
3328
- const eventData = JSON.stringify(event);
3329
- debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
3330
- await stream.writeSSE({
3331
- event: event.type,
3332
- data: eventData
3333
- });
3334
- }
3335
- if (streamState.messageCompleted) {
3336
- logger$7.debug("Message completed, ending stream");
3337
- break;
3338
- }
3154
+ return events$1;
3155
+ };
3156
+ const handleResponseCompleted = (rawEvent, state$1) => {
3157
+ const response = rawEvent.response;
3158
+ const events$1 = new Array();
3159
+ closeAllOpenBlocks(state$1, events$1);
3160
+ const anthropic = translateResponsesResultToAnthropic(response);
3161
+ events$1.push({
3162
+ type: "message_delta",
3163
+ delta: {
3164
+ stop_reason: anthropic.stop_reason,
3165
+ stop_sequence: anthropic.stop_sequence
3166
+ },
3167
+ usage: anthropic.usage
3168
+ }, { type: "message_stop" });
3169
+ state$1.messageCompleted = true;
3170
+ return events$1;
3171
+ };
3172
+ const handleResponseFailed = (rawEvent, state$1) => {
3173
+ const response = rawEvent.response;
3174
+ const events$1 = new Array();
3175
+ closeAllOpenBlocks(state$1, events$1);
3176
+ const message = response.error?.message ?? "The response failed due to an unknown error.";
3177
+ events$1.push(buildErrorEvent(message));
3178
+ state$1.messageCompleted = true;
3179
+ return events$1;
3180
+ };
3181
+ const handleErrorEvent = (rawEvent, state$1) => {
3182
+ const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
3183
+ state$1.messageCompleted = true;
3184
+ return [buildErrorEvent(message)];
3185
+ };
3186
+ const handleFunctionCallArgumentsValidationError = (error, state$1, events$1 = []) => {
3187
+ const reason = error.message;
3188
+ closeAllOpenBlocks(state$1, events$1);
3189
+ state$1.messageCompleted = true;
3190
+ events$1.push(buildErrorEvent(reason));
3191
+ return events$1;
3192
+ };
3193
+ const messageStart = (state$1, response) => {
3194
+ state$1.messageStartSent = true;
3195
+ const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
3196
+ const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
3197
+ return [{
3198
+ type: "message_start",
3199
+ message: {
3200
+ id: response.id,
3201
+ type: "message",
3202
+ role: "assistant",
3203
+ content: [],
3204
+ model: response.model,
3205
+ stop_reason: null,
3206
+ stop_sequence: null,
3207
+ usage: {
3208
+ input_tokens: inputTokens,
3209
+ output_tokens: 0,
3210
+ cache_read_input_tokens: inputCachedTokens ?? 0
3339
3211
  }
3340
- if (!streamState.messageCompleted) {
3341
- logger$7.warn("Responses stream ended without completion; sending error event");
3342
- const errorEvent = buildErrorEvent("Responses stream ended without completion");
3343
- await stream.writeSSE({
3344
- event: errorEvent.type,
3345
- data: JSON.stringify(errorEvent)
3346
- });
3212
+ }
3213
+ }];
3214
+ };
3215
+ const openTextBlockIfNeeded = (state$1, params) => {
3216
+ const { outputIndex, contentIndex, events: events$1 } = params;
3217
+ const key = getBlockKey(outputIndex, contentIndex);
3218
+ let blockIndex = state$1.blockIndexByKey.get(key);
3219
+ if (blockIndex === void 0) {
3220
+ blockIndex = state$1.nextContentBlockIndex;
3221
+ state$1.nextContentBlockIndex += 1;
3222
+ state$1.blockIndexByKey.set(key, blockIndex);
3223
+ }
3224
+ if (!state$1.openBlocks.has(blockIndex)) {
3225
+ closeOpenBlocks(state$1, events$1);
3226
+ events$1.push({
3227
+ type: "content_block_start",
3228
+ index: blockIndex,
3229
+ content_block: {
3230
+ type: "text",
3231
+ text: ""
3347
3232
  }
3348
- recordUsage(usage);
3349
3233
  });
3234
+ state$1.openBlocks.add(blockIndex);
3350
3235
  }
3351
- debugJsonTail(logger$7, "Non-streaming Responses result:", {
3352
- value: response,
3353
- tailLength: 400
3354
- });
3355
- const anthropicResponse = translateResponsesResultToAnthropic(response);
3356
- recordUsage(normalizeResponsesUsage(response.usage));
3357
- debugJson(logger$7, "Translated Anthropic response:", anthropicResponse);
3358
- return c.json(anthropicResponse);
3236
+ return blockIndex;
3359
3237
  };
3360
- const handleWithMessagesApi = async (c, anthropicPayload, options) => {
3361
- const { logger: logger$7, anthropicBetaHeader, subagentMarker, selectedModel, requestId, sessionId, compactType } = options;
3362
- prepareMessagesApiPayload(anthropicPayload, selectedModel);
3363
- const recordUsage = createCopilotUsageRecorder({
3364
- endpoint: "messages",
3365
- fallbackSessionId: sessionId,
3366
- model: anthropicPayload.model,
3367
- payload: anthropicPayload
3368
- });
3369
- debugJson(logger$7, "Translated Messages payload:", anthropicPayload);
3370
- const response = await createMessages(anthropicPayload, anthropicBetaHeader, {
3371
- subagentMarker,
3372
- requestId,
3373
- sessionId,
3374
- compactType
3375
- });
3376
- if (isAsyncIterable$1(response)) {
3377
- logger$7.debug("Streaming response from Copilot (Messages API)");
3378
- return streamSSE(c, async (stream) => {
3379
- let usage = {};
3380
- for await (const event of response) {
3381
- const eventName = event.event;
3382
- const data = event.data ?? "";
3383
- if (data === "[DONE]") break;
3384
- if (!data) continue;
3385
- debugLazy(logger$7, () => ["Messages raw stream event:", data]);
3386
- const parsedEvent = parseAnthropicStreamEvent(data);
3387
- if (parsedEvent?.type === "message_start") usage = mergeAnthropicUsage(usage, normalizeAnthropicUsage(parsedEvent.message.usage));
3388
- else if (parsedEvent?.type === "message_delta") usage = mergeAnthropicUsage(usage, normalizeAnthropicUsage(parsedEvent.usage));
3389
- await stream.writeSSE({
3390
- event: eventName,
3391
- data
3392
- });
3238
+ const openThinkingBlockIfNeeded = (state$1, outputIndex, events$1) => {
3239
+ const key = getBlockKey(outputIndex, 0);
3240
+ let blockIndex = state$1.blockIndexByKey.get(key);
3241
+ if (blockIndex === void 0) {
3242
+ blockIndex = state$1.nextContentBlockIndex;
3243
+ state$1.nextContentBlockIndex += 1;
3244
+ state$1.blockIndexByKey.set(key, blockIndex);
3245
+ }
3246
+ if (!state$1.openBlocks.has(blockIndex)) {
3247
+ closeOpenBlocks(state$1, events$1);
3248
+ events$1.push({
3249
+ type: "content_block_start",
3250
+ index: blockIndex,
3251
+ content_block: {
3252
+ type: "thinking",
3253
+ thinking: ""
3393
3254
  }
3394
- recordUsage(usage);
3395
3255
  });
3256
+ state$1.openBlocks.add(blockIndex);
3396
3257
  }
3397
- debugJsonTail(logger$7, "Non-streaming Messages result:", {
3398
- value: response,
3399
- tailLength: 400
3258
+ return blockIndex;
3259
+ };
3260
+ const closeBlockIfOpen = (state$1, blockIndex, events$1) => {
3261
+ if (!state$1.openBlocks.has(blockIndex)) return;
3262
+ events$1.push({
3263
+ type: "content_block_stop",
3264
+ index: blockIndex
3400
3265
  });
3401
- recordUsage(normalizeAnthropicUsage(response.usage));
3402
- return c.json(response);
3266
+ state$1.openBlocks.delete(blockIndex);
3267
+ state$1.blockHasDelta.delete(blockIndex);
3403
3268
  };
3404
- const isNonStreaming = (response) => Object.hasOwn(response, "choices");
3405
- const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
3406
- const createCopilotUsageRecorder = (options) => createCopilotTokenUsageRecorder({
3407
- endpoint: options.endpoint,
3408
- fallbackSessionId: options.fallbackSessionId,
3409
- model: options.model,
3410
- sessionId: getMetadataSessionId(options.payload)
3411
- });
3412
- const getMetadataSessionId = (payload) => parseUserIdMetadata(payload.metadata?.user_id).sessionId;
3413
- const parseAnthropicStreamEvent = (data) => {
3414
- try {
3415
- return JSON.parse(data);
3416
- } catch {
3417
- return null;
3418
- }
3269
+ const closeOpenBlocks = (state$1, events$1) => {
3270
+ for (const blockIndex of state$1.openBlocks) closeBlockIfOpen(state$1, blockIndex, events$1);
3419
3271
  };
3420
-
3421
- //#endregion
3422
- //#region src/lib/subagent.ts
3423
- const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
3424
-
3425
- //#endregion
3426
- //#region src/routes/messages/subagent-marker.ts
3427
- const parseSubagentMarkerFromFirstUser = (payload) => {
3428
- const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
3429
- if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
3430
- for (const block of firstUserMessage.content) {
3431
- if (block.type !== "text") continue;
3432
- const marker = parseSubagentMarkerFromSystemReminder(block.text);
3433
- if (marker) return marker;
3434
- }
3435
- return null;
3272
+ const closeAllOpenBlocks = (state$1, events$1) => {
3273
+ closeOpenBlocks(state$1, events$1);
3274
+ state$1.functionCallStateByOutputIndex.clear();
3436
3275
  };
3437
- const parseSubagentMarkerFromSystemReminder = (text) => {
3438
- const startTag = "<system-reminder>";
3439
- const endTag = "</system-reminder>";
3440
- let searchFrom = 0;
3441
- while (true) {
3442
- const reminderStart = text.indexOf(startTag, searchFrom);
3443
- if (reminderStart === -1) break;
3444
- const contentStart = reminderStart + 17;
3445
- const reminderEnd = text.indexOf(endTag, contentStart);
3446
- if (reminderEnd === -1) break;
3447
- const reminderContent = text.slice(contentStart, reminderEnd);
3448
- const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
3449
- if (markerIndex === -1) {
3450
- searchFrom = reminderEnd + 18;
3451
- continue;
3452
- }
3453
- const markerJson = reminderContent.slice(markerIndex + subagentMarkerPrefix.length).trim();
3454
- try {
3455
- const parsed = JSON.parse(markerJson);
3456
- if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
3457
- searchFrom = reminderEnd + 18;
3458
- continue;
3276
+ const buildErrorEvent = (message) => ({
3277
+ type: "error",
3278
+ error: {
3279
+ type: "api_error",
3280
+ message
3281
+ }
3282
+ });
3283
+ const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
3284
+ const openFunctionCallBlock = (state$1, params) => {
3285
+ const { outputIndex, toolCallId, name, events: events$1 } = params;
3286
+ let functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
3287
+ if (!functionCallState) {
3288
+ const blockIndex$1 = state$1.nextContentBlockIndex;
3289
+ state$1.nextContentBlockIndex += 1;
3290
+ const resolvedToolCallId = toolCallId ?? `tool_call_${blockIndex$1}`;
3291
+ functionCallState = {
3292
+ blockIndex: blockIndex$1,
3293
+ toolCallId: resolvedToolCallId,
3294
+ name: name ?? "function",
3295
+ consecutiveWhitespaceCount: 0
3296
+ };
3297
+ state$1.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
3298
+ }
3299
+ const { blockIndex } = functionCallState;
3300
+ if (!state$1.openBlocks.has(blockIndex)) {
3301
+ closeOpenBlocks(state$1, events$1);
3302
+ events$1.push({
3303
+ type: "content_block_start",
3304
+ index: blockIndex,
3305
+ content_block: {
3306
+ type: "tool_use",
3307
+ id: functionCallState.toolCallId,
3308
+ name: functionCallState.name,
3309
+ input: {}
3459
3310
  }
3460
- return parsed;
3461
- } catch {
3462
- searchFrom = reminderEnd + 18;
3463
- continue;
3464
- }
3311
+ });
3312
+ state$1.openBlocks.add(blockIndex);
3465
3313
  }
3466
- return null;
3314
+ return blockIndex;
3315
+ };
3316
+ const extractFunctionCallDetails = (rawEvent) => {
3317
+ const item = rawEvent.item;
3318
+ if (item.type !== "function_call") return;
3319
+ const outputIndex = rawEvent.output_index;
3320
+ const toolCallId = item.call_id;
3321
+ const name = item.name;
3322
+ const initialArguments = item.arguments;
3323
+ return {
3324
+ outputIndex,
3325
+ toolCallId,
3326
+ name,
3327
+ initialArguments
3328
+ };
3467
3329
  };
3468
3330
 
3469
3331
  //#endregion
3470
- //#region src/routes/messages/handler.ts
3471
- const logger$5 = createHandlerLogger("messages-handler");
3472
- async function handleCompletion(c) {
3473
- await checkRateLimit(state);
3474
- const anthropicPayload = await c.req.json();
3475
- debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
3476
- sanitizeIdeTools(anthropicPayload);
3477
- const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
3478
- if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
3479
- const sessionId = getRootSessionId(anthropicPayload, c);
3480
- logger$5.debug("Extracted session ID:", sessionId);
3481
- const compactType = getCompactType(anthropicPayload);
3482
- const anthropicBeta = c.req.header("anthropic-beta");
3483
- logger$5.debug("Anthropic Beta header:", anthropicBeta);
3484
- const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
3485
- if (anthropicBeta && noTools && compactType === 0) anthropicPayload.model = getSmallModel();
3486
- if (compactType) logger$5.debug("Compact request type:", compactType);
3487
- stripToolReferenceTurnBoundary(anthropicPayload);
3488
- mergeToolResultForClaude(anthropicPayload, { skipLastMessage: compactType === COMPACT_REQUEST });
3489
- const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
3490
- logger$5.debug("Generated request ID:", requestId);
3491
- if (state.manualApprove) await awaitApproval();
3492
- const selectedModel = findEndpointModel(anthropicPayload.model);
3493
- anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
3494
- if (shouldUseMessagesApi(selectedModel)) return await handleWithMessagesApi(c, anthropicPayload, {
3495
- anthropicBetaHeader: anthropicBeta,
3496
- subagentMarker,
3497
- selectedModel,
3498
- requestId,
3499
- sessionId,
3500
- compactType,
3501
- logger: logger$5
3502
- });
3503
- if (shouldUseResponsesApi(selectedModel)) return await handleWithResponsesApi(c, anthropicPayload, {
3504
- subagentMarker,
3505
- selectedModel,
3506
- requestId,
3507
- sessionId,
3508
- compactType,
3509
- logger: logger$5
3510
- });
3511
- return await handleWithChatCompletions(c, anthropicPayload, {
3512
- subagentMarker,
3513
- requestId,
3514
- sessionId,
3515
- compactType,
3516
- logger: logger$5
3517
- });
3518
- }
3519
- const RESPONSES_ENDPOINT$1 = "/responses";
3520
- const MESSAGES_ENDPOINT = "/v1/messages";
3521
- const shouldUseResponsesApi = (selectedModel) => {
3522
- return selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
3332
+ //#region src/routes/responses/utils.ts
3333
+ const getResponsesRequestOptions = (payload) => {
3334
+ const vision = hasVisionInput(payload);
3335
+ const initiator = hasAgentInitiator(payload) ? "agent" : "user";
3336
+ return {
3337
+ vision,
3338
+ initiator
3339
+ };
3340
+ };
3341
+ const hasAgentInitiator = (payload) => {
3342
+ const lastItem = getPayloadItems(payload).at(-1);
3343
+ if (!lastItem) return false;
3344
+ if (!("role" in lastItem) || !lastItem.role) return true;
3345
+ return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
3523
3346
  };
3524
- const shouldUseMessagesApi = (selectedModel) => {
3525
- if (!isMessagesApiEnabled()) return false;
3526
- return selectedModel?.supported_endpoints?.includes(MESSAGES_ENDPOINT) ?? false;
3347
+ const hasVisionInput = (payload) => {
3348
+ return getPayloadItems(payload).some((item) => containsVisionContent(item));
3349
+ };
3350
+ const resolveResponsesCompactThreshold = (maxPromptTokens) => {
3351
+ if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
3352
+ return 5e4;
3353
+ };
3354
+ const createCompactionContextManagement = (compactThreshold) => [{
3355
+ type: "compaction",
3356
+ compact_threshold: compactThreshold
3357
+ }];
3358
+ const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
3359
+ if (payload.context_management !== void 0) return;
3360
+ if (!isResponsesApiContextManagementModel(payload.model)) return;
3361
+ payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
3362
+ };
3363
+ const compactInputByLatestCompaction = (payload) => {
3364
+ if (!Array.isArray(payload.input) || payload.input.length === 0) return;
3365
+ const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
3366
+ if (latestCompactionMessageIndex === void 0) return;
3367
+ payload.input = payload.input.slice(latestCompactionMessageIndex);
3368
+ };
3369
+ const getLatestCompactionMessageIndex = (input) => {
3370
+ for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
3371
+ };
3372
+ const isCompactionInputItem = (value) => {
3373
+ return "type" in value && typeof value.type === "string" && value.type === "compaction";
3374
+ };
3375
+ const getPayloadItems = (payload) => {
3376
+ const result = [];
3377
+ const { input } = payload;
3378
+ if (Array.isArray(input)) result.push(...input);
3379
+ return result;
3380
+ };
3381
+ const containsVisionContent = (value) => {
3382
+ if (!value) return false;
3383
+ if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
3384
+ if (typeof value !== "object") return false;
3385
+ const record = value;
3386
+ if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
3387
+ if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
3388
+ return false;
3527
3389
  };
3528
3390
 
3529
3391
  //#endregion
3530
- //#region src/routes/messages/route.ts
3531
- const messageRoutes = new Hono();
3532
- messageRoutes.post("/", async (c) => {
3533
- try {
3534
- return await handleCompletion(c);
3535
- } catch (error) {
3536
- return await forwardError(c, error);
3392
+ //#region src/services/copilot/create-messages.ts
3393
+ const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
3394
+ const allowedAnthropicBetas = new Set([
3395
+ INTERLEAVED_THINKING_BETA,
3396
+ "context-management-2025-06-27",
3397
+ "advanced-tool-use-2025-11-20"
3398
+ ]);
3399
+ const buildAnthropicBetaHeader = (anthropicBetaHeader, thinking, _model) => {
3400
+ const isAdaptiveThinking = thinking?.type === "adaptive";
3401
+ if (anthropicBetaHeader) {
3402
+ const uniqueFilteredBetas = [...anthropicBetaHeader.split(",").map((item) => item.trim()).filter((item) => item.length > 0).filter((item) => allowedAnthropicBetas.has(item))];
3403
+ if (uniqueFilteredBetas.length > 0) return uniqueFilteredBetas.join(",");
3404
+ return;
3537
3405
  }
3538
- });
3539
- messageRoutes.post("/count_tokens", async (c) => {
3540
- try {
3541
- return await handleCountTokens(c);
3542
- } catch (error) {
3543
- return await forwardError(c, error);
3406
+ if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
3407
+ };
3408
+ const createMessages = async (payload, anthropicBetaHeader, options) => {
3409
+ if (!state.copilotToken) throw new Error("Copilot token not found");
3410
+ const enableVision = payload.messages.some((message) => {
3411
+ if (!Array.isArray(message.content)) return false;
3412
+ return message.content.some((block) => block.type === "image" || block.type === "tool_result" && Array.isArray(block.content) && block.content.some((inner) => inner.type === "image"));
3413
+ });
3414
+ let isInitiateRequest = false;
3415
+ const lastMessage = payload.messages.at(-1);
3416
+ if (lastMessage?.role === "user") isInitiateRequest = Array.isArray(lastMessage.content) ? lastMessage.content.some((block) => block.type !== "tool_result") : true;
3417
+ const headers = {
3418
+ ...copilotHeaders(state, options.requestId, enableVision),
3419
+ "x-initiator": isInitiateRequest ? "user" : "agent"
3420
+ };
3421
+ prepareInteractionHeaders(options.sessionId, Boolean(options.subagentMarker), headers);
3422
+ prepareForCompact(headers, options.compactType);
3423
+ const { safetyIdentifier, sessionId } = parseUserIdMetadata(payload.metadata?.user_id);
3424
+ if (safetyIdentifier && sessionId) prepareMessageProxyHeaders(headers);
3425
+ const anthropicBeta = buildAnthropicBetaHeader(anthropicBetaHeader, payload.thinking, payload.model);
3426
+ if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
3427
+ consola.log(`<-- model: ${payload.model}`);
3428
+ const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {
3429
+ method: "POST",
3430
+ headers,
3431
+ body: JSON.stringify(payload)
3432
+ });
3433
+ logCopilotRateLimits(response.headers);
3434
+ if (!response.ok) {
3435
+ consola.error("Failed to create messages", response);
3436
+ throw new HTTPError("Failed to create messages", response);
3544
3437
  }
3545
- });
3438
+ if (payload.stream) return events(response);
3439
+ return await response.json();
3440
+ };
3546
3441
 
3547
3442
  //#endregion
3548
- //#region src/routes/models/route.ts
3549
- const modelRoutes = new Hono();
3550
- modelRoutes.get("/", async (c) => {
3551
- try {
3552
- if (!state.models) await cacheModels();
3553
- const models = state.models?.data.map((model) => {
3554
- const is1m = model.capabilities.limits?.max_context_window_tokens === 1e6;
3555
- return {
3556
- ...model,
3557
- id: is1m ? `${model.id}[1m]` : model.id,
3558
- object: "model",
3559
- type: "model",
3560
- created: 0,
3561
- created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
3562
- owned_by: model.vendor,
3563
- display_name: model.name
3564
- };
3565
- });
3566
- return c.json({
3567
- object: "list",
3568
- data: models,
3569
- has_more: false
3570
- });
3571
- } catch (error) {
3572
- return await forwardError(c, error);
3443
+ //#region src/routes/messages/preprocess.ts
3444
+ const TOOL_REFERENCE_TURN_BOUNDARY = "Tool loaded.";
3445
+ const IDE_EXECUTE_CODE_TOOL = "mcp__ide__executeCode";
3446
+ const IDE_GET_DIAGNOSTICS_TOOL = "mcp__ide__getDiagnostics";
3447
+ const IDE_GET_DIAGNOSTICS_DESCRIPTION = "Get language diagnostics from VS Code. Returns errors, warnings, information, and hints for files in the workspace.";
3448
+ const PDF_FILE_READ_PREFIX = "PDF file read:";
3449
+ const getCompactCandidateText = (message) => {
3450
+ if (message.role !== "user") return "";
3451
+ if (typeof message.content === "string") return message.content;
3452
+ return message.content.filter((block) => block.type === "text").map((block) => block.text.startsWith("<system-reminder>") ? "" : block.text).filter((text) => text.length > 0).join("\n\n");
3453
+ };
3454
+ const isCompactMessage = (lastMessage) => {
3455
+ const text = getCompactCandidateText(lastMessage);
3456
+ if (!text) return false;
3457
+ return text.includes(compactTextOnlyGuard) && text.includes(compactSummaryPromptStart) && compactMessageSections.some((section) => text.includes(section));
3458
+ };
3459
+ const isCompactAutoContinueMessage = (lastMessage) => {
3460
+ const text = getCompactCandidateText(lastMessage);
3461
+ return Boolean(text) && compactAutoContinuePromptStarts.some((promptStart) => text.startsWith(promptStart));
3462
+ };
3463
+ const getCompactType = (anthropicPayload) => {
3464
+ const lastMessage = anthropicPayload.messages.at(-1);
3465
+ if (lastMessage && isCompactMessage(lastMessage)) return COMPACT_REQUEST;
3466
+ if (lastMessage && isCompactAutoContinueMessage(lastMessage)) return COMPACT_AUTO_CONTINUE;
3467
+ const system = anthropicPayload.system;
3468
+ if (typeof system === "string") return compactSystemPromptStarts.some((promptStart) => system.startsWith(promptStart)) ? COMPACT_REQUEST : 0;
3469
+ if (!Array.isArray(system)) return 0;
3470
+ if (system.some((msg) => typeof msg.text === "string" && compactSystemPromptStarts.some((promptStart) => msg.text.startsWith(promptStart)))) return COMPACT_REQUEST;
3471
+ return 0;
3472
+ };
3473
+ const mergeContentWithText = (tr, textBlock) => {
3474
+ if (typeof tr.content === "string") return {
3475
+ ...tr,
3476
+ content: `${tr.content}\n\n${textBlock.text}`
3477
+ };
3478
+ if (hasToolRef(tr)) return tr;
3479
+ return {
3480
+ ...tr,
3481
+ content: [...tr.content, textBlock]
3482
+ };
3483
+ };
3484
+ const mergeContentWithTexts = (tr, textBlocks) => {
3485
+ if (typeof tr.content === "string") {
3486
+ const appendedTexts = textBlocks.map((tb) => tb.text).join("\n\n");
3487
+ return {
3488
+ ...tr,
3489
+ content: `${tr.content}\n\n${appendedTexts}`
3490
+ };
3573
3491
  }
3574
- });
3575
-
3576
- //#endregion
3577
- //#region src/routes/provider/messages/count-tokens-handler.ts
3578
- const logger$4 = createHandlerLogger("provider-count-tokens-handler");
3579
- const createFallbackModel = (modelId) => ({
3580
- capabilities: {
3581
- family: "provider",
3582
- limits: {},
3583
- object: "model_capabilities",
3584
- supports: {},
3585
- tokenizer: "o200k_base",
3586
- type: "chat"
3587
- },
3588
- id: modelId,
3589
- model_picker_enabled: false,
3590
- name: modelId,
3591
- object: "model",
3592
- preview: false,
3593
- vendor: "provider",
3594
- version: "unknown"
3595
- });
3596
- async function handleProviderCountTokens(c) {
3597
- const provider = c.req.param("provider");
3598
- try {
3599
- const anthropicPayload = await c.req.json();
3600
- const modelId = anthropicPayload.model.trim();
3601
- const providerConfig = getProviderConfig(provider);
3602
- const modelConfig = providerConfig?.models?.[modelId];
3603
- const translationOptions = providerConfig?.type === "openai-compatible" ? {
3604
- supportPdf: modelConfig?.supportPdf,
3605
- toolContentSupportType: modelConfig?.toolContentSupportType ?? []
3606
- } : void 0;
3607
- const openAIPayload = translateToOpenAI(anthropicPayload, translationOptions);
3608
- let selectedModel = state.models?.data.find((model) => model.id === modelId);
3609
- if (!selectedModel && modelId) selectedModel = createFallbackModel(modelId);
3610
- if (!selectedModel) {
3611
- logger$4.warn("provider.count_tokens.model_not_found", {
3612
- provider,
3613
- model: anthropicPayload.model
3492
+ if (hasToolRef(tr)) return tr;
3493
+ return {
3494
+ ...tr,
3495
+ content: [...tr.content, ...textBlocks]
3496
+ };
3497
+ };
3498
+ const mergeContentWithAttachments = (tr, attachments) => {
3499
+ if (typeof tr.content === "string") return {
3500
+ ...tr,
3501
+ content: [{
3502
+ type: "text",
3503
+ text: tr.content
3504
+ }, ...attachments]
3505
+ };
3506
+ return {
3507
+ ...tr,
3508
+ content: [...tr.content, ...attachments]
3509
+ };
3510
+ };
3511
+ const isAttachmentBlock = (block) => {
3512
+ return block.type === "image" || block.type === "document";
3513
+ };
3514
+ const getMergeableToolResultIndices = (toolResults) => {
3515
+ return toolResults.flatMap((block, index) => block.is_error || hasToolRef(block) ? [] : [index]);
3516
+ };
3517
+ const mergeAttachmentsIntoToolResults = (toolResults, attachmentsByToolResultIndex) => {
3518
+ if (attachmentsByToolResultIndex.size === 0) return toolResults;
3519
+ return toolResults.map((block, index) => {
3520
+ const matchedAttachments = attachmentsByToolResultIndex.get(index);
3521
+ if (!matchedAttachments) return block;
3522
+ const orderedAttachments = [...matchedAttachments].sort((left, right) => left.order - right.order).map(({ attachment }) => attachment);
3523
+ return mergeContentWithAttachments(block, orderedAttachments);
3524
+ });
3525
+ };
3526
+ const assignAttachmentsToToolResults = (target, attachments, options) => {
3527
+ const { toolResultIndices } = options;
3528
+ const fallbackToolResultIndices = options.fallbackToolResultIndices ?? toolResultIndices;
3529
+ if (attachments.length === 0) return;
3530
+ if (toolResultIndices.length > 0 && toolResultIndices.length === attachments.length) {
3531
+ for (const [index, toolResultIndex] of toolResultIndices.entries()) {
3532
+ const currentAttachments$1 = target.get(toolResultIndex);
3533
+ if (currentAttachments$1) {
3534
+ currentAttachments$1.push(attachments[index]);
3535
+ continue;
3536
+ }
3537
+ target.set(toolResultIndex, [attachments[index]]);
3538
+ }
3539
+ return;
3540
+ }
3541
+ const lastToolResultIndex = fallbackToolResultIndices.at(-1);
3542
+ if (lastToolResultIndex === void 0) return;
3543
+ const currentAttachments = target.get(lastToolResultIndex);
3544
+ if (currentAttachments) {
3545
+ currentAttachments.push(...attachments);
3546
+ return;
3547
+ }
3548
+ target.set(lastToolResultIndex, [...attachments]);
3549
+ };
3550
+ const startsWithPdfFileRead = (toolResult) => {
3551
+ if (typeof toolResult.content === "string") return toolResult.content.startsWith(PDF_FILE_READ_PREFIX);
3552
+ if (toolResult.content.some((block) => block.type === "document")) return false;
3553
+ if (toolResult.content.length === 0) return false;
3554
+ const firstBlock = toolResult.content[0];
3555
+ if (firstBlock.type !== "text") return false;
3556
+ return firstBlock.text.startsWith(PDF_FILE_READ_PREFIX);
3557
+ };
3558
+ const collectMergeableUserContent = (content) => {
3559
+ const toolResults = [];
3560
+ const textBlocks = [];
3561
+ const attachments = [];
3562
+ for (const [order, block] of content.entries()) {
3563
+ if (block.type === "tool_result") {
3564
+ toolResults.push(block);
3565
+ continue;
3566
+ }
3567
+ if (block.type === "text") {
3568
+ textBlocks.push(block);
3569
+ continue;
3570
+ }
3571
+ if (isAttachmentBlock(block)) {
3572
+ attachments.push({
3573
+ attachment: block,
3574
+ order
3614
3575
  });
3615
- return c.json({ input_tokens: 1 });
3576
+ continue;
3616
3577
  }
3617
- const tokenCount = await getTokenCount(openAIPayload, selectedModel);
3618
- const finalTokenCount = tokenCount.input + tokenCount.output;
3619
- logger$4.debug("provider.count_tokens.success", {
3620
- provider,
3621
- model: anthropicPayload.model,
3622
- input_tokens: finalTokenCount
3623
- });
3624
- return c.json({ input_tokens: finalTokenCount });
3625
- } catch (error) {
3626
- logger$4.error("provider.count_tokens.error", {
3627
- provider,
3628
- error
3629
- });
3630
- return c.json({ input_tokens: 1 });
3578
+ return null;
3631
3579
  }
3632
- }
3633
-
3634
- //#endregion
3635
- //#region src/services/providers/anthropic-proxy.ts
3636
- const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
3637
- const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
3638
- const STRIPPED_RESPONSE_HEADERS = [
3639
- "connection",
3640
- "content-encoding",
3641
- "content-length",
3642
- "keep-alive",
3643
- "proxy-authenticate",
3644
- "proxy-authorization",
3645
- "te",
3646
- "trailer",
3647
- "transfer-encoding",
3648
- "upgrade"
3649
- ];
3650
- function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
3651
- const authHeaders = {};
3652
- if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
3653
- else authHeaders["x-api-key"] = providerConfig.apiKey;
3654
- const headers = {
3655
- "content-type": "application/json",
3656
- accept: "application/json",
3657
- ...authHeaders
3580
+ return {
3581
+ toolResults,
3582
+ textBlocks,
3583
+ attachments
3658
3584
  };
3659
- for (const headerName of SHARED_FORWARDABLE_HEADERS) {
3660
- const headerValue = requestHeaders.get(headerName);
3661
- if (headerValue) headers[headerName] = headerValue;
3662
- }
3663
- if (providerConfig.type !== "anthropic") return headers;
3664
- for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
3665
- const headerValue = requestHeaders.get(headerName);
3666
- if (headerValue) headers[headerName] = headerValue;
3585
+ };
3586
+ const mergeAttachmentsForToolResults = (toolResults, attachments) => {
3587
+ if (attachments.length === 0) return toolResults;
3588
+ const documentBlocks = attachments.filter(({ attachment }) => attachment.type === "document");
3589
+ const mergeableToolResultIndices = getMergeableToolResultIndices(toolResults);
3590
+ const pdfReadToolResultIndices = mergeableToolResultIndices.filter((index) => startsWithPdfFileRead(toolResults[index]));
3591
+ const attachmentsByToolResultIndex = /* @__PURE__ */ new Map();
3592
+ let remainingAttachments = attachments;
3593
+ let countMatchToolResultIndices = mergeableToolResultIndices;
3594
+ if (documentBlocks.length > 0 && pdfReadToolResultIndices.length > 0) {
3595
+ const matchedDocumentCount = Math.min(pdfReadToolResultIndices.length, documentBlocks.length);
3596
+ const matchedDocuments = documentBlocks.slice(0, matchedDocumentCount);
3597
+ const matchedDocumentOrders = new Set(matchedDocuments.map(({ order }) => order));
3598
+ const matchedPdfToolResultIndices = pdfReadToolResultIndices.slice(0, matchedDocumentCount);
3599
+ const matchedPdfToolResultIndexSet = new Set(matchedPdfToolResultIndices);
3600
+ assignAttachmentsToToolResults(attachmentsByToolResultIndex, matchedDocuments, { toolResultIndices: matchedPdfToolResultIndices });
3601
+ countMatchToolResultIndices = mergeableToolResultIndices.filter((index) => !matchedPdfToolResultIndexSet.has(index));
3602
+ remainingAttachments = attachments.filter(({ attachment, order }) => attachment.type !== "document" || !matchedDocumentOrders.has(order));
3667
3603
  }
3668
- return headers;
3669
- }
3670
- function createProviderProxyResponse(upstreamResponse) {
3671
- const headers = new Headers(upstreamResponse.headers);
3672
- for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
3673
- return new Response(upstreamResponse.body, {
3674
- headers,
3675
- status: upstreamResponse.status,
3676
- statusText: upstreamResponse.statusText
3677
- });
3678
- }
3679
- async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
3680
- return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
3681
- method: "POST",
3682
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
3683
- body: JSON.stringify(payload)
3684
- });
3685
- }
3686
- async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
3687
- return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
3688
- method: "POST",
3689
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
3690
- body: JSON.stringify(payload)
3604
+ assignAttachmentsToToolResults(attachmentsByToolResultIndex, remainingAttachments, {
3605
+ toolResultIndices: countMatchToolResultIndices,
3606
+ fallbackToolResultIndices: mergeableToolResultIndices
3691
3607
  });
3692
- }
3693
- async function forwardProviderModels(providerConfig, requestHeaders) {
3694
- return await fetch(`${providerConfig.baseUrl}/v1/models`, {
3695
- method: "GET",
3696
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
3608
+ return mergeAttachmentsIntoToolResults(toolResults, attachmentsByToolResultIndex);
3609
+ };
3610
+ const mergeUserMessageContent = (content) => {
3611
+ const mergeableContent = collectMergeableUserContent(content);
3612
+ if (!mergeableContent) return null;
3613
+ const { toolResults, textBlocks, attachments } = mergeableContent;
3614
+ if (toolResults.length === 0 || textBlocks.length === 0 && attachments.length === 0) return null;
3615
+ const mergedToolResults = textBlocks.length === 0 ? toolResults : mergeToolResult(toolResults, textBlocks);
3616
+ return mergeAttachmentsForToolResults(mergedToolResults, attachments);
3617
+ };
3618
+ const mergeToolResult = (toolResults, textBlocks) => {
3619
+ if (toolResults.length === textBlocks.length) return toolResults.map((tr, i) => mergeContentWithText(tr, textBlocks[i]));
3620
+ const lastIndex = toolResults.length - 1;
3621
+ return toolResults.map((tr, i) => i === lastIndex ? mergeContentWithTexts(tr, textBlocks) : tr);
3622
+ };
3623
+ const stripToolReferenceTurnBoundary = (anthropicPayload) => {
3624
+ for (const msg of anthropicPayload.messages) {
3625
+ if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
3626
+ if (!msg.content.some((block) => block.type === "tool_result" && hasToolRef(block))) continue;
3627
+ msg.content = msg.content.filter((block) => block.type !== "text" || block.text.trim() !== TOOL_REFERENCE_TURN_BOUNDARY);
3628
+ }
3629
+ };
3630
+ const mergeToolResultForClaude = (anthropicPayload, options) => {
3631
+ const lastMessageIndex = anthropicPayload.messages.length - 1;
3632
+ for (const [index, msg] of anthropicPayload.messages.entries()) {
3633
+ if (options?.skipLastMessage && index === lastMessageIndex) continue;
3634
+ if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
3635
+ const mergedContent = mergeUserMessageContent(msg.content);
3636
+ if (mergedContent) msg.content = mergedContent;
3637
+ }
3638
+ };
3639
+ const sanitizeIdeTools = (payload) => {
3640
+ if (!payload.tools || payload.tools.length === 0) return;
3641
+ payload.tools = payload.tools.flatMap((tool) => {
3642
+ if (tool.name === IDE_EXECUTE_CODE_TOOL && !tool.defer_loading) return [];
3643
+ if (tool.name === IDE_GET_DIAGNOSTICS_TOOL) return [{
3644
+ ...tool,
3645
+ description: IDE_GET_DIAGNOSTICS_DESCRIPTION
3646
+ }];
3647
+ return [tool];
3697
3648
  });
3698
- }
3699
-
3700
- //#endregion
3701
- //#region src/routes/provider/messages/handler.ts
3702
- const logger$3 = createHandlerLogger("provider-messages-handler");
3703
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
3704
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
3705
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
3706
- "system",
3707
- "user",
3708
- "assistant",
3709
- "tool"
3710
- ]);
3711
- async function handleProviderMessages(c) {
3712
- const provider = c.req.param("provider");
3713
- const providerConfig = getProviderConfig(provider);
3714
- if (!providerConfig) return c.json({ error: {
3715
- message: `Provider '${provider}' not found or disabled`,
3716
- type: "invalid_request_error"
3717
- } }, 404);
3718
- try {
3719
- const payload = await c.req.json();
3720
- const modelConfig = providerConfig.models?.[payload.model];
3721
- applyModelDefaults(payload, modelConfig);
3722
- debugJson(logger$3, "provider.messages.request", {
3723
- payload,
3724
- provider
3725
- });
3726
- if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
3727
- modelConfig,
3728
- payload,
3729
- provider,
3730
- providerConfig
3731
- });
3732
- applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
3733
- const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
3734
- if (!upstreamResponse.ok) {
3735
- logger$3.error("Failed to create responses", upstreamResponse);
3736
- throw new HTTPError("Failed to create responses", upstreamResponse);
3649
+ };
3650
+ const hasToolRef = (block) => {
3651
+ return Array.isArray(block.content) && block.content.some((c) => c.type === "tool_reference");
3652
+ };
3653
+ const stripCacheControl = (payload) => {
3654
+ if (Array.isArray(payload.system)) for (const block of payload.system) {
3655
+ const systemBlock = block;
3656
+ const cacheControl = systemBlock.cache_control;
3657
+ if (cacheControl && typeof cacheControl === "object") {
3658
+ const { scope,...rest } = cacheControl;
3659
+ systemBlock.cache_control = rest;
3737
3660
  }
3738
- const contentType = upstreamResponse.headers.get("content-type") ?? "";
3739
- if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
3740
- c,
3741
- payload,
3742
- provider,
3743
- providerConfig,
3744
- upstreamResponse
3745
- });
3746
- const jsonBody = await upstreamResponse.json();
3747
- return respondProviderMessagesJson(c, {
3748
- body: jsonBody,
3749
- payload,
3750
- provider,
3751
- providerConfig
3752
- });
3753
- } catch (error) {
3754
- logger$3.error("provider.messages.error", {
3755
- provider,
3756
- error
3757
- });
3758
- throw error;
3759
3661
  }
3760
- }
3761
- const applyModelDefaults = (payload, modelConfig) => {
3762
- payload.temperature ??= modelConfig?.temperature;
3763
- payload.top_p ??= modelConfig?.topP;
3764
- payload.top_k ??= modelConfig?.topK;
3765
- };
3766
- const applyMissingExtraBody = (payload, options) => {
3767
- for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
3768
3662
  };
3769
- const handleOpenAICompatibleProviderMessages = async (c, options) => {
3770
- const { modelConfig, payload, provider, providerConfig } = options;
3771
- const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
3772
- debugJson(logger$3, "provider.messages.openai_compatible.request", {
3773
- payload: openAIPayload,
3774
- provider
3663
+ const filterAssistantThinkingBlocks = (payload) => {
3664
+ for (const msg of payload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
3665
+ if (block.type !== "thinking") return true;
3666
+ return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
3667
+ });
3668
+ };
3669
+ const prepareMessagesApiPayload = (payload, selectedModel) => {
3670
+ stripCacheControl(payload);
3671
+ filterAssistantThinkingBlocks(payload);
3672
+ const hasThinking = Boolean(payload.thinking);
3673
+ const toolChoice = payload.tool_choice;
3674
+ const disableThink = toolChoice?.type === "any" || toolChoice?.type === "tool";
3675
+ if (selectedModel?.capabilities.supports.adaptive_thinking && !disableThink) {
3676
+ payload.thinking = { type: "adaptive" };
3677
+ if (!hasThinking) payload.thinking.display = "summarized";
3678
+ if (payload.model === "claude-opus-4.7") payload.thinking.display = "summarized";
3679
+ let effort = getReasoningEffortForModel(payload.model);
3680
+ if (effort === "none" || effort === "minimal") effort = "low";
3681
+ const reasoningEffort = selectedModel.capabilities.supports.reasoning_effort;
3682
+ if (reasoningEffort && !reasoningEffort.includes(effort)) effort = reasoningEffort.at(-1);
3683
+ payload.output_config = { effort };
3684
+ }
3685
+ };
3686
+
3687
+ //#endregion
3688
+ //#region src/routes/messages/api-flows.ts
3689
+ const COPILOT_CONTEXT_CACHE_SYSTEM_MARKER_LIMIT = 2;
3690
+ const COPILOT_CONTEXT_CACHE_NON_SYSTEM_MARKER_LIMIT = 2;
3691
+ const COPILOT_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
3692
+ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
3693
+ const { logger: logger$7, subagentMarker, requestId, sessionId, compactType } = options;
3694
+ const openAIPayload = translateToOpenAI(anthropicPayload);
3695
+ prepareCopilotChatCompletionsPayload(openAIPayload);
3696
+ const recordUsage = createCopilotUsageRecorder({
3697
+ endpoint: "chat_completions",
3698
+ fallbackSessionId: sessionId,
3699
+ model: openAIPayload.model,
3700
+ payload: anthropicPayload
3775
3701
  });
3776
- const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
3777
- if (!upstreamResponse.ok) {
3778
- logger$3.error("Failed to create openai-compatible responses", upstreamResponse);
3779
- throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
3780
- }
3781
- const contentType = upstreamResponse.headers.get("content-type") ?? "";
3782
- if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
3783
- c,
3784
- payload,
3785
- provider,
3786
- upstreamResponse
3702
+ debugJson(logger$7, "Translated OpenAI request payload:", openAIPayload);
3703
+ const response = await createChatCompletions(openAIPayload, {
3704
+ subagentMarker,
3705
+ requestId,
3706
+ sessionId,
3707
+ compactType
3787
3708
  });
3788
- const jsonBody = await upstreamResponse.json();
3789
- return respondOpenAICompatibleProviderMessagesJson(c, {
3790
- body: jsonBody,
3791
- payload,
3792
- provider
3709
+ if (isNonStreaming(response)) {
3710
+ debugJson(logger$7, "Non-streaming response from Copilot:", response);
3711
+ recordUsage(normalizeOpenAIUsage(response.usage));
3712
+ const anthropicResponse = translateToAnthropic(response);
3713
+ debugJson(logger$7, "Translated Anthropic response:", anthropicResponse);
3714
+ return c.json(anthropicResponse);
3715
+ }
3716
+ logger$7.debug("Streaming response from Copilot");
3717
+ return streamSSE(c, async (stream) => {
3718
+ let usage = {};
3719
+ const streamState = {
3720
+ messageStartSent: false,
3721
+ contentBlockIndex: 0,
3722
+ contentBlockOpen: false,
3723
+ toolCalls: {},
3724
+ thinkingBlockOpen: false
3725
+ };
3726
+ for await (const rawEvent of response) {
3727
+ debugJson(logger$7, "Copilot raw stream event:", rawEvent);
3728
+ if (rawEvent.data === "[DONE]") break;
3729
+ if (!rawEvent.data) continue;
3730
+ const chunk = JSON.parse(rawEvent.data);
3731
+ if (chunk.usage) usage = normalizeOpenAIUsage(chunk.usage);
3732
+ const events$1 = translateChunkToAnthropicEvents(chunk, streamState);
3733
+ for (const event of events$1) {
3734
+ const eventData = JSON.stringify(event);
3735
+ debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
3736
+ await stream.writeSSE({
3737
+ event: event.type,
3738
+ data: eventData
3739
+ });
3740
+ }
3741
+ }
3742
+ for (const event of flushPendingAnthropicStreamEvents(streamState)) {
3743
+ const eventData = JSON.stringify(event);
3744
+ debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
3745
+ await stream.writeSSE({
3746
+ event: event.type,
3747
+ data: eventData
3748
+ });
3749
+ }
3750
+ recordUsage(usage);
3793
3751
  });
3794
3752
  };
3795
- const createOpenAICompatiblePayload = (payload, modelConfig) => {
3796
- const openAIPayload = translateToOpenAI(payload, {
3797
- supportPdf: modelConfig?.supportPdf,
3798
- toolContentSupportType: modelConfig?.toolContentSupportType ?? []
3753
+ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
3754
+ const { logger: logger$7, selectedModel,...requestOptions } = options;
3755
+ const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
3756
+ const recordUsage = createCopilotUsageRecorder({
3757
+ endpoint: "responses",
3758
+ fallbackSessionId: requestOptions.sessionId,
3759
+ model: responsesPayload.model,
3760
+ payload: anthropicPayload
3799
3761
  });
3800
- if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
3801
- if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
3802
- normalizeOpenAICompatibleReasoningContent(openAIPayload);
3803
- applyOpenAICompatibleRequestOverrides(openAIPayload, {
3804
- extraBody: modelConfig?.extraBody,
3805
- source: payload
3762
+ applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
3763
+ compactInputByLatestCompaction(responsesPayload);
3764
+ debugJson(logger$7, "Translated Responses payload:", responsesPayload);
3765
+ const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
3766
+ const response = await createResponses(responsesPayload, {
3767
+ vision,
3768
+ initiator,
3769
+ ...requestOptions
3806
3770
  });
3807
- applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
3808
- if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
3809
- if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
3810
- return openAIPayload;
3771
+ if (responsesPayload.stream && isAsyncIterable$1(response)) {
3772
+ logger$7.debug("Streaming response from Copilot (Responses API)");
3773
+ return streamSSE(c, async (stream) => {
3774
+ const streamState = createResponsesStreamState();
3775
+ let usage = {};
3776
+ for await (const chunk of response) {
3777
+ if (chunk.event === "ping") {
3778
+ await stream.writeSSE({
3779
+ event: "ping",
3780
+ data: "{\"type\":\"ping\"}"
3781
+ });
3782
+ continue;
3783
+ }
3784
+ const data = chunk.data;
3785
+ if (!data) continue;
3786
+ debugLazy(logger$7, () => ["Responses raw stream event:", data]);
3787
+ const responseEvent = JSON.parse(data);
3788
+ if (responseEvent.type === "response.completed" || responseEvent.type === "response.failed" || responseEvent.type === "response.incomplete") usage = normalizeResponsesUsage(responseEvent.response.usage);
3789
+ const events$1 = translateResponsesStreamEvent(responseEvent, streamState);
3790
+ for (const event of events$1) {
3791
+ const eventData = JSON.stringify(event);
3792
+ debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
3793
+ await stream.writeSSE({
3794
+ event: event.type,
3795
+ data: eventData
3796
+ });
3797
+ }
3798
+ if (streamState.messageCompleted) {
3799
+ logger$7.debug("Message completed, ending stream");
3800
+ break;
3801
+ }
3802
+ }
3803
+ if (!streamState.messageCompleted) {
3804
+ logger$7.warn("Responses stream ended without completion; sending error event");
3805
+ const errorEvent = buildErrorEvent("Responses stream ended without completion");
3806
+ await stream.writeSSE({
3807
+ event: errorEvent.type,
3808
+ data: JSON.stringify(errorEvent)
3809
+ });
3810
+ }
3811
+ recordUsage(usage);
3812
+ });
3813
+ }
3814
+ debugJsonTail(logger$7, "Non-streaming Responses result:", {
3815
+ value: response,
3816
+ tailLength: 400
3817
+ });
3818
+ const anthropicResponse = translateResponsesResultToAnthropic(response);
3819
+ recordUsage(normalizeResponsesUsage(response.usage));
3820
+ debugJson(logger$7, "Translated Anthropic response:", anthropicResponse);
3821
+ return c.json(anthropicResponse);
3811
3822
  };
3812
- const normalizeOpenAICompatibleReasoningContent = (payload) => {
3813
- for (const message of payload.messages) {
3814
- if (message.role !== "assistant") continue;
3815
- if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
3816
- delete message.reasoning_text;
3817
- delete message.reasoning_opaque;
3823
+ const handleWithMessagesApi = async (c, anthropicPayload, options) => {
3824
+ const { logger: logger$7, anthropicBetaHeader, subagentMarker, selectedModel, requestId, sessionId, compactType } = options;
3825
+ prepareMessagesApiPayload(anthropicPayload, selectedModel);
3826
+ const recordUsage = createCopilotUsageRecorder({
3827
+ endpoint: "messages",
3828
+ fallbackSessionId: sessionId,
3829
+ model: anthropicPayload.model,
3830
+ payload: anthropicPayload
3831
+ });
3832
+ debugJson(logger$7, "Translated Messages payload:", anthropicPayload);
3833
+ const response = await createMessages(anthropicPayload, anthropicBetaHeader, {
3834
+ subagentMarker,
3835
+ requestId,
3836
+ sessionId,
3837
+ compactType
3838
+ });
3839
+ if (isAsyncIterable$1(response)) {
3840
+ logger$7.debug("Streaming response from Copilot (Messages API)");
3841
+ return streamSSE(c, async (stream) => {
3842
+ let usage = {};
3843
+ for await (const event of response) {
3844
+ const eventName = event.event;
3845
+ const data = event.data ?? "";
3846
+ if (data === "[DONE]") break;
3847
+ if (!data) continue;
3848
+ debugLazy(logger$7, () => ["Messages raw stream event:", data]);
3849
+ const parsedEvent = parseAnthropicStreamEvent(data);
3850
+ if (parsedEvent?.type === "message_start") usage = mergeAnthropicUsage(usage, normalizeAnthropicUsage(parsedEvent.message.usage));
3851
+ else if (parsedEvent?.type === "message_delta") usage = mergeAnthropicUsage(usage, normalizeAnthropicUsage(parsedEvent.usage));
3852
+ await stream.writeSSE({
3853
+ event: eventName,
3854
+ data
3855
+ });
3856
+ }
3857
+ recordUsage(usage);
3858
+ });
3818
3859
  }
3860
+ debugJsonTail(logger$7, "Non-streaming Messages result:", {
3861
+ value: response,
3862
+ tailLength: 400
3863
+ });
3864
+ recordUsage(normalizeAnthropicUsage(response.usage));
3865
+ return c.json(response);
3819
3866
  };
3820
- const applyOpenAICompatibleRequestOverrides = (payload, options) => {
3821
- const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
3822
- for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
3867
+ const prepareCopilotChatCompletionsPayload = (payload) => {
3868
+ applyCopilotContextCache(payload);
3823
3869
  };
3824
- const applyOpenAICompatibleContextCache = (payload) => {
3825
- const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
3826
- for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
3870
+ const applyCopilotContextCache = (payload) => {
3871
+ const messageIndexes = selectCopilotContextCacheMessageIndexes(payload.messages);
3872
+ for (const messageIndex of messageIndexes) {
3873
+ const message = payload.messages[messageIndex];
3874
+ message.copilot_cache_control = { ...COPILOT_CONTEXT_CACHE_CONTROL };
3875
+ }
3827
3876
  };
3828
- const selectContextCacheMessageIndexes = (messages) => {
3829
- const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
3830
- const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
3831
- const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
3832
- return uniqueIndexes([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
3877
+ const selectCopilotContextCacheMessageIndexes = (messages) => {
3878
+ const systemIndexes = messages.flatMap((message, index) => message.role === "system" && isCopilotContextCacheEligible(message) ? [index] : []).slice(0, COPILOT_CONTEXT_CACHE_SYSTEM_MARKER_LIMIT);
3879
+ const reverseNonSystemIndexes = messages.flatMap((message, index) => message.role !== "system" && isCopilotContextCacheEligible(message) ? [index] : []).reverse().slice(0, COPILOT_CONTEXT_CACHE_NON_SYSTEM_MARKER_LIMIT);
3880
+ return uniqueIndexes([...systemIndexes, ...reverseNonSystemIndexes]).sort((a, b) => a - b);
3833
3881
  };
3834
- const uniqueIndexes = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
3835
- const isContextCacheMarkerEligible = (message) => {
3836
- if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
3882
+ const isCopilotContextCacheEligible = (message) => {
3837
3883
  if (typeof message.content === "string") return message.content.length > 0;
3838
3884
  return Array.isArray(message.content) && message.content.length > 0;
3839
3885
  };
3840
- const applyContextCacheControl = (message) => {
3841
- if (!message) return;
3842
- if (typeof message.content === "string") {
3843
- message.content = [{
3844
- type: "text",
3845
- text: message.content,
3846
- cache_control: { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL }
3847
- }];
3848
- return;
3886
+ const uniqueIndexes = (indexes) => [...new Set(indexes)];
3887
+ const isNonStreaming = (response) => Object.hasOwn(response, "choices");
3888
+ const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
3889
+ const createCopilotUsageRecorder = (options) => createCopilotTokenUsageRecorder({
3890
+ endpoint: options.endpoint,
3891
+ fallbackSessionId: options.fallbackSessionId,
3892
+ model: options.model,
3893
+ sessionId: getMetadataSessionId(options.payload)
3894
+ });
3895
+ const getMetadataSessionId = (payload) => parseUserIdMetadata(payload.metadata?.user_id).sessionId;
3896
+ const parseAnthropicStreamEvent = (data) => {
3897
+ try {
3898
+ return JSON.parse(data);
3899
+ } catch {
3900
+ return null;
3849
3901
  }
3850
- if (!Array.isArray(message.content)) return;
3851
- const lastPart = message.content.at(-1);
3852
- if (!lastPart) return;
3853
- setContextCacheControl(lastPart);
3854
3902
  };
3855
- const setContextCacheControl = (part) => {
3856
- part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
3903
+
3904
+ //#endregion
3905
+ //#region src/lib/subagent.ts
3906
+ const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
3907
+
3908
+ //#endregion
3909
+ //#region src/routes/messages/subagent-marker.ts
3910
+ const parseSubagentMarkerFromFirstUser = (payload) => {
3911
+ const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
3912
+ if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
3913
+ for (const block of firstUserMessage.content) {
3914
+ if (block.type !== "text") continue;
3915
+ const marker = parseSubagentMarkerFromSystemReminder(block.text);
3916
+ if (marker) return marker;
3917
+ }
3918
+ return null;
3857
3919
  };
3858
- const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
3859
- logger$3.debug("provider.messages.streaming");
3860
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
3861
- return streamSSE(c, async (stream) => {
3862
- let usage = {};
3863
- for await (const chunk of events(upstreamResponse)) {
3864
- logger$3.debug("provider.messages.raw_stream_event:", chunk.data);
3865
- const eventName = chunk.event;
3866
- if (eventName === "ping") {
3867
- await stream.writeSSE({
3868
- event: "ping",
3869
- data: "{\"type\":\"ping\"}"
3870
- });
3871
- continue;
3872
- }
3873
- let data = chunk.data;
3874
- if (!data) continue;
3875
- if (chunk.data === "[DONE]") break;
3876
- const parsed = parseProviderStreamEvent(data, providerConfig);
3877
- if (parsed) {
3878
- usage = mergeAnthropicUsage(usage, parsed.usage);
3879
- data = parsed.data;
3880
- }
3881
- await stream.writeSSE({
3882
- event: eventName,
3883
- data
3884
- });
3920
+ const parseSubagentMarkerFromSystemReminder = (text) => {
3921
+ const startTag = "<system-reminder>";
3922
+ const endTag = "</system-reminder>";
3923
+ let searchFrom = 0;
3924
+ while (true) {
3925
+ const reminderStart = text.indexOf(startTag, searchFrom);
3926
+ if (reminderStart === -1) break;
3927
+ const contentStart = reminderStart + 17;
3928
+ const reminderEnd = text.indexOf(endTag, contentStart);
3929
+ if (reminderEnd === -1) break;
3930
+ const reminderContent = text.slice(contentStart, reminderEnd);
3931
+ const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
3932
+ if (markerIndex === -1) {
3933
+ searchFrom = reminderEnd + 18;
3934
+ continue;
3885
3935
  }
3886
- recordUsage(usage);
3887
- });
3888
- };
3889
- const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
3890
- logger$3.debug("provider.messages.openai_compatible.streaming");
3891
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
3892
- return streamSSE(c, async (stream) => {
3893
- let usage = {};
3894
- const streamState = {
3895
- messageStartSent: false,
3896
- contentBlockIndex: 0,
3897
- contentBlockOpen: false,
3898
- toolCalls: {},
3899
- thinkingBlockOpen: false
3900
- };
3901
- for await (const chunk of events(upstreamResponse)) {
3902
- logger$3.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
3903
- if (chunk.event === "ping") {
3904
- await stream.writeSSE({
3905
- event: "ping",
3906
- data: "{\"type\":\"ping\"}"
3907
- });
3908
- continue;
3909
- }
3910
- if (!chunk.data || chunk.data === "[DONE]") {
3911
- if (chunk.data === "[DONE]") break;
3936
+ const markerJson = reminderContent.slice(markerIndex + subagentMarkerPrefix.length).trim();
3937
+ try {
3938
+ const parsed = JSON.parse(markerJson);
3939
+ if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
3940
+ searchFrom = reminderEnd + 18;
3912
3941
  continue;
3913
3942
  }
3914
- const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
3915
- if (!parsed) continue;
3916
- if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
3917
- const events$1 = translateChunkToAnthropicEvents(parsed, streamState);
3918
- for (const event of events$1) {
3919
- const eventData = JSON.stringify(event);
3920
- debugLazy(logger$3, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
3921
- await stream.writeSSE({
3922
- event: event.type,
3923
- data: eventData
3924
- });
3925
- }
3926
- }
3927
- for (const event of flushPendingAnthropicStreamEvents(streamState)) {
3928
- const eventData = JSON.stringify(event);
3929
- debugLazy(logger$3, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
3930
- await stream.writeSSE({
3931
- event: event.type,
3932
- data: eventData
3933
- });
3943
+ return parsed;
3944
+ } catch {
3945
+ searchFrom = reminderEnd + 18;
3946
+ continue;
3934
3947
  }
3935
- recordUsage(usage);
3948
+ }
3949
+ return null;
3950
+ };
3951
+
3952
+ //#endregion
3953
+ //#region src/routes/messages/handler.ts
3954
+ const logger$3 = createHandlerLogger("messages-handler");
3955
+ const messagesFlowHandlers = {
3956
+ handleWithChatCompletions,
3957
+ handleWithMessagesApi,
3958
+ handleWithResponsesApi
3959
+ };
3960
+ async function handleCompletion(c) {
3961
+ const anthropicPayload = await c.req.json();
3962
+ const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
3963
+ if (providerModelAlias) {
3964
+ anthropicPayload.model = providerModelAlias.model;
3965
+ return await handleProviderMessagesForProvider(c, {
3966
+ payload: anthropicPayload,
3967
+ provider: providerModelAlias.provider
3968
+ });
3969
+ }
3970
+ await checkRateLimit(state);
3971
+ debugJson(logger$3, "Anthropic request payload:", anthropicPayload);
3972
+ sanitizeIdeTools(anthropicPayload);
3973
+ const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
3974
+ if (subagentMarker) debugJson(logger$3, "Detected Subagent marker:", subagentMarker);
3975
+ const sessionId = getRootSessionId(anthropicPayload, c);
3976
+ logger$3.debug("Extracted session ID:", sessionId);
3977
+ const compactType = getCompactType(anthropicPayload);
3978
+ const anthropicBeta = c.req.header("anthropic-beta");
3979
+ logger$3.debug("Anthropic Beta header:", anthropicBeta);
3980
+ const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
3981
+ if (anthropicBeta && noTools && compactType === 0) anthropicPayload.model = getSmallModel();
3982
+ if (compactType) logger$3.debug("Compact request type:", compactType);
3983
+ stripToolReferenceTurnBoundary(anthropicPayload);
3984
+ mergeToolResultForClaude(anthropicPayload, { skipLastMessage: compactType === COMPACT_REQUEST });
3985
+ const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
3986
+ logger$3.debug("Generated request ID:", requestId);
3987
+ if (state.manualApprove) await awaitApproval();
3988
+ const selectedModel = findEndpointModel(anthropicPayload.model);
3989
+ anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
3990
+ if (shouldUseMessagesApi(selectedModel)) return await messagesFlowHandlers.handleWithMessagesApi(c, anthropicPayload, {
3991
+ anthropicBetaHeader: anthropicBeta,
3992
+ subagentMarker,
3993
+ selectedModel,
3994
+ requestId,
3995
+ sessionId,
3996
+ compactType,
3997
+ logger: logger$3
3998
+ });
3999
+ if (shouldUseResponsesApi(selectedModel)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
4000
+ subagentMarker,
4001
+ selectedModel,
4002
+ requestId,
4003
+ sessionId,
4004
+ compactType,
4005
+ logger: logger$3
4006
+ });
4007
+ return await messagesFlowHandlers.handleWithChatCompletions(c, anthropicPayload, {
4008
+ subagentMarker,
4009
+ requestId,
4010
+ sessionId,
4011
+ compactType,
4012
+ logger: logger$3
3936
4013
  });
4014
+ }
4015
+ const RESPONSES_ENDPOINT$1 = "/responses";
4016
+ const MESSAGES_ENDPOINT = "/v1/messages";
4017
+ const shouldUseResponsesApi = (selectedModel) => {
4018
+ return selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
3937
4019
  };
3938
- const parseOpenAICompatibleStreamChunk = (data) => {
4020
+ const shouldUseMessagesApi = (selectedModel) => {
4021
+ if (!isMessagesApiEnabled()) return false;
4022
+ return selectedModel?.supported_endpoints?.includes(MESSAGES_ENDPOINT) ?? false;
4023
+ };
4024
+
4025
+ //#endregion
4026
+ //#region src/routes/messages/route.ts
4027
+ const messageRoutes = new Hono();
4028
+ messageRoutes.post("/", async (c) => {
3939
4029
  try {
3940
- return JSON.parse(data);
4030
+ return await handleCompletion(c);
3941
4031
  } catch (error) {
3942
- logger$3.error("provider.messages.openai_compatible.parse_chunk_error", {
3943
- data,
3944
- error
3945
- });
3946
- return null;
4032
+ return await forwardError(c, error);
3947
4033
  }
3948
- };
3949
- const parseProviderStreamEvent = (data, providerConfig) => {
4034
+ });
4035
+ messageRoutes.post("/count_tokens", async (c) => {
3950
4036
  try {
3951
- const parsed = JSON.parse(data);
3952
- if (parsed.type === "message_start") {
3953
- adjustInputTokens(providerConfig, parsed.message.usage);
3954
- return {
3955
- data: JSON.stringify(parsed),
3956
- model: parsed.message.model,
3957
- usage: normalizeAnthropicUsage(parsed.message.usage)
3958
- };
3959
- }
3960
- if (parsed.type === "message_delta") {
3961
- adjustInputTokens(providerConfig, parsed.usage);
4037
+ return await handleCountTokens(c);
4038
+ } catch (error) {
4039
+ return await forwardError(c, error);
4040
+ }
4041
+ });
4042
+
4043
+ //#endregion
4044
+ //#region src/routes/models/route.ts
4045
+ const modelRoutes = new Hono();
4046
+ modelRoutes.get("/", async (c) => {
4047
+ try {
4048
+ if (!state.models) await cacheModels();
4049
+ const models = state.models?.data.map((model) => {
4050
+ const is1m = model.capabilities.limits?.max_context_window_tokens === 1e6;
3962
4051
  return {
3963
- data: JSON.stringify(parsed),
3964
- usage: normalizeAnthropicUsage(parsed.usage)
4052
+ ...model,
4053
+ id: is1m ? `${model.id}[1m]` : model.id,
4054
+ object: "model",
4055
+ type: "model",
4056
+ created: 0,
4057
+ created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
4058
+ owned_by: model.vendor,
4059
+ display_name: model.name
3965
4060
  };
3966
- }
3967
- return {
3968
- data: JSON.stringify(parsed),
3969
- usage: {}
3970
- };
3971
- } catch (error) {
3972
- logger$3.error("provider.messages.streaming.adjust_tokens_error", {
3973
- error,
3974
- originalData: data
3975
4061
  });
3976
- return null;
4062
+ return c.json({
4063
+ object: "list",
4064
+ data: models,
4065
+ has_more: false
4066
+ });
4067
+ } catch (error) {
4068
+ return await forwardError(c, error);
3977
4069
  }
3978
- };
3979
- const respondProviderMessagesJson = (c, options) => {
3980
- const { body, payload, provider, providerConfig } = options;
3981
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
3982
- adjustInputTokens(providerConfig, body.usage);
3983
- recordUsage(normalizeAnthropicUsage(body.usage));
3984
- debugJson(logger$3, "provider.messages.no_stream result:", body);
3985
- return c.json(body);
3986
- };
3987
- const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
3988
- const { body, payload, provider } = options;
3989
- createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
3990
- const anthropicResponse = translateToAnthropic(body);
3991
- debugJson(logger$3, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
3992
- return c.json(anthropicResponse);
3993
- };
3994
- const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
3995
- endpoint: "provider_messages",
3996
- model: payload.model,
3997
- providerName: provider,
3998
- sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
3999
4070
  });
4000
- const adjustInputTokens = (providerConfig, usage) => {
4001
- if (!providerConfig.adjustInputTokens || !usage) return;
4002
- usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
4003
- debugJson(logger$3, "provider.messages.adjusted_usage:", usage);
4004
- };
4005
4071
 
4006
4072
  //#endregion
4007
4073
  //#region src/routes/provider/messages/route.ts
@@ -4310,4 +4376,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
4310
4376
 
4311
4377
  //#endregion
4312
4378
  export { server };
4313
- //# sourceMappingURL=server-D4FT8suK.js.map
4379
+ //# sourceMappingURL=server-D4pg54e1.js.map