@h-ai/ai 0.1.0-alpha.39 → 0.1.0-alpha.40
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/dist/{ai-audio-ws-protocol-DbZZvH52.d.ts → ai-audio-ws-protocol-DE4WxxHM.d.ts} +3 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/{chunk-OZYROB52.js → chunk-DRSZFSCT.js} +4 -2
- package/dist/chunk-DRSZFSCT.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +134 -69
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-OZYROB52.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { aiM, AIConfigSchema, MemoryConfigSchema, RetrievalConfigSchema, KnowledgeConfigSchema, TokenConfigSchema, SummaryConfigSchema, CompressConfigSchema, AudioConfigSchema, resolveModelEntry, resolveAudioModel, resolveModelApi } from './chunk-
|
|
2
|
-
export { A2AConfigSchema, A2ASkillConfigSchema, AIConfigSchema, AUDIO_WS_PATH, ApiTypeSchema, AudioConfigSchema, AudioFormatSchema, AudioModelEntrySchema, AudioProviderSchema, AudioWsClientMessageSchema, AudioWsDoneMessageSchema, AudioWsStartMessageSchema, AudioWsTextMessageSchema, CompressConfigSchema, CompressionStrategySchema, EmbeddingConfigSchema, EntityTypeSchema, FileConfigSchema, KnowledgeConfigSchema, LLMConfigSchema, MCPConfigSchema, MCPServerCapabilitiesSchema, MCPServerConfigSchema, MemoryConfigSchema, MemoryTypeSchema, ModelEntrySchema, ModelScenarioSchema, RetrievalConfigSchema, RetrievalSourceSchema, SummaryConfigSchema, TokenConfigSchema, resolveAudioModel, resolveModelApi, resolveModelEntry } from './chunk-
|
|
1
|
+
import { aiM, AIConfigSchema, MemoryConfigSchema, RetrievalConfigSchema, KnowledgeConfigSchema, TokenConfigSchema, SummaryConfigSchema, CompressConfigSchema, AudioConfigSchema, resolveModelEntry, resolveAudioModel, resolveModelApi } from './chunk-DRSZFSCT.js';
|
|
2
|
+
export { A2AConfigSchema, A2ASkillConfigSchema, AIConfigSchema, AUDIO_WS_PATH, ApiTypeSchema, AudioConfigSchema, AudioFormatSchema, AudioModelEntrySchema, AudioProviderSchema, AudioWsClientMessageSchema, AudioWsDoneMessageSchema, AudioWsStartMessageSchema, AudioWsTextMessageSchema, CompressConfigSchema, CompressionStrategySchema, EmbeddingConfigSchema, EntityTypeSchema, FileConfigSchema, KnowledgeConfigSchema, LLMConfigSchema, MCPConfigSchema, MCPServerCapabilitiesSchema, MCPServerConfigSchema, MemoryConfigSchema, MemoryTypeSchema, ModelEntrySchema, ModelScenarioSchema, RetrievalConfigSchema, RetrievalSourceSchema, SummaryConfigSchema, TokenConfigSchema, resolveAudioModel, resolveModelApi, resolveModelEntry } from './chunk-DRSZFSCT.js';
|
|
3
3
|
import { HaiAIError } from './chunk-P3GND76X.js';
|
|
4
4
|
export { HaiAIError } from './chunk-P3GND76X.js';
|
|
5
5
|
import { core, err, ok } from '@h-ai/core';
|
|
@@ -1553,6 +1553,86 @@ function createProvider(name) {
|
|
|
1553
1553
|
}
|
|
1554
1554
|
}
|
|
1555
1555
|
var logger8 = core.logger.child({ module: "ai", scope: "compress" });
|
|
1556
|
+
var CONVERSATION_SUMMARY_PREFIX = "[Conversation Summary]";
|
|
1557
|
+
function completeCompression(result, maxTokens) {
|
|
1558
|
+
if (result.compressedTokens > maxTokens) {
|
|
1559
|
+
return err(
|
|
1560
|
+
HaiAIError.CONTEXT_BUDGET_EXCEEDED,
|
|
1561
|
+
aiM("ai_contextBudgetExceeded", {
|
|
1562
|
+
params: {
|
|
1563
|
+
tokens: result.compressedTokens,
|
|
1564
|
+
budget: maxTokens
|
|
1565
|
+
}
|
|
1566
|
+
})
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
return ok(result);
|
|
1570
|
+
}
|
|
1571
|
+
function isGeneratedConversationSummary(message) {
|
|
1572
|
+
return message.role === "system" && typeof message.content === "string" && message.content.startsWith(CONVERSATION_SUMMARY_PREFIX);
|
|
1573
|
+
}
|
|
1574
|
+
function partitionSummaryMessages(messages, preserveSystem) {
|
|
1575
|
+
const isPreservedSystem = (message) => {
|
|
1576
|
+
return preserveSystem && message.role === "system" && !isGeneratedConversationSummary(message);
|
|
1577
|
+
};
|
|
1578
|
+
return {
|
|
1579
|
+
systemMessages: messages.filter(isPreservedSystem),
|
|
1580
|
+
conversationMessages: messages.filter((message) => !isPreservedSystem(message))
|
|
1581
|
+
};
|
|
1582
|
+
}
|
|
1583
|
+
function unitizeNonSystemMessages(messages) {
|
|
1584
|
+
const units = [];
|
|
1585
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
1586
|
+
const message = messages[index];
|
|
1587
|
+
if (message?.role !== "assistant" || !message.tool_calls?.length) {
|
|
1588
|
+
if (message) {
|
|
1589
|
+
units.push([message]);
|
|
1590
|
+
}
|
|
1591
|
+
continue;
|
|
1592
|
+
}
|
|
1593
|
+
const toolCallIds = new Set(message.tool_calls.map((toolCall) => toolCall.id));
|
|
1594
|
+
const unit = [message];
|
|
1595
|
+
let nextIndex = index + 1;
|
|
1596
|
+
while (nextIndex < messages.length) {
|
|
1597
|
+
const nextMessage = messages[nextIndex];
|
|
1598
|
+
if (nextMessage?.role !== "tool" || !toolCallIds.has(nextMessage.tool_call_id)) {
|
|
1599
|
+
break;
|
|
1600
|
+
}
|
|
1601
|
+
unit.push(nextMessage);
|
|
1602
|
+
nextIndex += 1;
|
|
1603
|
+
}
|
|
1604
|
+
units.push(unit);
|
|
1605
|
+
index = nextIndex - 1;
|
|
1606
|
+
}
|
|
1607
|
+
return units;
|
|
1608
|
+
}
|
|
1609
|
+
function flattenUnits(units) {
|
|
1610
|
+
return units.flatMap((unit) => unit);
|
|
1611
|
+
}
|
|
1612
|
+
function splitProtectedSuffix(messages, preserveLastN) {
|
|
1613
|
+
const units = unitizeNonSystemMessages(messages);
|
|
1614
|
+
let protectedStart = units.length;
|
|
1615
|
+
let protectedMessageCount = 0;
|
|
1616
|
+
const requiredMessageCount = Math.max(0, preserveLastN);
|
|
1617
|
+
while (protectedStart > 0 && protectedMessageCount < requiredMessageCount) {
|
|
1618
|
+
protectedStart -= 1;
|
|
1619
|
+
protectedMessageCount += units[protectedStart]?.length ?? 0;
|
|
1620
|
+
}
|
|
1621
|
+
let latestUserUnitIndex = -1;
|
|
1622
|
+
for (let index = units.length - 1; index >= 0; index -= 1) {
|
|
1623
|
+
if (units[index]?.some((message) => message.role === "user")) {
|
|
1624
|
+
latestUserUnitIndex = index;
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
if (latestUserUnitIndex >= 0) {
|
|
1629
|
+
protectedStart = Math.min(protectedStart, latestUserUnitIndex);
|
|
1630
|
+
}
|
|
1631
|
+
return {
|
|
1632
|
+
removableUnits: units.slice(0, protectedStart),
|
|
1633
|
+
protectedUnits: units.slice(protectedStart)
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1556
1636
|
function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
1557
1637
|
function resolveMaxTokens(optionMaxTokens) {
|
|
1558
1638
|
const fromOption = optionMaxTokens ?? config.defaultMaxTokens;
|
|
@@ -1570,29 +1650,38 @@ function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
|
1570
1650
|
nonSystemMessages.push(msg);
|
|
1571
1651
|
}
|
|
1572
1652
|
}
|
|
1573
|
-
const
|
|
1574
|
-
const
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1653
|
+
const { removableUnits, protectedUnits } = splitProtectedSuffix(nonSystemMessages, preserveLastN);
|
|
1654
|
+
const emptyMessageTokens = token.estimateMessages([]);
|
|
1655
|
+
const estimateUnitTokens = (unit) => {
|
|
1656
|
+
return Math.max(0, token.estimateMessages(unit) - emptyMessageTokens);
|
|
1657
|
+
};
|
|
1658
|
+
let retainedTokens = token.estimateMessages(systemMessages) + protectedUnits.reduce((total, unit) => total + estimateUnitTokens(unit), 0);
|
|
1659
|
+
let retainedStart = removableUnits.length;
|
|
1660
|
+
let retainedUnits = [...protectedUnits];
|
|
1661
|
+
let finalMessages = [...systemMessages, ...flattenUnits(retainedUnits)];
|
|
1662
|
+
if (retainedTokens > maxTokens) {
|
|
1577
1663
|
return {
|
|
1578
|
-
messages:
|
|
1579
|
-
removedCount:
|
|
1664
|
+
messages: finalMessages,
|
|
1665
|
+
removedCount: flattenUnits(removableUnits).length
|
|
1580
1666
|
};
|
|
1581
1667
|
}
|
|
1582
|
-
let
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1668
|
+
for (let index = removableUnits.length - 1; index >= 0; index -= 1) {
|
|
1669
|
+
const unit = removableUnits[index];
|
|
1670
|
+
if (!unit) {
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
const unitTokens = estimateUnitTokens(unit);
|
|
1674
|
+
if (retainedTokens + unitTokens > maxTokens) {
|
|
1588
1675
|
break;
|
|
1589
|
-
|
|
1590
|
-
|
|
1676
|
+
}
|
|
1677
|
+
retainedTokens += unitTokens;
|
|
1678
|
+
retainedStart = index;
|
|
1591
1679
|
}
|
|
1592
|
-
|
|
1680
|
+
retainedUnits = [...removableUnits.slice(retainedStart), ...protectedUnits];
|
|
1681
|
+
finalMessages = [...systemMessages, ...flattenUnits(retainedUnits)];
|
|
1593
1682
|
return {
|
|
1594
1683
|
messages: finalMessages,
|
|
1595
|
-
removedCount: nonSystemMessages.length -
|
|
1684
|
+
removedCount: nonSystemMessages.length - flattenUnits(retainedUnits).length
|
|
1596
1685
|
};
|
|
1597
1686
|
}
|
|
1598
1687
|
async function tryCompress(messages, options) {
|
|
@@ -1620,25 +1709,25 @@ function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
|
1620
1709
|
);
|
|
1621
1710
|
const compressedTokens = token.estimateMessages(compressed);
|
|
1622
1711
|
logger8.trace("Sliding window compression completed", { originalTokens, compressedTokens, removedCount });
|
|
1623
|
-
return
|
|
1712
|
+
return completeCompression({
|
|
1624
1713
|
messages: compressed,
|
|
1625
1714
|
originalTokens,
|
|
1626
1715
|
compressedTokens,
|
|
1627
1716
|
removedCount
|
|
1628
|
-
});
|
|
1717
|
+
}, maxTokens);
|
|
1629
1718
|
}
|
|
1630
1719
|
if (strategy === "summary") {
|
|
1631
|
-
const
|
|
1632
|
-
const
|
|
1633
|
-
const preserved =
|
|
1634
|
-
const toSummarize2 =
|
|
1720
|
+
const { systemMessages: systemMessages2, conversationMessages: conversationMessages2 } = partitionSummaryMessages(messages, preserveSystem);
|
|
1721
|
+
const { removableUnits: removableUnits2, protectedUnits: protectedUnits2 } = splitProtectedSuffix(conversationMessages2, preserveLastN);
|
|
1722
|
+
const preserved = flattenUnits(protectedUnits2);
|
|
1723
|
+
const toSummarize2 = flattenUnits(removableUnits2);
|
|
1635
1724
|
if (toSummarize2.length === 0) {
|
|
1636
|
-
return
|
|
1725
|
+
return completeCompression({
|
|
1637
1726
|
messages: [...messages],
|
|
1638
1727
|
originalTokens,
|
|
1639
1728
|
compressedTokens: originalTokens,
|
|
1640
1729
|
removedCount: 0
|
|
1641
|
-
});
|
|
1730
|
+
}, maxTokens);
|
|
1642
1731
|
}
|
|
1643
1732
|
const summaryResult = await summary.generate(toSummarize2, { model: options?.summaryModel });
|
|
1644
1733
|
if (!summaryResult.success)
|
|
@@ -1646,19 +1735,19 @@ function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
|
1646
1735
|
const summaryText = summaryResult.data;
|
|
1647
1736
|
const summaryMessage = {
|
|
1648
1737
|
role: "system",
|
|
1649
|
-
content:
|
|
1738
|
+
content: `${CONVERSATION_SUMMARY_PREFIX}
|
|
1650
1739
|
${summaryText}`
|
|
1651
1740
|
};
|
|
1652
1741
|
const compressed = [...systemMessages2, summaryMessage, ...preserved];
|
|
1653
1742
|
const compressedTokens = token.estimateMessages(compressed);
|
|
1654
1743
|
logger8.trace("Summary compression completed", { originalTokens, compressedTokens, removedCount: toSummarize2.length });
|
|
1655
|
-
return
|
|
1744
|
+
return completeCompression({
|
|
1656
1745
|
messages: compressed,
|
|
1657
1746
|
originalTokens,
|
|
1658
1747
|
compressedTokens,
|
|
1659
1748
|
removedCount: toSummarize2.length,
|
|
1660
1749
|
summary: summaryText
|
|
1661
|
-
});
|
|
1750
|
+
}, maxTokens);
|
|
1662
1751
|
}
|
|
1663
1752
|
const { messages: windowResult, removedCount: windowRemoved } = slidingWindow(
|
|
1664
1753
|
messages,
|
|
@@ -1668,17 +1757,17 @@ ${summaryText}`
|
|
|
1668
1757
|
);
|
|
1669
1758
|
const windowTokens = token.estimateMessages(windowResult);
|
|
1670
1759
|
if (windowTokens <= maxTokens) {
|
|
1671
|
-
return
|
|
1760
|
+
return completeCompression({
|
|
1672
1761
|
messages: windowResult,
|
|
1673
1762
|
originalTokens,
|
|
1674
1763
|
compressedTokens: windowTokens,
|
|
1675
1764
|
removedCount: windowRemoved
|
|
1676
|
-
});
|
|
1765
|
+
}, maxTokens);
|
|
1677
1766
|
}
|
|
1678
|
-
const systemMessages
|
|
1679
|
-
const
|
|
1680
|
-
const preservedMessages =
|
|
1681
|
-
const toSummarize =
|
|
1767
|
+
const { systemMessages, conversationMessages } = partitionSummaryMessages(messages, preserveSystem);
|
|
1768
|
+
const { removableUnits, protectedUnits } = splitProtectedSuffix(conversationMessages, preserveLastN);
|
|
1769
|
+
const preservedMessages = flattenUnits(protectedUnits);
|
|
1770
|
+
const toSummarize = flattenUnits(removableUnits);
|
|
1682
1771
|
if (toSummarize.length > 0) {
|
|
1683
1772
|
const summaryResult = await summary.generate(toSummarize, { model: options?.summaryModel });
|
|
1684
1773
|
if (!summaryResult.success)
|
|
@@ -1686,26 +1775,26 @@ ${summaryText}`
|
|
|
1686
1775
|
const summaryText = summaryResult.data;
|
|
1687
1776
|
const summaryMessage = {
|
|
1688
1777
|
role: "system",
|
|
1689
|
-
content:
|
|
1778
|
+
content: `${CONVERSATION_SUMMARY_PREFIX}
|
|
1690
1779
|
${summaryText}`
|
|
1691
1780
|
};
|
|
1692
1781
|
const compressed = [...systemMessages, summaryMessage, ...preservedMessages];
|
|
1693
1782
|
const compressedTokens = token.estimateMessages(compressed);
|
|
1694
1783
|
logger8.trace("Hybrid compression completed", { originalTokens, compressedTokens, removedCount: toSummarize.length });
|
|
1695
|
-
return
|
|
1784
|
+
return completeCompression({
|
|
1696
1785
|
messages: compressed,
|
|
1697
1786
|
originalTokens,
|
|
1698
1787
|
compressedTokens,
|
|
1699
1788
|
removedCount: toSummarize.length,
|
|
1700
1789
|
summary: summaryText
|
|
1701
|
-
});
|
|
1790
|
+
}, maxTokens);
|
|
1702
1791
|
}
|
|
1703
|
-
return
|
|
1792
|
+
return completeCompression({
|
|
1704
1793
|
messages: windowResult,
|
|
1705
1794
|
originalTokens,
|
|
1706
1795
|
compressedTokens: windowTokens,
|
|
1707
1796
|
removedCount: windowRemoved
|
|
1708
|
-
});
|
|
1797
|
+
}, maxTokens);
|
|
1709
1798
|
} catch (error) {
|
|
1710
1799
|
logger8.error("Context compression failed", { error });
|
|
1711
1800
|
return err(HaiAIError.CONTEXT_COMPRESS_FAILED, aiM("ai_contextCompressFailed", { params: { error: String(error) } }), error);
|
|
@@ -1857,7 +1946,7 @@ function createContextOperations(compressConfig, tokenOps, compressOps, contextS
|
|
|
1857
1946
|
});
|
|
1858
1947
|
if (!compressResult.success) {
|
|
1859
1948
|
logger9.warn("Auto-compression failed, keeping original messages", { error: compressResult.error });
|
|
1860
|
-
return
|
|
1949
|
+
return compressResult;
|
|
1861
1950
|
}
|
|
1862
1951
|
if (compressResult.data.summary) {
|
|
1863
1952
|
state.summaries.push({
|
|
@@ -2142,7 +2231,9 @@ function createContextOperations(compressConfig, tokenOps, compressOps, contextS
|
|
|
2142
2231
|
const signal = combineSignal2(chatOpts?.signal, internalSignal);
|
|
2143
2232
|
let streamTurn = null;
|
|
2144
2233
|
try {
|
|
2145
|
-
await manager.addMessage({ role: "user", content: message });
|
|
2234
|
+
const addResult = await manager.addMessage({ role: "user", content: message });
|
|
2235
|
+
if (!addResult.success)
|
|
2236
|
+
throw addResult.error;
|
|
2146
2237
|
const messagesResult = manager.getMessages();
|
|
2147
2238
|
if (!messagesResult.success) {
|
|
2148
2239
|
throw new Error(`Failed to get messages: ${String(messagesResult.error)}`);
|
|
@@ -6915,37 +7006,11 @@ function estimateMessagesTokens(messages, tokenRatio = 0.25) {
|
|
|
6915
7006
|
let total = 0;
|
|
6916
7007
|
for (const msg of messages) {
|
|
6917
7008
|
total += 4;
|
|
6918
|
-
|
|
6919
|
-
total += estimateTextTokens(content, tokenRatio);
|
|
7009
|
+
total += estimateTextTokens(JSON.stringify(msg), tokenRatio);
|
|
6920
7010
|
}
|
|
6921
7011
|
total += 2;
|
|
6922
7012
|
return total;
|
|
6923
7013
|
}
|
|
6924
|
-
function getMessageContent(msg) {
|
|
6925
|
-
if (msg.role === "assistant") {
|
|
6926
|
-
const c = msg.content;
|
|
6927
|
-
if (typeof c === "string")
|
|
6928
|
-
return c;
|
|
6929
|
-
if (Array.isArray(c))
|
|
6930
|
-
return c.filter((p) => p.type === "text").map((p) => p.text ?? "").join(" ");
|
|
6931
|
-
return "";
|
|
6932
|
-
}
|
|
6933
|
-
if (msg.role === "tool") {
|
|
6934
|
-
const c = msg.content;
|
|
6935
|
-
if (typeof c === "string")
|
|
6936
|
-
return c;
|
|
6937
|
-
if (Array.isArray(c))
|
|
6938
|
-
return c.filter((p) => p.type === "text").map((p) => p.text ?? "").join(" ");
|
|
6939
|
-
return "";
|
|
6940
|
-
}
|
|
6941
|
-
const content = msg.content;
|
|
6942
|
-
if (typeof content === "string")
|
|
6943
|
-
return content;
|
|
6944
|
-
if (Array.isArray(content)) {
|
|
6945
|
-
return content.filter((c) => c.type === "text").map((c) => c.text).join(" ");
|
|
6946
|
-
}
|
|
6947
|
-
return "";
|
|
6948
|
-
}
|
|
6949
7014
|
function createTokenOperations(config) {
|
|
6950
7015
|
const { tokenRatio } = config;
|
|
6951
7016
|
return {
|