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