@jeffreycao/copilot-api 1.9.10 → 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.
- package/README.md +4 -4
- package/README.zh-CN.md +4 -4
- package/dist/main.js +1 -1
- package/dist/{server-BG69Fgym.js → server-D4pg54e1.js} +2228 -2191
- package/dist/server-D4pg54e1.js.map +1 -0
- package/dist/{start-CpqH2Ekm.js → start-D2K2jpHF.js} +2 -2
- package/dist/{start-CpqH2Ekm.js.map → start-D2K2jpHF.js.map} +1 -1
- package/package.json +1 -1
- package/dist/server-BG69Fgym.js.map +0 -1
|
@@ -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 ? [{
|
|
@@ -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,2262 +1740,1980 @@ 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
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
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
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
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/
|
|
1701
|
-
|
|
1702
|
-
if (!state.
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
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
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
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
|
-
|
|
1722
|
-
|
|
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"]
|
|
1757
|
-
};
|
|
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
|
|
1818
|
-
});
|
|
1819
|
-
items.push(compactionContent);
|
|
1820
|
-
continue;
|
|
1821
|
-
}
|
|
1822
|
-
if (block.signature.includes("@")) {
|
|
1823
|
-
flushPendingContent(pendingContent, items, {
|
|
1824
|
-
role: "assistant",
|
|
1825
|
-
phase: assistantPhase
|
|
1826
|
-
});
|
|
1827
|
-
items.push(createReasoningContent(block));
|
|
1828
|
-
continue;
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
const converted = translateAssistantContentBlock(block);
|
|
1832
|
-
if (converted) pendingContent.push(converted);
|
|
1833
|
-
}
|
|
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 [];
|
|
1846
|
-
}
|
|
1847
|
-
};
|
|
1848
|
-
const translateAssistantContentBlock = (block) => {
|
|
1849
|
-
switch (block.type) {
|
|
1850
|
-
case "text": return createOutPutTextContent(block.text);
|
|
1851
|
-
default: return;
|
|
1852
|
-
}
|
|
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;
|
|
1837
|
+
}
|
|
1838
|
+
function getAnthropicUsageFromOpenAIChunk(chunk) {
|
|
1839
|
+
const { cachedTokens, cacheCreationTokens, inputTokens } = getOpenAIChunkUsageTokens(chunk);
|
|
2079
1840
|
return {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
encrypted_content: item.encrypted_content
|
|
2085
|
-
})
|
|
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 }
|
|
2086
1845
|
};
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
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;
|
|
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;
|
|
2125
1851
|
return {
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
1852
|
+
cacheCreationTokens,
|
|
1853
|
+
cachedTokens,
|
|
1854
|
+
inputTokens: Math.max(0, promptTokens - cachedTokens - cacheCreationTokens)
|
|
2129
1855
|
};
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
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
|
+
}
|
|
1886
|
+
});
|
|
1887
|
+
state$1.contentBlockOpen = true;
|
|
1888
|
+
}
|
|
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
|
+
}
|
|
1898
|
+
});
|
|
1899
|
+
}
|
|
2152
1900
|
}
|
|
2153
|
-
return result;
|
|
2154
1901
|
}
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
this.name = "FunctionCallArgumentsValidationError";
|
|
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;
|
|
2165
1911
|
}
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
};
|
|
2176
|
-
|
|
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;
|
|
2177
1924
|
}
|
|
2178
|
-
if (
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
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 [];
|
|
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
|
+
});
|
|
2210
1944
|
}
|
|
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:
|
|
1948
|
+
index: state$1.contentBlockIndex,
|
|
2230
1949
|
delta: {
|
|
2231
|
-
type: "
|
|
2232
|
-
|
|
1950
|
+
type: "signature_delta",
|
|
1951
|
+
signature: delta.reasoning_opaque
|
|
1952
|
+
}
|
|
1953
|
+
}, {
|
|
1954
|
+
type: "content_block_stop",
|
|
1955
|
+
index: state$1.contentBlockIndex
|
|
1956
|
+
});
|
|
1957
|
+
state$1.contentBlockIndex++;
|
|
1958
|
+
state$1.thinkingBlockOpen = false;
|
|
1959
|
+
}
|
|
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
|
+
}
|
|
2233
1980
|
}
|
|
2234
1981
|
});
|
|
2235
|
-
state$1.
|
|
1982
|
+
state$1.messageStartSent = true;
|
|
2236
1983
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
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:
|
|
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:
|
|
2003
|
+
index: state$1.contentBlockIndex,
|
|
2258
2004
|
delta: {
|
|
2259
2005
|
type: "signature_delta",
|
|
2260
|
-
signature:
|
|
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.
|
|
2267
|
-
return events$1;
|
|
2012
|
+
state$1.contentBlockIndex++;
|
|
2268
2013
|
}
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
const
|
|
2272
|
-
if (
|
|
2273
|
-
if (
|
|
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:
|
|
2037
|
+
index: state$1.contentBlockIndex,
|
|
2276
2038
|
delta: {
|
|
2277
2039
|
type: "thinking_delta",
|
|
2278
|
-
thinking:
|
|
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:
|
|
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.
|
|
2058
|
+
state$1.contentBlockIndex++;
|
|
2059
|
+
state$1.thinkingBlockOpen = false;
|
|
2290
2060
|
}
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
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
|
|
2301
2106
|
});
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
type: "content_block_delta",
|
|
2309
|
-
index: blockIndex,
|
|
2310
|
-
delta: {
|
|
2311
|
-
type: "input_json_delta",
|
|
2312
|
-
partial_json: deltaText
|
|
2313
|
-
}
|
|
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)
|
|
2314
2113
|
});
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
}
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
const blockIndex = openFunctionCallBlock(state$1, {
|
|
2322
|
-
outputIndex,
|
|
2323
|
-
events: events$1
|
|
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)
|
|
2324
2120
|
});
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
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
|
-
};
|
|
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
|
|
2121
|
+
}
|
|
2122
|
+
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
2123
|
+
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
2124
|
+
method: "GET",
|
|
2125
|
+
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
2350
2126
|
});
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
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
|
|
2358
2146
|
});
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
};
|
|
2362
|
-
const
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
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);
|
|
2373
2173
|
}
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
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;
|
|
2377
2201
|
};
|
|
2378
|
-
const
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
const
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2202
|
+
const applyMissingExtraBody = (payload, options) => {
|
|
2203
|
+
for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
|
|
2204
|
+
};
|
|
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
|
|
2211
|
+
});
|
|
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
|
|
2223
|
+
});
|
|
2224
|
+
const jsonBody = await upstreamResponse.json();
|
|
2225
|
+
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
2226
|
+
body: jsonBody,
|
|
2227
|
+
payload,
|
|
2228
|
+
provider
|
|
2390
2229
|
});
|
|
2391
|
-
return events$1;
|
|
2392
2230
|
};
|
|
2393
|
-
const
|
|
2394
|
-
const
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
const text = rawEvent.text;
|
|
2398
|
-
const blockIndex = openTextBlockIfNeeded(state$1, {
|
|
2399
|
-
outputIndex,
|
|
2400
|
-
contentIndex,
|
|
2401
|
-
events: events$1
|
|
2231
|
+
const createOpenAICompatiblePayload = (payload, modelConfig) => {
|
|
2232
|
+
const openAIPayload = translateToOpenAI(payload, {
|
|
2233
|
+
supportPdf: modelConfig?.supportPdf,
|
|
2234
|
+
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
2402
2235
|
});
|
|
2403
|
-
if (
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
}
|
|
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
|
|
2410
2242
|
});
|
|
2411
|
-
|
|
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;
|
|
2412
2247
|
};
|
|
2413
|
-
const
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
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;
|
|
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
|
+
}
|
|
2428
2255
|
};
|
|
2429
|
-
const
|
|
2430
|
-
const
|
|
2431
|
-
const
|
|
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;
|
|
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];
|
|
2437
2259
|
};
|
|
2438
|
-
const
|
|
2439
|
-
const
|
|
2440
|
-
|
|
2441
|
-
return [buildErrorEvent(message)];
|
|
2260
|
+
const applyOpenAICompatibleContextCache = (payload) => {
|
|
2261
|
+
const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
|
|
2262
|
+
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
2442
2263
|
};
|
|
2443
|
-
const
|
|
2444
|
-
const
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
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);
|
|
2449
2269
|
};
|
|
2450
|
-
const
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
return
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
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;
|
|
2275
|
+
};
|
|
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);
|
|
2290
|
+
};
|
|
2291
|
+
const setContextCacheControl = (part) => {
|
|
2292
|
+
part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
|
|
2293
|
+
};
|
|
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
|
|
2473
|
-
|
|
2474
|
-
const
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
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;
|
|
2345
|
+
}
|
|
2346
|
+
if (!chunk.data || chunk.data === "[DONE]") {
|
|
2347
|
+
if (chunk.data === "[DONE]") break;
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
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
|
+
});
|
|
2489
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);
|
|
2372
|
+
});
|
|
2373
|
+
};
|
|
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
|
|
2490
2381
|
});
|
|
2491
|
-
|
|
2382
|
+
return null;
|
|
2492
2383
|
}
|
|
2493
|
-
return blockIndex;
|
|
2494
2384
|
};
|
|
2495
|
-
const
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
}
|
|
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: {}
|
|
2406
|
+
};
|
|
2407
|
+
} catch (error) {
|
|
2408
|
+
logger$4.error("provider.messages.streaming.adjust_tokens_error", {
|
|
2409
|
+
error,
|
|
2410
|
+
originalData: data
|
|
2512
2411
|
});
|
|
2513
|
-
|
|
2412
|
+
return null;
|
|
2514
2413
|
}
|
|
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
|
|
2522
|
-
});
|
|
2523
|
-
state$1.openBlocks.delete(blockIndex);
|
|
2524
|
-
state$1.blockHasDelta.delete(blockIndex);
|
|
2525
2414
|
};
|
|
2526
|
-
const
|
|
2527
|
-
|
|
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);
|
|
2528
2422
|
};
|
|
2529
|
-
const
|
|
2530
|
-
|
|
2531
|
-
|
|
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);
|
|
2532
2429
|
};
|
|
2533
|
-
const
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
}
|
|
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
|
|
2539
2435
|
});
|
|
2540
|
-
const
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
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
|
|
2553
|
-
};
|
|
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
|
-
}
|
|
2568
|
-
});
|
|
2569
|
-
state$1.openBlocks.add(blockIndex);
|
|
2570
|
-
}
|
|
2571
|
-
return blockIndex;
|
|
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);
|
|
2572
2440
|
};
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
const
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
outputIndex,
|
|
2582
|
-
toolCallId,
|
|
2583
|
-
name,
|
|
2584
|
-
initialArguments
|
|
2441
|
+
|
|
2442
|
+
//#endregion
|
|
2443
|
+
//#region src/services/copilot/create-responses.ts
|
|
2444
|
+
const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType }) => {
|
|
2445
|
+
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
2446
|
+
const headers = {
|
|
2447
|
+
...copilotHeaders(state, requestId, vision),
|
|
2448
|
+
"x-initiator": initiator
|
|
2585
2449
|
};
|
|
2450
|
+
prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
|
|
2451
|
+
prepareForCompact(headers, compactType);
|
|
2452
|
+
payload.service_tier = void 0;
|
|
2453
|
+
consola.log(`<-- model: ${payload.model}`);
|
|
2454
|
+
const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
|
|
2455
|
+
method: "POST",
|
|
2456
|
+
headers,
|
|
2457
|
+
body: JSON.stringify(payload)
|
|
2458
|
+
});
|
|
2459
|
+
logCopilotRateLimits(response.headers);
|
|
2460
|
+
if (!response.ok) {
|
|
2461
|
+
consola.error("Failed to create responses", response);
|
|
2462
|
+
throw new HTTPError("Failed to create responses", response);
|
|
2463
|
+
}
|
|
2464
|
+
if (payload.stream) return events(response);
|
|
2465
|
+
return await response.json();
|
|
2586
2466
|
};
|
|
2587
2467
|
|
|
2588
2468
|
//#endregion
|
|
2589
|
-
//#region src/routes/responses
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
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);
|
|
2593
2481
|
return {
|
|
2594
|
-
|
|
2595
|
-
|
|
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"]
|
|
2596
2500
|
};
|
|
2597
2501
|
};
|
|
2598
|
-
const
|
|
2599
|
-
|
|
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);
|
|
2502
|
+
const encodeCompactionCarrierSignature = (compaction) => {
|
|
2503
|
+
return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
|
|
2625
2504
|
};
|
|
2626
|
-
const
|
|
2627
|
-
|
|
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
|
+
}
|
|
2628
2518
|
};
|
|
2629
|
-
const
|
|
2630
|
-
|
|
2519
|
+
const translateMessage = (message, model, applyPhase) => {
|
|
2520
|
+
if (message.role === "user") return translateUserMessage(message);
|
|
2521
|
+
return translateAssistantMessage(message, model, applyPhase);
|
|
2631
2522
|
};
|
|
2632
|
-
const
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
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;
|
|
2637
2539
|
};
|
|
2638
|
-
const
|
|
2639
|
-
|
|
2640
|
-
if (
|
|
2641
|
-
if (
|
|
2642
|
-
const
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
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;
|
|
2646
2582
|
};
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
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;
|
|
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 [];
|
|
2662
2589
|
}
|
|
2663
|
-
if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
|
|
2664
2590
|
};
|
|
2665
|
-
const
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
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
|
-
const headers = {
|
|
2675
|
-
...copilotHeaders(state, options.requestId, enableVision),
|
|
2676
|
-
"x-initiator": isInitiateRequest ? "user" : "agent"
|
|
2677
|
-
};
|
|
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;
|
|
2684
|
-
consola.log(`<-- model: ${payload.model}`);
|
|
2685
|
-
const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {
|
|
2686
|
-
method: "POST",
|
|
2687
|
-
headers,
|
|
2688
|
-
body: JSON.stringify(payload)
|
|
2689
|
-
});
|
|
2690
|
-
logCopilotRateLimits(response.headers);
|
|
2691
|
-
if (!response.ok) {
|
|
2692
|
-
consola.error("Failed to create messages", response);
|
|
2693
|
-
throw new HTTPError("Failed to create messages", response);
|
|
2591
|
+
const translateAssistantContentBlock = (block) => {
|
|
2592
|
+
switch (block.type) {
|
|
2593
|
+
case "text": return createOutPutTextContent(block.text);
|
|
2594
|
+
default: return;
|
|
2694
2595
|
}
|
|
2695
|
-
if (payload.stream) return events(response);
|
|
2696
|
-
return await response.json();
|
|
2697
2596
|
};
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
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");
|
|
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;
|
|
2710
2602
|
};
|
|
2711
|
-
const
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
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";
|
|
2715
2615
|
};
|
|
2716
|
-
const
|
|
2717
|
-
|
|
2718
|
-
return Boolean(text) && compactAutoContinuePromptStarts.some((promptStart) => text.startsWith(promptStart));
|
|
2616
|
+
const shouldApplyPhase = (model) => {
|
|
2617
|
+
return getExtraPromptForModel(model).includes("## Intermediary updates");
|
|
2719
2618
|
};
|
|
2720
|
-
const
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
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;
|
|
2640
|
+
return {
|
|
2641
|
+
id,
|
|
2642
|
+
type: "reasoning",
|
|
2643
|
+
summary: thinking ? [{
|
|
2644
|
+
type: "summary_text",
|
|
2645
|
+
text: thinking
|
|
2646
|
+
}] : [],
|
|
2647
|
+
encrypted_content: encryptedContent
|
|
2648
|
+
};
|
|
2729
2649
|
};
|
|
2730
|
-
const
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2650
|
+
const createCompactionContent = (block) => {
|
|
2651
|
+
const compaction = decodeCompactionCarrierSignature(block.signature);
|
|
2652
|
+
if (!compaction) return;
|
|
2653
|
+
return {
|
|
2654
|
+
id: compaction.id,
|
|
2655
|
+
type: "compaction",
|
|
2656
|
+
encrypted_content: compaction.encrypted_content
|
|
2657
|
+
};
|
|
2658
|
+
};
|
|
2659
|
+
const parseReasoningSignature = (signature) => {
|
|
2660
|
+
const splitIndex = signature.lastIndexOf("@");
|
|
2661
|
+
if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
|
|
2662
|
+
encryptedContent: signature,
|
|
2663
|
+
id: ""
|
|
2734
2664
|
};
|
|
2735
|
-
if (hasToolRef(tr)) return tr;
|
|
2736
2665
|
return {
|
|
2737
|
-
|
|
2738
|
-
|
|
2666
|
+
encryptedContent: signature.slice(0, splitIndex),
|
|
2667
|
+
id: signature.slice(splitIndex + 1)
|
|
2739
2668
|
};
|
|
2740
2669
|
};
|
|
2741
|
-
const
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
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;
|
|
2692
|
+
};
|
|
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
|
+
}));
|
|
2702
|
+
};
|
|
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";
|
|
2748
2714
|
}
|
|
2749
|
-
if (hasToolRef(tr)) return tr;
|
|
2750
|
-
return {
|
|
2751
|
-
...tr,
|
|
2752
|
-
content: [...tr.content, ...textBlocks]
|
|
2753
|
-
};
|
|
2754
2715
|
};
|
|
2755
|
-
const
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
}, ...attachments]
|
|
2762
|
-
};
|
|
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);
|
|
2763
2722
|
return {
|
|
2764
|
-
|
|
2765
|
-
|
|
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
|
|
2766
2731
|
};
|
|
2767
2732
|
};
|
|
2768
|
-
const
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
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;
|
|
2744
|
+
}
|
|
2745
|
+
case "function_call": {
|
|
2746
|
+
const toolUseBlock = createToolUseContentBlock(item);
|
|
2747
|
+
if (toolUseBlock) contentBlocks.push(toolUseBlock);
|
|
2748
|
+
break;
|
|
2749
|
+
}
|
|
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
|
|
2768
|
+
});
|
|
2795
2769
|
}
|
|
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;
|
|
2804
2770
|
}
|
|
2805
|
-
|
|
2806
|
-
};
|
|
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);
|
|
2771
|
+
return contentBlocks;
|
|
2814
2772
|
};
|
|
2815
|
-
const
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
toolResults.push(block);
|
|
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;
|
|
2822
2779
|
continue;
|
|
2823
2780
|
}
|
|
2824
|
-
if (block
|
|
2825
|
-
|
|
2781
|
+
if (isResponseOutputRefusal(block)) {
|
|
2782
|
+
aggregated += block.refusal;
|
|
2826
2783
|
continue;
|
|
2827
2784
|
}
|
|
2828
|
-
if (
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2785
|
+
if (typeof block.text === "string") {
|
|
2786
|
+
aggregated += block.text;
|
|
2787
|
+
continue;
|
|
2788
|
+
}
|
|
2789
|
+
if (typeof block.reasoning === "string") {
|
|
2790
|
+
aggregated += block.reasoning;
|
|
2833
2791
|
continue;
|
|
2834
2792
|
}
|
|
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
2793
|
}
|
|
2861
|
-
|
|
2862
|
-
toolResultIndices: countMatchToolResultIndices,
|
|
2863
|
-
fallbackToolResultIndices: mergeableToolResultIndices
|
|
2864
|
-
});
|
|
2865
|
-
return mergeAttachmentsIntoToolResults(toolResults, attachmentsByToolResultIndex);
|
|
2794
|
+
return aggregated;
|
|
2866
2795
|
};
|
|
2867
|
-
const
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
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;
|
|
2803
|
+
}
|
|
2804
|
+
};
|
|
2805
|
+
if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
|
|
2806
|
+
collectFromBlocks(item.summary);
|
|
2807
|
+
return segments.join("").trim();
|
|
2874
2808
|
};
|
|
2875
|
-
const
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
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
|
+
};
|
|
2879
2819
|
};
|
|
2880
|
-
const
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
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
|
+
};
|
|
2886
2830
|
};
|
|
2887
|
-
const
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
if (
|
|
2892
|
-
|
|
2893
|
-
|
|
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
|
+
});
|
|
2894
2842
|
}
|
|
2843
|
+
return { raw_arguments: rawArguments };
|
|
2895
2844
|
};
|
|
2896
|
-
const
|
|
2897
|
-
if (!
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
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");
|
|
2845
|
+
const fallbackContentBlocks = (outputText) => {
|
|
2846
|
+
if (!outputText) return [];
|
|
2847
|
+
return [{
|
|
2848
|
+
type: "text",
|
|
2849
|
+
text: outputText
|
|
2850
|
+
}];
|
|
2909
2851
|
};
|
|
2910
|
-
const
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
const { scope,...rest } = cacheControl;
|
|
2916
|
-
systemBlock.cache_control = rest;
|
|
2917
|
-
}
|
|
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";
|
|
2918
2857
|
}
|
|
2858
|
+
if (status === "incomplete") {
|
|
2859
|
+
if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
|
|
2860
|
+
if (incompleteDetails?.reason === "content_filter") return "end_turn";
|
|
2861
|
+
}
|
|
2862
|
+
return null;
|
|
2919
2863
|
};
|
|
2920
|
-
const
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
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;
|
|
2868
|
+
return {
|
|
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 }
|
|
2872
|
+
};
|
|
2925
2873
|
};
|
|
2926
|
-
const
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
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;
|
|
2895
|
+
}
|
|
2896
|
+
return result;
|
|
2941
2897
|
}
|
|
2898
|
+
return "";
|
|
2942
2899
|
};
|
|
2943
2900
|
|
|
2944
2901
|
//#endregion
|
|
2945
|
-
//#region src/routes/messages/stream-translation.ts
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
const events$1 = [];
|
|
2952
|
-
if (chunk.choices.length === 0) {
|
|
2953
|
-
completePendingMessage(state$1, events$1, chunk);
|
|
2954
|
-
return events$1;
|
|
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";
|
|
2955
2908
|
}
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
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);
|
|
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;
|
|
2991
2920
|
}
|
|
2992
|
-
|
|
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);
|
|
2921
|
+
if (char !== " ") count = 0;
|
|
3001
2922
|
}
|
|
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;
|
|
3016
2923
|
return {
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
inputTokens: Math.max(0, promptTokens - cachedTokens - cacheCreationTokens)
|
|
2924
|
+
nextCount: count,
|
|
2925
|
+
exceeded: false
|
|
3020
2926
|
};
|
|
3021
|
-
}
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
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) {
|
|
2970
|
+
events$1.push({
|
|
2971
|
+
type: "content_block_delta",
|
|
2972
|
+
index: blockIndex,
|
|
2973
|
+
delta: {
|
|
2974
|
+
type: "input_json_delta",
|
|
2975
|
+
partial_json: initialArguments
|
|
2976
|
+
}
|
|
2977
|
+
});
|
|
2978
|
+
state$1.blockHasDelta.add(blockIndex);
|
|
2979
|
+
}
|
|
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({
|
|
2991
|
+
type: "content_block_delta",
|
|
2992
|
+
index: blockIndex$1,
|
|
2993
|
+
delta: {
|
|
2994
|
+
type: "thinking_delta",
|
|
2995
|
+
thinking: THINKING_TEXT
|
|
3053
2996
|
}
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
})
|
|
2997
|
+
});
|
|
2998
|
+
events$1.push({
|
|
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
|
+
})
|
|
3064
3007
|
}
|
|
3065
|
-
}
|
|
3008
|
+
});
|
|
3009
|
+
state$1.blockHasDelta.add(blockIndex$1);
|
|
3010
|
+
return events$1;
|
|
3066
3011
|
}
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
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({
|
|
3017
|
+
type: "content_block_delta",
|
|
3018
|
+
index: blockIndex,
|
|
3019
|
+
delta: {
|
|
3020
|
+
type: "thinking_delta",
|
|
3021
|
+
thinking: THINKING_TEXT
|
|
3022
|
+
}
|
|
3023
|
+
});
|
|
3070
3024
|
events$1.push({
|
|
3071
|
-
type: "
|
|
3072
|
-
index:
|
|
3025
|
+
type: "content_block_delta",
|
|
3026
|
+
index: blockIndex,
|
|
3027
|
+
delta: {
|
|
3028
|
+
type: "signature_delta",
|
|
3029
|
+
signature
|
|
3030
|
+
}
|
|
3073
3031
|
});
|
|
3074
|
-
state$1.
|
|
3075
|
-
state$1.contentBlockOpen = false;
|
|
3032
|
+
state$1.blockHasDelta.add(blockIndex);
|
|
3076
3033
|
}
|
|
3077
|
-
|
|
3078
|
-
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
state$1.contentBlockOpen = true;
|
|
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
|
|
3100
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) {
|
|
3101
3070
|
events$1.push({
|
|
3102
3071
|
type: "content_block_delta",
|
|
3103
|
-
index:
|
|
3072
|
+
index: blockIndex,
|
|
3104
3073
|
delta: {
|
|
3105
|
-
type: "
|
|
3106
|
-
|
|
3074
|
+
type: "input_json_delta",
|
|
3075
|
+
partial_json: finalArguments
|
|
3076
|
+
}
|
|
3077
|
+
});
|
|
3078
|
+
state$1.blockHasDelta.add(blockIndex);
|
|
3079
|
+
}
|
|
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
|
|
3093
|
+
});
|
|
3094
|
+
events$1.push({
|
|
3095
|
+
type: "content_block_delta",
|
|
3096
|
+
index: blockIndex,
|
|
3097
|
+
delta: {
|
|
3098
|
+
type: "text_delta",
|
|
3099
|
+
text: deltaText
|
|
3100
|
+
}
|
|
3101
|
+
});
|
|
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
|
|
3116
|
+
}
|
|
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
|
|
3132
|
+
}
|
|
3133
|
+
});
|
|
3134
|
+
return events$1;
|
|
3135
|
+
};
|
|
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
|
|
3145
|
+
});
|
|
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
|
+
}
|
|
3153
|
+
});
|
|
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
|
|
3107
3211
|
}
|
|
3108
|
-
}
|
|
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);
|
|
3109
3223
|
}
|
|
3110
|
-
if (
|
|
3224
|
+
if (!state$1.openBlocks.has(blockIndex)) {
|
|
3225
|
+
closeOpenBlocks(state$1, events$1);
|
|
3111
3226
|
events$1.push({
|
|
3112
|
-
type: "
|
|
3113
|
-
index:
|
|
3114
|
-
|
|
3115
|
-
type: "
|
|
3116
|
-
|
|
3227
|
+
type: "content_block_start",
|
|
3228
|
+
index: blockIndex,
|
|
3229
|
+
content_block: {
|
|
3230
|
+
type: "text",
|
|
3231
|
+
text: ""
|
|
3117
3232
|
}
|
|
3118
|
-
}, {
|
|
3119
|
-
type: "content_block_stop",
|
|
3120
|
-
index: state$1.contentBlockIndex
|
|
3121
3233
|
});
|
|
3122
|
-
state$1.
|
|
3123
|
-
state$1.thinkingBlockOpen = false;
|
|
3234
|
+
state$1.openBlocks.add(blockIndex);
|
|
3124
3235
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
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
|
-
}
|
|
3145
|
-
}
|
|
3146
|
-
});
|
|
3147
|
-
state$1.messageStartSent = true;
|
|
3236
|
+
return blockIndex;
|
|
3237
|
+
};
|
|
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);
|
|
3148
3245
|
}
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
if (delta.reasoning_opaque && delta.reasoning_opaque.length > 0) {
|
|
3246
|
+
if (!state$1.openBlocks.has(blockIndex)) {
|
|
3247
|
+
closeOpenBlocks(state$1, events$1);
|
|
3152
3248
|
events$1.push({
|
|
3153
3249
|
type: "content_block_start",
|
|
3154
|
-
index:
|
|
3250
|
+
index: blockIndex,
|
|
3155
3251
|
content_block: {
|
|
3156
3252
|
type: "thinking",
|
|
3157
3253
|
thinking: ""
|
|
3158
3254
|
}
|
|
3159
|
-
}, {
|
|
3160
|
-
type: "content_block_delta",
|
|
3161
|
-
index: state$1.contentBlockIndex,
|
|
3162
|
-
delta: {
|
|
3163
|
-
type: "thinking_delta",
|
|
3164
|
-
thinking: THINKING_TEXT
|
|
3165
|
-
}
|
|
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
3255
|
});
|
|
3177
|
-
state$1.
|
|
3256
|
+
state$1.openBlocks.add(blockIndex);
|
|
3178
3257
|
}
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
if (
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
index: state$1.contentBlockIndex,
|
|
3203
|
-
delta: {
|
|
3204
|
-
type: "thinking_delta",
|
|
3205
|
-
thinking: reasoningText
|
|
3206
|
-
}
|
|
3207
|
-
});
|
|
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
|
|
3265
|
+
});
|
|
3266
|
+
state$1.openBlocks.delete(blockIndex);
|
|
3267
|
+
state$1.blockHasDelta.delete(blockIndex);
|
|
3268
|
+
};
|
|
3269
|
+
const closeOpenBlocks = (state$1, events$1) => {
|
|
3270
|
+
for (const blockIndex of state$1.openBlocks) closeBlockIfOpen(state$1, blockIndex, events$1);
|
|
3271
|
+
};
|
|
3272
|
+
const closeAllOpenBlocks = (state$1, events$1) => {
|
|
3273
|
+
closeOpenBlocks(state$1, events$1);
|
|
3274
|
+
state$1.functionCallStateByOutputIndex.clear();
|
|
3275
|
+
};
|
|
3276
|
+
const buildErrorEvent = (message) => ({
|
|
3277
|
+
type: "error",
|
|
3278
|
+
error: {
|
|
3279
|
+
type: "api_error",
|
|
3280
|
+
message
|
|
3208
3281
|
}
|
|
3209
|
-
}
|
|
3210
|
-
|
|
3211
|
-
|
|
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);
|
|
3212
3302
|
events$1.push({
|
|
3213
|
-
type: "
|
|
3214
|
-
index:
|
|
3215
|
-
|
|
3216
|
-
type: "
|
|
3217
|
-
|
|
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: {}
|
|
3218
3310
|
}
|
|
3219
|
-
}, {
|
|
3220
|
-
type: "content_block_stop",
|
|
3221
|
-
index: state$1.contentBlockIndex
|
|
3222
3311
|
});
|
|
3223
|
-
state$1.
|
|
3224
|
-
state$1.thinkingBlockOpen = false;
|
|
3312
|
+
state$1.openBlocks.add(blockIndex);
|
|
3225
3313
|
}
|
|
3226
|
-
|
|
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
|
+
};
|
|
3329
|
+
};
|
|
3330
|
+
|
|
3331
|
+
//#endregion
|
|
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";
|
|
3346
|
+
};
|
|
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;
|
|
3389
|
+
};
|
|
3227
3390
|
|
|
3228
3391
|
//#endregion
|
|
3229
|
-
//#region src/
|
|
3230
|
-
const
|
|
3231
|
-
const
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
const
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
});
|
|
3243
|
-
debugJson(logger$7, "Translated OpenAI request payload:", openAIPayload);
|
|
3244
|
-
const response = await createChatCompletions(openAIPayload, {
|
|
3245
|
-
subagentMarker,
|
|
3246
|
-
requestId,
|
|
3247
|
-
sessionId,
|
|
3248
|
-
compactType
|
|
3249
|
-
});
|
|
3250
|
-
if (isNonStreaming(response)) {
|
|
3251
|
-
debugJson(logger$7, "Non-streaming response from Copilot:", response);
|
|
3252
|
-
recordUsage(normalizeOpenAIUsage(response.usage));
|
|
3253
|
-
const anthropicResponse = translateToAnthropic(response);
|
|
3254
|
-
debugJson(logger$7, "Translated Anthropic response:", anthropicResponse);
|
|
3255
|
-
return c.json(anthropicResponse);
|
|
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;
|
|
3256
3405
|
}
|
|
3257
|
-
|
|
3258
|
-
return streamSSE(c, async (stream) => {
|
|
3259
|
-
let usage = {};
|
|
3260
|
-
const streamState = {
|
|
3261
|
-
messageStartSent: false,
|
|
3262
|
-
contentBlockIndex: 0,
|
|
3263
|
-
contentBlockOpen: false,
|
|
3264
|
-
toolCalls: {},
|
|
3265
|
-
thinkingBlockOpen: false
|
|
3266
|
-
};
|
|
3267
|
-
for await (const rawEvent of response) {
|
|
3268
|
-
debugJson(logger$7, "Copilot raw stream event:", rawEvent);
|
|
3269
|
-
if (rawEvent.data === "[DONE]") break;
|
|
3270
|
-
if (!rawEvent.data) continue;
|
|
3271
|
-
const chunk = JSON.parse(rawEvent.data);
|
|
3272
|
-
if (chunk.usage) usage = normalizeOpenAIUsage(chunk.usage);
|
|
3273
|
-
const events$1 = translateChunkToAnthropicEvents(chunk, streamState);
|
|
3274
|
-
for (const event of events$1) {
|
|
3275
|
-
const eventData = JSON.stringify(event);
|
|
3276
|
-
debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
|
|
3277
|
-
await stream.writeSSE({
|
|
3278
|
-
event: event.type,
|
|
3279
|
-
data: eventData
|
|
3280
|
-
});
|
|
3281
|
-
}
|
|
3282
|
-
}
|
|
3283
|
-
for (const event of flushPendingAnthropicStreamEvents(streamState)) {
|
|
3284
|
-
const eventData = JSON.stringify(event);
|
|
3285
|
-
debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
|
|
3286
|
-
await stream.writeSSE({
|
|
3287
|
-
event: event.type,
|
|
3288
|
-
data: eventData
|
|
3289
|
-
});
|
|
3290
|
-
}
|
|
3291
|
-
recordUsage(usage);
|
|
3292
|
-
});
|
|
3406
|
+
if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
|
|
3293
3407
|
};
|
|
3294
|
-
const
|
|
3295
|
-
|
|
3296
|
-
const
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
fallbackSessionId: requestOptions.sessionId,
|
|
3300
|
-
model: responsesPayload.model,
|
|
3301
|
-
payload: anthropicPayload
|
|
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"));
|
|
3302
3413
|
});
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
const
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
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)
|
|
3311
3432
|
});
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
let usage = {};
|
|
3317
|
-
for await (const chunk of response) {
|
|
3318
|
-
if (chunk.event === "ping") {
|
|
3319
|
-
await stream.writeSSE({
|
|
3320
|
-
event: "ping",
|
|
3321
|
-
data: "{\"type\":\"ping\"}"
|
|
3322
|
-
});
|
|
3323
|
-
continue;
|
|
3324
|
-
}
|
|
3325
|
-
const data = chunk.data;
|
|
3326
|
-
if (!data) continue;
|
|
3327
|
-
debugLazy(logger$7, () => ["Responses raw stream event:", data]);
|
|
3328
|
-
const responseEvent = JSON.parse(data);
|
|
3329
|
-
if (responseEvent.type === "response.completed" || responseEvent.type === "response.failed" || responseEvent.type === "response.incomplete") usage = normalizeResponsesUsage(responseEvent.response.usage);
|
|
3330
|
-
const events$1 = translateResponsesStreamEvent(responseEvent, streamState);
|
|
3331
|
-
for (const event of events$1) {
|
|
3332
|
-
const eventData = JSON.stringify(event);
|
|
3333
|
-
debugLazy(logger$7, () => ["Translated Anthropic event:", eventData]);
|
|
3334
|
-
await stream.writeSSE({
|
|
3335
|
-
event: event.type,
|
|
3336
|
-
data: eventData
|
|
3337
|
-
});
|
|
3338
|
-
}
|
|
3339
|
-
if (streamState.messageCompleted) {
|
|
3340
|
-
logger$7.debug("Message completed, ending stream");
|
|
3341
|
-
break;
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
if (!streamState.messageCompleted) {
|
|
3345
|
-
logger$7.warn("Responses stream ended without completion; sending error event");
|
|
3346
|
-
const errorEvent = buildErrorEvent("Responses stream ended without completion");
|
|
3347
|
-
await stream.writeSSE({
|
|
3348
|
-
event: errorEvent.type,
|
|
3349
|
-
data: JSON.stringify(errorEvent)
|
|
3350
|
-
});
|
|
3351
|
-
}
|
|
3352
|
-
recordUsage(usage);
|
|
3353
|
-
});
|
|
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);
|
|
3354
3437
|
}
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
tailLength: 400
|
|
3358
|
-
});
|
|
3359
|
-
const anthropicResponse = translateResponsesResultToAnthropic(response);
|
|
3360
|
-
recordUsage(normalizeResponsesUsage(response.usage));
|
|
3361
|
-
debugJson(logger$7, "Translated Anthropic response:", anthropicResponse);
|
|
3362
|
-
return c.json(anthropicResponse);
|
|
3438
|
+
if (payload.stream) return events(response);
|
|
3439
|
+
return await response.json();
|
|
3363
3440
|
};
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
requestId,
|
|
3377
|
-
sessionId,
|
|
3378
|
-
compactType
|
|
3379
|
-
});
|
|
3380
|
-
if (isAsyncIterable$1(response)) {
|
|
3381
|
-
logger$7.debug("Streaming response from Copilot (Messages API)");
|
|
3382
|
-
return streamSSE(c, async (stream) => {
|
|
3383
|
-
let usage = {};
|
|
3384
|
-
for await (const event of response) {
|
|
3385
|
-
const eventName = event.event;
|
|
3386
|
-
const data = event.data ?? "";
|
|
3387
|
-
if (data === "[DONE]") break;
|
|
3388
|
-
if (!data) continue;
|
|
3389
|
-
debugLazy(logger$7, () => ["Messages raw stream event:", data]);
|
|
3390
|
-
const parsedEvent = parseAnthropicStreamEvent(data);
|
|
3391
|
-
if (parsedEvent?.type === "message_start") usage = mergeAnthropicUsage(usage, normalizeAnthropicUsage(parsedEvent.message.usage));
|
|
3392
|
-
else if (parsedEvent?.type === "message_delta") usage = mergeAnthropicUsage(usage, normalizeAnthropicUsage(parsedEvent.usage));
|
|
3393
|
-
await stream.writeSSE({
|
|
3394
|
-
event: eventName,
|
|
3395
|
-
data
|
|
3396
|
-
});
|
|
3397
|
-
}
|
|
3398
|
-
recordUsage(usage);
|
|
3399
|
-
});
|
|
3400
|
-
}
|
|
3401
|
-
debugJsonTail(logger$7, "Non-streaming Messages result:", {
|
|
3402
|
-
value: response,
|
|
3403
|
-
tailLength: 400
|
|
3404
|
-
});
|
|
3405
|
-
recordUsage(normalizeAnthropicUsage(response.usage));
|
|
3406
|
-
return c.json(response);
|
|
3441
|
+
|
|
3442
|
+
//#endregion
|
|
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");
|
|
3407
3453
|
};
|
|
3408
|
-
const
|
|
3409
|
-
|
|
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
|
+
};
|
|
3410
3483
|
};
|
|
3411
|
-
const
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
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
|
+
};
|
|
3416
3491
|
}
|
|
3492
|
+
if (hasToolRef(tr)) return tr;
|
|
3493
|
+
return {
|
|
3494
|
+
...tr,
|
|
3495
|
+
content: [...tr.content, ...textBlocks]
|
|
3496
|
+
};
|
|
3417
3497
|
};
|
|
3418
|
-
const
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
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
|
+
};
|
|
3422
3510
|
};
|
|
3423
|
-
const
|
|
3424
|
-
|
|
3425
|
-
return Array.isArray(message.content) && message.content.length > 0;
|
|
3511
|
+
const isAttachmentBlock = (block) => {
|
|
3512
|
+
return block.type === "image" || block.type === "document";
|
|
3426
3513
|
};
|
|
3427
|
-
const
|
|
3428
|
-
|
|
3429
|
-
const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
3430
|
-
const createCopilotUsageRecorder = (options) => createCopilotTokenUsageRecorder({
|
|
3431
|
-
endpoint: options.endpoint,
|
|
3432
|
-
fallbackSessionId: options.fallbackSessionId,
|
|
3433
|
-
model: options.model,
|
|
3434
|
-
sessionId: getMetadataSessionId(options.payload)
|
|
3435
|
-
});
|
|
3436
|
-
const getMetadataSessionId = (payload) => parseUserIdMetadata(payload.metadata?.user_id).sessionId;
|
|
3437
|
-
const parseAnthropicStreamEvent = (data) => {
|
|
3438
|
-
try {
|
|
3439
|
-
return JSON.parse(data);
|
|
3440
|
-
} catch {
|
|
3441
|
-
return null;
|
|
3442
|
-
}
|
|
3514
|
+
const getMergeableToolResultIndices = (toolResults) => {
|
|
3515
|
+
return toolResults.flatMap((block, index) => block.is_error || hasToolRef(block) ? [] : [index]);
|
|
3443
3516
|
};
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
const
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
|
|
3453
|
-
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
|
|
3454
|
-
for (const block of firstUserMessage.content) {
|
|
3455
|
-
if (block.type !== "text") continue;
|
|
3456
|
-
const marker = parseSubagentMarkerFromSystemReminder(block.text);
|
|
3457
|
-
if (marker) return marker;
|
|
3458
|
-
}
|
|
3459
|
-
return null;
|
|
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
|
+
});
|
|
3460
3525
|
};
|
|
3461
|
-
const
|
|
3462
|
-
const
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
if (reminderEnd === -1) break;
|
|
3471
|
-
const reminderContent = text.slice(contentStart, reminderEnd);
|
|
3472
|
-
const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
|
|
3473
|
-
if (markerIndex === -1) {
|
|
3474
|
-
searchFrom = reminderEnd + 18;
|
|
3475
|
-
continue;
|
|
3476
|
-
}
|
|
3477
|
-
const markerJson = reminderContent.slice(markerIndex + subagentMarkerPrefix.length).trim();
|
|
3478
|
-
try {
|
|
3479
|
-
const parsed = JSON.parse(markerJson);
|
|
3480
|
-
if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
|
|
3481
|
-
searchFrom = reminderEnd + 18;
|
|
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]);
|
|
3482
3535
|
continue;
|
|
3483
3536
|
}
|
|
3484
|
-
|
|
3485
|
-
} catch {
|
|
3486
|
-
searchFrom = reminderEnd + 18;
|
|
3487
|
-
continue;
|
|
3537
|
+
target.set(toolResultIndex, [attachments[index]]);
|
|
3488
3538
|
}
|
|
3539
|
+
return;
|
|
3489
3540
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
const messagesFlowHandlers = {
|
|
3497
|
-
handleWithChatCompletions,
|
|
3498
|
-
handleWithMessagesApi,
|
|
3499
|
-
handleWithResponsesApi
|
|
3500
|
-
};
|
|
3501
|
-
async function handleCompletion(c) {
|
|
3502
|
-
await checkRateLimit(state);
|
|
3503
|
-
const anthropicPayload = await c.req.json();
|
|
3504
|
-
debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
|
|
3505
|
-
sanitizeIdeTools(anthropicPayload);
|
|
3506
|
-
const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
|
|
3507
|
-
if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
|
|
3508
|
-
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
3509
|
-
logger$5.debug("Extracted session ID:", sessionId);
|
|
3510
|
-
const compactType = getCompactType(anthropicPayload);
|
|
3511
|
-
const anthropicBeta = c.req.header("anthropic-beta");
|
|
3512
|
-
logger$5.debug("Anthropic Beta header:", anthropicBeta);
|
|
3513
|
-
const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
|
|
3514
|
-
if (anthropicBeta && noTools && compactType === 0) anthropicPayload.model = getSmallModel();
|
|
3515
|
-
if (compactType) logger$5.debug("Compact request type:", compactType);
|
|
3516
|
-
stripToolReferenceTurnBoundary(anthropicPayload);
|
|
3517
|
-
mergeToolResultForClaude(anthropicPayload, { skipLastMessage: compactType === COMPACT_REQUEST });
|
|
3518
|
-
const requestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
|
|
3519
|
-
logger$5.debug("Generated request ID:", requestId);
|
|
3520
|
-
if (state.manualApprove) await awaitApproval();
|
|
3521
|
-
const selectedModel = findEndpointModel(anthropicPayload.model);
|
|
3522
|
-
anthropicPayload.model = selectedModel?.id ?? anthropicPayload.model;
|
|
3523
|
-
if (shouldUseMessagesApi(selectedModel)) return await messagesFlowHandlers.handleWithMessagesApi(c, anthropicPayload, {
|
|
3524
|
-
anthropicBetaHeader: anthropicBeta,
|
|
3525
|
-
subagentMarker,
|
|
3526
|
-
selectedModel,
|
|
3527
|
-
requestId,
|
|
3528
|
-
sessionId,
|
|
3529
|
-
compactType,
|
|
3530
|
-
logger: logger$5
|
|
3531
|
-
});
|
|
3532
|
-
if (shouldUseResponsesApi(selectedModel)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
|
|
3533
|
-
subagentMarker,
|
|
3534
|
-
selectedModel,
|
|
3535
|
-
requestId,
|
|
3536
|
-
sessionId,
|
|
3537
|
-
compactType,
|
|
3538
|
-
logger: logger$5
|
|
3539
|
-
});
|
|
3540
|
-
return await messagesFlowHandlers.handleWithChatCompletions(c, anthropicPayload, {
|
|
3541
|
-
subagentMarker,
|
|
3542
|
-
requestId,
|
|
3543
|
-
sessionId,
|
|
3544
|
-
compactType,
|
|
3545
|
-
logger: logger$5
|
|
3546
|
-
});
|
|
3547
|
-
}
|
|
3548
|
-
const RESPONSES_ENDPOINT$1 = "/responses";
|
|
3549
|
-
const MESSAGES_ENDPOINT = "/v1/messages";
|
|
3550
|
-
const shouldUseResponsesApi = (selectedModel) => {
|
|
3551
|
-
return selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
|
|
3552
|
-
};
|
|
3553
|
-
const shouldUseMessagesApi = (selectedModel) => {
|
|
3554
|
-
if (!isMessagesApiEnabled()) return false;
|
|
3555
|
-
return selectedModel?.supported_endpoints?.includes(MESSAGES_ENDPOINT) ?? false;
|
|
3556
|
-
};
|
|
3557
|
-
|
|
3558
|
-
//#endregion
|
|
3559
|
-
//#region src/routes/messages/route.ts
|
|
3560
|
-
const messageRoutes = new Hono();
|
|
3561
|
-
messageRoutes.post("/", async (c) => {
|
|
3562
|
-
try {
|
|
3563
|
-
return await handleCompletion(c);
|
|
3564
|
-
} catch (error) {
|
|
3565
|
-
return await forwardError(c, error);
|
|
3566
|
-
}
|
|
3567
|
-
});
|
|
3568
|
-
messageRoutes.post("/count_tokens", async (c) => {
|
|
3569
|
-
try {
|
|
3570
|
-
return await handleCountTokens(c);
|
|
3571
|
-
} catch (error) {
|
|
3572
|
-
return await forwardError(c, error);
|
|
3573
|
-
}
|
|
3574
|
-
});
|
|
3575
|
-
|
|
3576
|
-
//#endregion
|
|
3577
|
-
//#region src/routes/models/route.ts
|
|
3578
|
-
const modelRoutes = new Hono();
|
|
3579
|
-
modelRoutes.get("/", async (c) => {
|
|
3580
|
-
try {
|
|
3581
|
-
if (!state.models) await cacheModels();
|
|
3582
|
-
const models = state.models?.data.map((model) => {
|
|
3583
|
-
const is1m = model.capabilities.limits?.max_context_window_tokens === 1e6;
|
|
3584
|
-
return {
|
|
3585
|
-
...model,
|
|
3586
|
-
id: is1m ? `${model.id}[1m]` : model.id,
|
|
3587
|
-
object: "model",
|
|
3588
|
-
type: "model",
|
|
3589
|
-
created: 0,
|
|
3590
|
-
created_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
3591
|
-
owned_by: model.vendor,
|
|
3592
|
-
display_name: model.name
|
|
3593
|
-
};
|
|
3594
|
-
});
|
|
3595
|
-
return c.json({
|
|
3596
|
-
object: "list",
|
|
3597
|
-
data: models,
|
|
3598
|
-
has_more: false
|
|
3599
|
-
});
|
|
3600
|
-
} catch (error) {
|
|
3601
|
-
return await forwardError(c, error);
|
|
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;
|
|
3602
3547
|
}
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
const providerConfig = getProviderConfig(provider);
|
|
3631
|
-
const modelConfig = providerConfig?.models?.[modelId];
|
|
3632
|
-
const translationOptions = providerConfig?.type === "openai-compatible" ? {
|
|
3633
|
-
supportPdf: modelConfig?.supportPdf,
|
|
3634
|
-
toolContentSupportType: modelConfig?.toolContentSupportType ?? []
|
|
3635
|
-
} : void 0;
|
|
3636
|
-
const openAIPayload = translateToOpenAI(anthropicPayload, translationOptions);
|
|
3637
|
-
let selectedModel = state.models?.data.find((model) => model.id === modelId);
|
|
3638
|
-
if (!selectedModel && modelId) selectedModel = createFallbackModel(modelId);
|
|
3639
|
-
if (!selectedModel) {
|
|
3640
|
-
logger$4.warn("provider.count_tokens.model_not_found", {
|
|
3641
|
-
provider,
|
|
3642
|
-
model: anthropicPayload.model
|
|
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
|
|
3643
3575
|
});
|
|
3644
|
-
|
|
3576
|
+
continue;
|
|
3645
3577
|
}
|
|
3646
|
-
|
|
3647
|
-
const finalTokenCount = tokenCount.input + tokenCount.output;
|
|
3648
|
-
logger$4.debug("provider.count_tokens.success", {
|
|
3649
|
-
provider,
|
|
3650
|
-
model: anthropicPayload.model,
|
|
3651
|
-
input_tokens: finalTokenCount
|
|
3652
|
-
});
|
|
3653
|
-
return c.json({ input_tokens: finalTokenCount });
|
|
3654
|
-
} catch (error) {
|
|
3655
|
-
logger$4.error("provider.count_tokens.error", {
|
|
3656
|
-
provider,
|
|
3657
|
-
error
|
|
3658
|
-
});
|
|
3659
|
-
return c.json({ input_tokens: 1 });
|
|
3578
|
+
return null;
|
|
3660
3579
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
|
|
3666
|
-
const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
|
|
3667
|
-
const STRIPPED_RESPONSE_HEADERS = [
|
|
3668
|
-
"connection",
|
|
3669
|
-
"content-encoding",
|
|
3670
|
-
"content-length",
|
|
3671
|
-
"keep-alive",
|
|
3672
|
-
"proxy-authenticate",
|
|
3673
|
-
"proxy-authorization",
|
|
3674
|
-
"te",
|
|
3675
|
-
"trailer",
|
|
3676
|
-
"transfer-encoding",
|
|
3677
|
-
"upgrade"
|
|
3678
|
-
];
|
|
3679
|
-
function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
|
|
3680
|
-
const authHeaders = {};
|
|
3681
|
-
if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
|
|
3682
|
-
else authHeaders["x-api-key"] = providerConfig.apiKey;
|
|
3683
|
-
const headers = {
|
|
3684
|
-
"content-type": "application/json",
|
|
3685
|
-
accept: "application/json",
|
|
3686
|
-
...authHeaders
|
|
3580
|
+
return {
|
|
3581
|
+
toolResults,
|
|
3582
|
+
textBlocks,
|
|
3583
|
+
attachments
|
|
3687
3584
|
};
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
}
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
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));
|
|
3696
3603
|
}
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
const headers = new Headers(upstreamResponse.headers);
|
|
3701
|
-
for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
|
|
3702
|
-
return new Response(upstreamResponse.body, {
|
|
3703
|
-
headers,
|
|
3704
|
-
status: upstreamResponse.status,
|
|
3705
|
-
statusText: upstreamResponse.statusText
|
|
3706
|
-
});
|
|
3707
|
-
}
|
|
3708
|
-
async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
|
|
3709
|
-
return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
|
|
3710
|
-
method: "POST",
|
|
3711
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3712
|
-
body: JSON.stringify(payload)
|
|
3713
|
-
});
|
|
3714
|
-
}
|
|
3715
|
-
async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
|
|
3716
|
-
return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
|
|
3717
|
-
method: "POST",
|
|
3718
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
|
|
3719
|
-
body: JSON.stringify(payload)
|
|
3720
|
-
});
|
|
3721
|
-
}
|
|
3722
|
-
async function forwardProviderModels(providerConfig, requestHeaders) {
|
|
3723
|
-
return await fetch(`${providerConfig.baseUrl}/v1/models`, {
|
|
3724
|
-
method: "GET",
|
|
3725
|
-
headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
|
|
3604
|
+
assignAttachmentsToToolResults(attachmentsByToolResultIndex, remainingAttachments, {
|
|
3605
|
+
toolResultIndices: countMatchToolResultIndices,
|
|
3606
|
+
fallbackToolResultIndices: mergeableToolResultIndices
|
|
3726
3607
|
});
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
//#endregion
|
|
3730
|
-
//#region src/routes/provider/messages/handler.ts
|
|
3731
|
-
const logger$3 = createHandlerLogger("provider-messages-handler");
|
|
3732
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
|
|
3733
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
|
|
3734
|
-
const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
|
|
3735
|
-
"system",
|
|
3736
|
-
"user",
|
|
3737
|
-
"assistant",
|
|
3738
|
-
"tool"
|
|
3739
|
-
]);
|
|
3740
|
-
async function handleProviderMessages(c) {
|
|
3741
|
-
const provider = c.req.param("provider");
|
|
3742
|
-
const providerConfig = getProviderConfig(provider);
|
|
3743
|
-
if (!providerConfig) return c.json({ error: {
|
|
3744
|
-
message: `Provider '${provider}' not found or disabled`,
|
|
3745
|
-
type: "invalid_request_error"
|
|
3746
|
-
} }, 404);
|
|
3747
|
-
try {
|
|
3748
|
-
const payload = await c.req.json();
|
|
3749
|
-
const modelConfig = providerConfig.models?.[payload.model];
|
|
3750
|
-
applyModelDefaults(payload, modelConfig);
|
|
3751
|
-
debugJson(logger$3, "provider.messages.request", {
|
|
3752
|
-
payload,
|
|
3753
|
-
provider
|
|
3754
|
-
});
|
|
3755
|
-
if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
|
|
3756
|
-
modelConfig,
|
|
3757
|
-
payload,
|
|
3758
|
-
provider,
|
|
3759
|
-
providerConfig
|
|
3760
|
-
});
|
|
3761
|
-
applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
|
|
3762
|
-
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
3763
|
-
if (!upstreamResponse.ok) {
|
|
3764
|
-
logger$3.error("Failed to create responses", upstreamResponse);
|
|
3765
|
-
throw new HTTPError("Failed to create responses", upstreamResponse);
|
|
3766
|
-
}
|
|
3767
|
-
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
3768
|
-
if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
|
|
3769
|
-
c,
|
|
3770
|
-
payload,
|
|
3771
|
-
provider,
|
|
3772
|
-
providerConfig,
|
|
3773
|
-
upstreamResponse
|
|
3774
|
-
});
|
|
3775
|
-
const jsonBody = await upstreamResponse.json();
|
|
3776
|
-
return respondProviderMessagesJson(c, {
|
|
3777
|
-
body: jsonBody,
|
|
3778
|
-
payload,
|
|
3779
|
-
provider,
|
|
3780
|
-
providerConfig
|
|
3781
|
-
});
|
|
3782
|
-
} catch (error) {
|
|
3783
|
-
logger$3.error("provider.messages.error", {
|
|
3784
|
-
provider,
|
|
3785
|
-
error
|
|
3786
|
-
});
|
|
3787
|
-
throw error;
|
|
3788
|
-
}
|
|
3789
|
-
}
|
|
3790
|
-
const applyModelDefaults = (payload, modelConfig) => {
|
|
3791
|
-
payload.temperature ??= modelConfig?.temperature;
|
|
3792
|
-
payload.top_p ??= modelConfig?.topP;
|
|
3793
|
-
payload.top_k ??= modelConfig?.topK;
|
|
3608
|
+
return mergeAttachmentsIntoToolResults(toolResults, attachmentsByToolResultIndex);
|
|
3794
3609
|
};
|
|
3795
|
-
const
|
|
3796
|
-
|
|
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);
|
|
3797
3617
|
};
|
|
3798
|
-
const
|
|
3799
|
-
|
|
3800
|
-
const
|
|
3801
|
-
|
|
3802
|
-
payload: openAIPayload,
|
|
3803
|
-
provider
|
|
3804
|
-
});
|
|
3805
|
-
const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
|
|
3806
|
-
if (!upstreamResponse.ok) {
|
|
3807
|
-
logger$3.error("Failed to create openai-compatible responses", upstreamResponse);
|
|
3808
|
-
throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
|
|
3809
|
-
}
|
|
3810
|
-
const contentType = upstreamResponse.headers.get("content-type") ?? "";
|
|
3811
|
-
if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
|
|
3812
|
-
c,
|
|
3813
|
-
payload,
|
|
3814
|
-
provider,
|
|
3815
|
-
upstreamResponse
|
|
3816
|
-
});
|
|
3817
|
-
const jsonBody = await upstreamResponse.json();
|
|
3818
|
-
return respondOpenAICompatibleProviderMessagesJson(c, {
|
|
3819
|
-
body: jsonBody,
|
|
3820
|
-
payload,
|
|
3821
|
-
provider
|
|
3822
|
-
});
|
|
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);
|
|
3823
3622
|
};
|
|
3824
|
-
const
|
|
3825
|
-
const
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
|
|
3831
|
-
normalizeOpenAICompatibleReasoningContent(openAIPayload);
|
|
3832
|
-
applyOpenAICompatibleRequestOverrides(openAIPayload, {
|
|
3833
|
-
extraBody: modelConfig?.extraBody,
|
|
3834
|
-
source: payload
|
|
3835
|
-
});
|
|
3836
|
-
applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
|
|
3837
|
-
if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
|
|
3838
|
-
if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
|
|
3839
|
-
return openAIPayload;
|
|
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
|
+
}
|
|
3840
3629
|
};
|
|
3841
|
-
const
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
if (
|
|
3845
|
-
|
|
3846
|
-
|
|
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;
|
|
3847
3637
|
}
|
|
3848
3638
|
};
|
|
3849
|
-
const
|
|
3850
|
-
|
|
3851
|
-
|
|
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];
|
|
3648
|
+
});
|
|
3852
3649
|
};
|
|
3853
|
-
const
|
|
3854
|
-
|
|
3855
|
-
for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
|
|
3650
|
+
const hasToolRef = (block) => {
|
|
3651
|
+
return Array.isArray(block.content) && block.content.some((c) => c.type === "tool_reference");
|
|
3856
3652
|
};
|
|
3857
|
-
const
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
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;
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3862
3662
|
};
|
|
3863
|
-
const
|
|
3864
|
-
const
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
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
|
+
});
|
|
3868
3668
|
};
|
|
3869
|
-
const
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
}
|
|
3877
|
-
|
|
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 };
|
|
3878
3684
|
}
|
|
3879
|
-
if (!Array.isArray(message.content)) return;
|
|
3880
|
-
const lastPart = message.content.at(-1);
|
|
3881
|
-
if (!lastPart) return;
|
|
3882
|
-
setContextCacheControl(lastPart);
|
|
3883
3685
|
};
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
const
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
});
|
|
3900
|
-
continue;
|
|
3901
|
-
}
|
|
3902
|
-
let data = chunk.data;
|
|
3903
|
-
if (!data) continue;
|
|
3904
|
-
if (chunk.data === "[DONE]") break;
|
|
3905
|
-
const parsed = parseProviderStreamEvent(data, providerConfig);
|
|
3906
|
-
if (parsed) {
|
|
3907
|
-
usage = mergeAnthropicUsage(usage, parsed.usage);
|
|
3908
|
-
data = parsed.data;
|
|
3909
|
-
}
|
|
3910
|
-
await stream.writeSSE({
|
|
3911
|
-
event: eventName,
|
|
3912
|
-
data
|
|
3913
|
-
});
|
|
3914
|
-
}
|
|
3915
|
-
recordUsage(usage);
|
|
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
|
|
3916
3701
|
});
|
|
3917
|
-
|
|
3918
|
-
const
|
|
3919
|
-
|
|
3920
|
-
|
|
3702
|
+
debugJson(logger$7, "Translated OpenAI request payload:", openAIPayload);
|
|
3703
|
+
const response = await createChatCompletions(openAIPayload, {
|
|
3704
|
+
subagentMarker,
|
|
3705
|
+
requestId,
|
|
3706
|
+
sessionId,
|
|
3707
|
+
compactType
|
|
3708
|
+
});
|
|
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");
|
|
3921
3717
|
return streamSSE(c, async (stream) => {
|
|
3922
3718
|
let usage = {};
|
|
3923
3719
|
const streamState = {
|
|
@@ -3927,111 +3723,352 @@ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstream
|
|
|
3927
3723
|
toolCalls: {},
|
|
3928
3724
|
thinkingBlockOpen: false
|
|
3929
3725
|
};
|
|
3930
|
-
for await (const
|
|
3931
|
-
logger$
|
|
3932
|
-
if (
|
|
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]);
|
|
3933
3736
|
await stream.writeSSE({
|
|
3934
|
-
event:
|
|
3935
|
-
data:
|
|
3737
|
+
event: event.type,
|
|
3738
|
+
data: eventData
|
|
3936
3739
|
});
|
|
3937
|
-
continue;
|
|
3938
3740
|
}
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
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);
|
|
3751
|
+
});
|
|
3752
|
+
};
|
|
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
|
|
3761
|
+
});
|
|
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
|
|
3770
|
+
});
|
|
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
|
+
}
|
|
3942
3802
|
}
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
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);
|
|
3822
|
+
};
|
|
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));
|
|
3950
3852
|
await stream.writeSSE({
|
|
3951
|
-
event:
|
|
3952
|
-
data
|
|
3853
|
+
event: eventName,
|
|
3854
|
+
data
|
|
3953
3855
|
});
|
|
3954
3856
|
}
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
data: eventData
|
|
3962
|
-
});
|
|
3963
|
-
}
|
|
3964
|
-
recordUsage(usage);
|
|
3857
|
+
recordUsage(usage);
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
debugJsonTail(logger$7, "Non-streaming Messages result:", {
|
|
3861
|
+
value: response,
|
|
3862
|
+
tailLength: 400
|
|
3965
3863
|
});
|
|
3864
|
+
recordUsage(normalizeAnthropicUsage(response.usage));
|
|
3865
|
+
return c.json(response);
|
|
3966
3866
|
};
|
|
3967
|
-
const
|
|
3867
|
+
const prepareCopilotChatCompletionsPayload = (payload) => {
|
|
3868
|
+
applyCopilotContextCache(payload);
|
|
3869
|
+
};
|
|
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
|
+
}
|
|
3876
|
+
};
|
|
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);
|
|
3881
|
+
};
|
|
3882
|
+
const isCopilotContextCacheEligible = (message) => {
|
|
3883
|
+
if (typeof message.content === "string") return message.content.length > 0;
|
|
3884
|
+
return Array.isArray(message.content) && message.content.length > 0;
|
|
3885
|
+
};
|
|
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) => {
|
|
3968
3897
|
try {
|
|
3969
3898
|
return JSON.parse(data);
|
|
3970
|
-
} catch
|
|
3971
|
-
logger$3.error("provider.messages.openai_compatible.parse_chunk_error", {
|
|
3972
|
-
data,
|
|
3973
|
-
error
|
|
3974
|
-
});
|
|
3899
|
+
} catch {
|
|
3975
3900
|
return null;
|
|
3976
3901
|
}
|
|
3977
3902
|
};
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
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;
|
|
3919
|
+
};
|
|
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;
|
|
3988
3935
|
}
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
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;
|
|
3941
|
+
continue;
|
|
3942
|
+
}
|
|
3943
|
+
return parsed;
|
|
3944
|
+
} catch {
|
|
3945
|
+
searchFrom = reminderEnd + 18;
|
|
3946
|
+
continue;
|
|
3995
3947
|
}
|
|
3996
|
-
return {
|
|
3997
|
-
data: JSON.stringify(parsed),
|
|
3998
|
-
usage: {}
|
|
3999
|
-
};
|
|
4000
|
-
} catch (error) {
|
|
4001
|
-
logger$3.error("provider.messages.streaming.adjust_tokens_error", {
|
|
4002
|
-
error,
|
|
4003
|
-
originalData: data
|
|
4004
|
-
});
|
|
4005
|
-
return null;
|
|
4006
3948
|
}
|
|
3949
|
+
return null;
|
|
4007
3950
|
};
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
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
|
|
4015
3959
|
};
|
|
4016
|
-
|
|
4017
|
-
const
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
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
|
|
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;
|
|
4022
4019
|
};
|
|
4023
|
-
const
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
providerName: provider,
|
|
4027
|
-
sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
|
|
4028
|
-
});
|
|
4029
|
-
const adjustInputTokens = (providerConfig, usage) => {
|
|
4030
|
-
if (!providerConfig.adjustInputTokens || !usage) return;
|
|
4031
|
-
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
4032
|
-
debugJson(logger$3, "provider.messages.adjusted_usage:", usage);
|
|
4020
|
+
const shouldUseMessagesApi = (selectedModel) => {
|
|
4021
|
+
if (!isMessagesApiEnabled()) return false;
|
|
4022
|
+
return selectedModel?.supported_endpoints?.includes(MESSAGES_ENDPOINT) ?? false;
|
|
4033
4023
|
};
|
|
4034
4024
|
|
|
4025
|
+
//#endregion
|
|
4026
|
+
//#region src/routes/messages/route.ts
|
|
4027
|
+
const messageRoutes = new Hono();
|
|
4028
|
+
messageRoutes.post("/", async (c) => {
|
|
4029
|
+
try {
|
|
4030
|
+
return await handleCompletion(c);
|
|
4031
|
+
} catch (error) {
|
|
4032
|
+
return await forwardError(c, error);
|
|
4033
|
+
}
|
|
4034
|
+
});
|
|
4035
|
+
messageRoutes.post("/count_tokens", async (c) => {
|
|
4036
|
+
try {
|
|
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;
|
|
4051
|
+
return {
|
|
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
|
|
4060
|
+
};
|
|
4061
|
+
});
|
|
4062
|
+
return c.json({
|
|
4063
|
+
object: "list",
|
|
4064
|
+
data: models,
|
|
4065
|
+
has_more: false
|
|
4066
|
+
});
|
|
4067
|
+
} catch (error) {
|
|
4068
|
+
return await forwardError(c, error);
|
|
4069
|
+
}
|
|
4070
|
+
});
|
|
4071
|
+
|
|
4035
4072
|
//#endregion
|
|
4036
4073
|
//#region src/routes/provider/messages/route.ts
|
|
4037
4074
|
const providerMessageRoutes = new Hono();
|
|
@@ -4339,4 +4376,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
4339
4376
|
|
|
4340
4377
|
//#endregion
|
|
4341
4378
|
export { server };
|
|
4342
|
-
//# sourceMappingURL=server-
|
|
4379
|
+
//# sourceMappingURL=server-D4pg54e1.js.map
|