@h-ai/ai 0.1.0-alpha.39 → 0.1.0-alpha.41
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 +137 -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,87 @@ 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
|
+
var CONVERSATION_SUMMARY_NAME = "hai_internal_conversation_summary_v1";
|
|
1558
|
+
function completeCompression(result, maxTokens) {
|
|
1559
|
+
if (result.compressedTokens > maxTokens) {
|
|
1560
|
+
return err(
|
|
1561
|
+
HaiAIError.CONTEXT_BUDGET_EXCEEDED,
|
|
1562
|
+
aiM("ai_contextBudgetExceeded", {
|
|
1563
|
+
params: {
|
|
1564
|
+
tokens: result.compressedTokens,
|
|
1565
|
+
budget: maxTokens
|
|
1566
|
+
}
|
|
1567
|
+
})
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
return ok(result);
|
|
1571
|
+
}
|
|
1572
|
+
function isGeneratedConversationSummary(message) {
|
|
1573
|
+
return message.role === "system" && message.name === CONVERSATION_SUMMARY_NAME;
|
|
1574
|
+
}
|
|
1575
|
+
function partitionSummaryMessages(messages, preserveSystem) {
|
|
1576
|
+
const isPreservedSystem = (message) => {
|
|
1577
|
+
return preserveSystem && message.role === "system" && !isGeneratedConversationSummary(message);
|
|
1578
|
+
};
|
|
1579
|
+
return {
|
|
1580
|
+
systemMessages: messages.filter(isPreservedSystem),
|
|
1581
|
+
conversationMessages: messages.filter((message) => !isPreservedSystem(message))
|
|
1582
|
+
};
|
|
1583
|
+
}
|
|
1584
|
+
function unitizeNonSystemMessages(messages) {
|
|
1585
|
+
const units = [];
|
|
1586
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
1587
|
+
const message = messages[index];
|
|
1588
|
+
if (message?.role !== "assistant" || !message.tool_calls?.length) {
|
|
1589
|
+
if (message) {
|
|
1590
|
+
units.push([message]);
|
|
1591
|
+
}
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
const toolCallIds = new Set(message.tool_calls.map((toolCall) => toolCall.id));
|
|
1595
|
+
const unit = [message];
|
|
1596
|
+
let nextIndex = index + 1;
|
|
1597
|
+
while (nextIndex < messages.length) {
|
|
1598
|
+
const nextMessage = messages[nextIndex];
|
|
1599
|
+
if (nextMessage?.role !== "tool" || !toolCallIds.has(nextMessage.tool_call_id)) {
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
unit.push(nextMessage);
|
|
1603
|
+
nextIndex += 1;
|
|
1604
|
+
}
|
|
1605
|
+
units.push(unit);
|
|
1606
|
+
index = nextIndex - 1;
|
|
1607
|
+
}
|
|
1608
|
+
return units;
|
|
1609
|
+
}
|
|
1610
|
+
function flattenUnits(units) {
|
|
1611
|
+
return units.flatMap((unit) => unit);
|
|
1612
|
+
}
|
|
1613
|
+
function splitProtectedSuffix(messages, preserveLastN) {
|
|
1614
|
+
const units = unitizeNonSystemMessages(messages);
|
|
1615
|
+
let protectedStart = units.length;
|
|
1616
|
+
let protectedMessageCount = 0;
|
|
1617
|
+
const requiredMessageCount = Math.max(0, preserveLastN);
|
|
1618
|
+
while (protectedStart > 0 && protectedMessageCount < requiredMessageCount) {
|
|
1619
|
+
protectedStart -= 1;
|
|
1620
|
+
protectedMessageCount += units[protectedStart]?.length ?? 0;
|
|
1621
|
+
}
|
|
1622
|
+
let latestUserUnitIndex = -1;
|
|
1623
|
+
for (let index = units.length - 1; index >= 0; index -= 1) {
|
|
1624
|
+
if (units[index]?.some((message) => message.role === "user")) {
|
|
1625
|
+
latestUserUnitIndex = index;
|
|
1626
|
+
break;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
if (latestUserUnitIndex >= 0) {
|
|
1630
|
+
protectedStart = Math.min(protectedStart, latestUserUnitIndex);
|
|
1631
|
+
}
|
|
1632
|
+
return {
|
|
1633
|
+
removableUnits: units.slice(0, protectedStart),
|
|
1634
|
+
protectedUnits: units.slice(protectedStart)
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1556
1637
|
function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
1557
1638
|
function resolveMaxTokens(optionMaxTokens) {
|
|
1558
1639
|
const fromOption = optionMaxTokens ?? config.defaultMaxTokens;
|
|
@@ -1570,29 +1651,38 @@ function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
|
1570
1651
|
nonSystemMessages.push(msg);
|
|
1571
1652
|
}
|
|
1572
1653
|
}
|
|
1573
|
-
const
|
|
1574
|
-
const
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1654
|
+
const { removableUnits, protectedUnits } = splitProtectedSuffix(nonSystemMessages, preserveLastN);
|
|
1655
|
+
const emptyMessageTokens = token.estimateMessages([]);
|
|
1656
|
+
const estimateUnitTokens = (unit) => {
|
|
1657
|
+
return Math.max(0, token.estimateMessages(unit) - emptyMessageTokens);
|
|
1658
|
+
};
|
|
1659
|
+
let retainedTokens = token.estimateMessages(systemMessages) + protectedUnits.reduce((total, unit) => total + estimateUnitTokens(unit), 0);
|
|
1660
|
+
let retainedStart = removableUnits.length;
|
|
1661
|
+
let retainedUnits = [...protectedUnits];
|
|
1662
|
+
let finalMessages = [...systemMessages, ...flattenUnits(retainedUnits)];
|
|
1663
|
+
if (retainedTokens > maxTokens) {
|
|
1577
1664
|
return {
|
|
1578
|
-
messages:
|
|
1579
|
-
removedCount:
|
|
1665
|
+
messages: finalMessages,
|
|
1666
|
+
removedCount: flattenUnits(removableUnits).length
|
|
1580
1667
|
};
|
|
1581
1668
|
}
|
|
1582
|
-
let
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1669
|
+
for (let index = removableUnits.length - 1; index >= 0; index -= 1) {
|
|
1670
|
+
const unit = removableUnits[index];
|
|
1671
|
+
if (!unit) {
|
|
1672
|
+
continue;
|
|
1673
|
+
}
|
|
1674
|
+
const unitTokens = estimateUnitTokens(unit);
|
|
1675
|
+
if (retainedTokens + unitTokens > maxTokens) {
|
|
1588
1676
|
break;
|
|
1589
|
-
|
|
1590
|
-
|
|
1677
|
+
}
|
|
1678
|
+
retainedTokens += unitTokens;
|
|
1679
|
+
retainedStart = index;
|
|
1591
1680
|
}
|
|
1592
|
-
|
|
1681
|
+
retainedUnits = [...removableUnits.slice(retainedStart), ...protectedUnits];
|
|
1682
|
+
finalMessages = [...systemMessages, ...flattenUnits(retainedUnits)];
|
|
1593
1683
|
return {
|
|
1594
1684
|
messages: finalMessages,
|
|
1595
|
-
removedCount: nonSystemMessages.length -
|
|
1685
|
+
removedCount: nonSystemMessages.length - flattenUnits(retainedUnits).length
|
|
1596
1686
|
};
|
|
1597
1687
|
}
|
|
1598
1688
|
async function tryCompress(messages, options) {
|
|
@@ -1620,25 +1710,25 @@ function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
|
1620
1710
|
);
|
|
1621
1711
|
const compressedTokens = token.estimateMessages(compressed);
|
|
1622
1712
|
logger8.trace("Sliding window compression completed", { originalTokens, compressedTokens, removedCount });
|
|
1623
|
-
return
|
|
1713
|
+
return completeCompression({
|
|
1624
1714
|
messages: compressed,
|
|
1625
1715
|
originalTokens,
|
|
1626
1716
|
compressedTokens,
|
|
1627
1717
|
removedCount
|
|
1628
|
-
});
|
|
1718
|
+
}, maxTokens);
|
|
1629
1719
|
}
|
|
1630
1720
|
if (strategy === "summary") {
|
|
1631
|
-
const
|
|
1632
|
-
const
|
|
1633
|
-
const preserved =
|
|
1634
|
-
const toSummarize2 =
|
|
1721
|
+
const { systemMessages: systemMessages2, conversationMessages: conversationMessages2 } = partitionSummaryMessages(messages, preserveSystem);
|
|
1722
|
+
const { removableUnits: removableUnits2, protectedUnits: protectedUnits2 } = splitProtectedSuffix(conversationMessages2, preserveLastN);
|
|
1723
|
+
const preserved = flattenUnits(protectedUnits2);
|
|
1724
|
+
const toSummarize2 = flattenUnits(removableUnits2);
|
|
1635
1725
|
if (toSummarize2.length === 0) {
|
|
1636
|
-
return
|
|
1726
|
+
return completeCompression({
|
|
1637
1727
|
messages: [...messages],
|
|
1638
1728
|
originalTokens,
|
|
1639
1729
|
compressedTokens: originalTokens,
|
|
1640
1730
|
removedCount: 0
|
|
1641
|
-
});
|
|
1731
|
+
}, maxTokens);
|
|
1642
1732
|
}
|
|
1643
1733
|
const summaryResult = await summary.generate(toSummarize2, { model: options?.summaryModel });
|
|
1644
1734
|
if (!summaryResult.success)
|
|
@@ -1646,19 +1736,20 @@ function createCompressOperations(config, token, summary, modelMaxTokens) {
|
|
|
1646
1736
|
const summaryText = summaryResult.data;
|
|
1647
1737
|
const summaryMessage = {
|
|
1648
1738
|
role: "system",
|
|
1649
|
-
|
|
1739
|
+
name: CONVERSATION_SUMMARY_NAME,
|
|
1740
|
+
content: `${CONVERSATION_SUMMARY_PREFIX}
|
|
1650
1741
|
${summaryText}`
|
|
1651
1742
|
};
|
|
1652
1743
|
const compressed = [...systemMessages2, summaryMessage, ...preserved];
|
|
1653
1744
|
const compressedTokens = token.estimateMessages(compressed);
|
|
1654
1745
|
logger8.trace("Summary compression completed", { originalTokens, compressedTokens, removedCount: toSummarize2.length });
|
|
1655
|
-
return
|
|
1746
|
+
return completeCompression({
|
|
1656
1747
|
messages: compressed,
|
|
1657
1748
|
originalTokens,
|
|
1658
1749
|
compressedTokens,
|
|
1659
1750
|
removedCount: toSummarize2.length,
|
|
1660
1751
|
summary: summaryText
|
|
1661
|
-
});
|
|
1752
|
+
}, maxTokens);
|
|
1662
1753
|
}
|
|
1663
1754
|
const { messages: windowResult, removedCount: windowRemoved } = slidingWindow(
|
|
1664
1755
|
messages,
|
|
@@ -1668,17 +1759,17 @@ ${summaryText}`
|
|
|
1668
1759
|
);
|
|
1669
1760
|
const windowTokens = token.estimateMessages(windowResult);
|
|
1670
1761
|
if (windowTokens <= maxTokens) {
|
|
1671
|
-
return
|
|
1762
|
+
return completeCompression({
|
|
1672
1763
|
messages: windowResult,
|
|
1673
1764
|
originalTokens,
|
|
1674
1765
|
compressedTokens: windowTokens,
|
|
1675
1766
|
removedCount: windowRemoved
|
|
1676
|
-
});
|
|
1767
|
+
}, maxTokens);
|
|
1677
1768
|
}
|
|
1678
|
-
const systemMessages
|
|
1679
|
-
const
|
|
1680
|
-
const preservedMessages =
|
|
1681
|
-
const toSummarize =
|
|
1769
|
+
const { systemMessages, conversationMessages } = partitionSummaryMessages(messages, preserveSystem);
|
|
1770
|
+
const { removableUnits, protectedUnits } = splitProtectedSuffix(conversationMessages, preserveLastN);
|
|
1771
|
+
const preservedMessages = flattenUnits(protectedUnits);
|
|
1772
|
+
const toSummarize = flattenUnits(removableUnits);
|
|
1682
1773
|
if (toSummarize.length > 0) {
|
|
1683
1774
|
const summaryResult = await summary.generate(toSummarize, { model: options?.summaryModel });
|
|
1684
1775
|
if (!summaryResult.success)
|
|
@@ -1686,26 +1777,27 @@ ${summaryText}`
|
|
|
1686
1777
|
const summaryText = summaryResult.data;
|
|
1687
1778
|
const summaryMessage = {
|
|
1688
1779
|
role: "system",
|
|
1689
|
-
|
|
1780
|
+
name: CONVERSATION_SUMMARY_NAME,
|
|
1781
|
+
content: `${CONVERSATION_SUMMARY_PREFIX}
|
|
1690
1782
|
${summaryText}`
|
|
1691
1783
|
};
|
|
1692
1784
|
const compressed = [...systemMessages, summaryMessage, ...preservedMessages];
|
|
1693
1785
|
const compressedTokens = token.estimateMessages(compressed);
|
|
1694
1786
|
logger8.trace("Hybrid compression completed", { originalTokens, compressedTokens, removedCount: toSummarize.length });
|
|
1695
|
-
return
|
|
1787
|
+
return completeCompression({
|
|
1696
1788
|
messages: compressed,
|
|
1697
1789
|
originalTokens,
|
|
1698
1790
|
compressedTokens,
|
|
1699
1791
|
removedCount: toSummarize.length,
|
|
1700
1792
|
summary: summaryText
|
|
1701
|
-
});
|
|
1793
|
+
}, maxTokens);
|
|
1702
1794
|
}
|
|
1703
|
-
return
|
|
1795
|
+
return completeCompression({
|
|
1704
1796
|
messages: windowResult,
|
|
1705
1797
|
originalTokens,
|
|
1706
1798
|
compressedTokens: windowTokens,
|
|
1707
1799
|
removedCount: windowRemoved
|
|
1708
|
-
});
|
|
1800
|
+
}, maxTokens);
|
|
1709
1801
|
} catch (error) {
|
|
1710
1802
|
logger8.error("Context compression failed", { error });
|
|
1711
1803
|
return err(HaiAIError.CONTEXT_COMPRESS_FAILED, aiM("ai_contextCompressFailed", { params: { error: String(error) } }), error);
|
|
@@ -1857,7 +1949,7 @@ function createContextOperations(compressConfig, tokenOps, compressOps, contextS
|
|
|
1857
1949
|
});
|
|
1858
1950
|
if (!compressResult.success) {
|
|
1859
1951
|
logger9.warn("Auto-compression failed, keeping original messages", { error: compressResult.error });
|
|
1860
|
-
return
|
|
1952
|
+
return compressResult;
|
|
1861
1953
|
}
|
|
1862
1954
|
if (compressResult.data.summary) {
|
|
1863
1955
|
state.summaries.push({
|
|
@@ -2142,7 +2234,9 @@ function createContextOperations(compressConfig, tokenOps, compressOps, contextS
|
|
|
2142
2234
|
const signal = combineSignal2(chatOpts?.signal, internalSignal);
|
|
2143
2235
|
let streamTurn = null;
|
|
2144
2236
|
try {
|
|
2145
|
-
await manager.addMessage({ role: "user", content: message });
|
|
2237
|
+
const addResult = await manager.addMessage({ role: "user", content: message });
|
|
2238
|
+
if (!addResult.success)
|
|
2239
|
+
throw addResult.error;
|
|
2146
2240
|
const messagesResult = manager.getMessages();
|
|
2147
2241
|
if (!messagesResult.success) {
|
|
2148
2242
|
throw new Error(`Failed to get messages: ${String(messagesResult.error)}`);
|
|
@@ -6915,37 +7009,11 @@ function estimateMessagesTokens(messages, tokenRatio = 0.25) {
|
|
|
6915
7009
|
let total = 0;
|
|
6916
7010
|
for (const msg of messages) {
|
|
6917
7011
|
total += 4;
|
|
6918
|
-
|
|
6919
|
-
total += estimateTextTokens(content, tokenRatio);
|
|
7012
|
+
total += estimateTextTokens(JSON.stringify(msg), tokenRatio);
|
|
6920
7013
|
}
|
|
6921
7014
|
total += 2;
|
|
6922
7015
|
return total;
|
|
6923
7016
|
}
|
|
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
7017
|
function createTokenOperations(config) {
|
|
6950
7018
|
const { tokenRatio } = config;
|
|
6951
7019
|
return {
|