@librechat/agents 3.7.0 → 3.7.1
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/cjs/agents/AgentContext.cjs +11 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +7 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +56 -9
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/main.cjs +11 -1
- package/dist/cjs/messages/content.cjs +8 -5
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +15 -5
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +2 -1
- package/dist/cjs/messages/projectionInvariant.cjs +74 -0
- package/dist/cjs/messages/projectionInvariant.cjs.map +1 -0
- package/dist/cjs/messages/provenance.cjs +30 -8
- package/dist/cjs/messages/provenance.cjs.map +1 -1
- package/dist/cjs/messages/recency.cjs +202 -7
- package/dist/cjs/messages/recency.cjs.map +1 -1
- package/dist/cjs/messages/toolResultTypes.cjs +1 -0
- package/dist/cjs/session/AgentSession.cjs +4 -23
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/deriveMessages.cjs +25 -0
- package/dist/cjs/session/deriveMessages.cjs.map +1 -0
- package/dist/cjs/session/index.cjs +1 -0
- package/dist/cjs/summarization/node.cjs +13 -6
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/subagent/InMemorySubagentTaskStore.cjs +65 -8
- package/dist/cjs/tools/subagent/InMemorySubagentTaskStore.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +11 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +8 -3
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +56 -9
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/main.mjs +6 -4
- package/dist/esm/messages/content.mjs +8 -6
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +16 -6
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +2 -1
- package/dist/esm/messages/projectionInvariant.mjs +72 -0
- package/dist/esm/messages/projectionInvariant.mjs.map +1 -0
- package/dist/esm/messages/provenance.mjs +30 -9
- package/dist/esm/messages/provenance.mjs.map +1 -1
- package/dist/esm/messages/recency.mjs +201 -8
- package/dist/esm/messages/recency.mjs.map +1 -1
- package/dist/esm/messages/toolResultTypes.mjs +1 -1
- package/dist/esm/session/AgentSession.mjs +4 -23
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/deriveMessages.mjs +25 -0
- package/dist/esm/session/deriveMessages.mjs.map +1 -0
- package/dist/esm/session/index.mjs +1 -0
- package/dist/esm/summarization/node.mjs +14 -7
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/subagent/InMemorySubagentTaskStore.mjs +65 -8
- package/dist/esm/tools/subagent/InMemorySubagentTaskStore.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +6 -1
- package/dist/types/messages/content.d.ts +4 -1
- package/dist/types/messages/format.d.ts +6 -0
- package/dist/types/messages/index.d.ts +1 -0
- package/dist/types/messages/projectionInvariant.d.ts +25 -0
- package/dist/types/messages/provenance.d.ts +10 -0
- package/dist/types/messages/recency.d.ts +30 -18
- package/dist/types/session/deriveMessages.d.ts +11 -0
- package/dist/types/session/index.d.ts +2 -0
- package/dist/types/tools/subagent/InMemorySubagentTaskStore.d.ts +13 -1
- package/dist/types/types/graph.d.ts +1 -1
- package/dist/types/types/subagentTasks.d.ts +22 -0
- package/dist/types/types/summarize.d.ts +15 -13
- package/package.json +3 -1
- package/src/agents/AgentContext.ts +19 -5
- package/src/graphs/Graph.ts +9 -1
- package/src/llm/invoke.ts +102 -23
- package/src/messages/content.ts +20 -10
- package/src/messages/format.ts +29 -5
- package/src/messages/index.ts +1 -0
- package/src/messages/projectionInvariant.ts +134 -0
- package/src/messages/provenance.ts +60 -18
- package/src/messages/recency.ts +429 -27
- package/src/session/AgentSession.ts +4 -30
- package/src/session/deriveMessages.ts +37 -0
- package/src/session/index.ts +2 -0
- package/src/summarization/node.ts +37 -15
- package/src/tools/subagent/InMemorySubagentTaskStore.ts +139 -6
- package/src/types/graph.ts +1 -0
- package/src/types/subagentTasks.ts +28 -0
- package/src/types/summarize.ts +15 -13
package/src/messages/format.ts
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
hasBijectiveProviderContentPartMapping,
|
|
47
47
|
inspectProviderMessageProvenance,
|
|
48
48
|
inspectProviderSourceMessageIds,
|
|
49
|
+
setFreshProviderMessageProvenance,
|
|
49
50
|
setInvalidProviderMessageProvenance,
|
|
50
51
|
setProviderMessageProvenance,
|
|
51
52
|
} from './provenance';
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
} from '@/utils/toolContent';
|
|
58
59
|
import { normalizeAnthropicToolCallId } from '@/llm/anthropic/utils/message_inputs';
|
|
59
60
|
import { toLangChainContent, toLangChainMessageFields } from './langchain';
|
|
61
|
+
import { flattenLegacyContent, isLegacyConvertible } from './content';
|
|
60
62
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
61
63
|
import { Providers, ContentTypes, Constants } from '@/common';
|
|
62
64
|
import { emitAgentLog } from '@/utils/events';
|
|
@@ -378,6 +380,12 @@ type SourceContentPartIndices = number | readonly number[];
|
|
|
378
380
|
|
|
379
381
|
interface FormatAgentMessagesOptions {
|
|
380
382
|
provider?: ProviderName;
|
|
383
|
+
/** Emit flattenable text content as the joined string the legacy-content
|
|
384
|
+
* projection would produce, so the per-request `formatContentStrings` pass
|
|
385
|
+
* finds nothing to convert and every history message keeps its identity —
|
|
386
|
+
* which is what lets exact-count reuse skip re-tokenizing it. Set this if
|
|
387
|
+
* and only if the run's provider uses legacy string content. */
|
|
388
|
+
legacyContent?: boolean;
|
|
381
389
|
/** Reconstruct hidden `reasoning_content` from `THINK` parts onto prior
|
|
382
390
|
* tool-call messages. Explicit opt-in for OpenAI-compatible endpoints that
|
|
383
391
|
* replay reasoning across turns; defaults to on for DeepSeek thinking-mode. */
|
|
@@ -1380,7 +1388,7 @@ function stampSourceMessageIdentity(
|
|
|
1380
1388
|
},
|
|
1381
1389
|
];
|
|
1382
1390
|
}
|
|
1383
|
-
|
|
1391
|
+
setFreshProviderMessageProvenance(message, partsToStamp);
|
|
1384
1392
|
if (sourceMessageId == null || derivedIndex !== 0) {
|
|
1385
1393
|
return;
|
|
1386
1394
|
}
|
|
@@ -1996,6 +2004,22 @@ export const formatAgentMessages = (
|
|
|
1996
2004
|
* assistant turn. Held rather than emitted so an entry that produces
|
|
1997
2005
|
* nothing cannot leave the anchor stranded as the final turn.
|
|
1998
2006
|
*/
|
|
2007
|
+
const legacyContentEnabled = options?.legacyContent === true;
|
|
2008
|
+
/** Emission choke point: every formatted message enters the result here, so
|
|
2009
|
+
* the legacy flatten happens once per message with no closing rescan. The
|
|
2010
|
+
* summary boundary slices payload entries before formatting, and nothing
|
|
2011
|
+
* mutates an emitted message's content afterwards, so flattening at
|
|
2012
|
+
* emission and flattening at return are equivalent. */
|
|
2013
|
+
const emitFormattedMessage = (message: (typeof messages)[number]): void => {
|
|
2014
|
+
if (legacyContentEnabled && isLegacyConvertible(message)) {
|
|
2015
|
+
const flattened = flattenLegacyContent(
|
|
2016
|
+
message.content as MessageContentComplex[]
|
|
2017
|
+
);
|
|
2018
|
+
message.content = flattened;
|
|
2019
|
+
message.lc_kwargs.content = flattened;
|
|
2020
|
+
}
|
|
2021
|
+
messages.push(message);
|
|
2022
|
+
};
|
|
1999
2023
|
let pendingSteerAnchor = false;
|
|
2000
2024
|
/**
|
|
2001
2025
|
* Emits the deferred anchor ahead of `next` — the message about to be
|
|
@@ -2018,7 +2042,7 @@ export const formatAgentMessages = (
|
|
|
2018
2042
|
'assistant'
|
|
2019
2043
|
);
|
|
2020
2044
|
stampSourceMessageIdentity(anchor, undefined, 0, 'synthetic');
|
|
2021
|
-
|
|
2045
|
+
emitFormattedMessage(anchor);
|
|
2022
2046
|
};
|
|
2023
2047
|
// If indexTokenCountMap is provided, create a new map to track the updated indices
|
|
2024
2048
|
const updatedIndexTokenCountMap: Record<number, number> = {};
|
|
@@ -2098,7 +2122,7 @@ export const formatAgentMessages = (
|
|
|
2098
2122
|
provenanceParts
|
|
2099
2123
|
);
|
|
2100
2124
|
flushSteerAnchor(formattedMessage);
|
|
2101
|
-
|
|
2125
|
+
emitFormattedMessage(formattedMessage);
|
|
2102
2126
|
|
|
2103
2127
|
// Update the index mapping for this message
|
|
2104
2128
|
indexMapping[i] = [messages.length - 1];
|
|
@@ -2359,7 +2383,7 @@ export const formatAgentMessages = (
|
|
|
2359
2383
|
flushSteerAnchor(formattedMessages[0]);
|
|
2360
2384
|
}
|
|
2361
2385
|
for (const formattedMessage of formattedMessages) {
|
|
2362
|
-
|
|
2386
|
+
emitFormattedMessage(formattedMessage);
|
|
2363
2387
|
}
|
|
2364
2388
|
if (endsWithSteerMessage(formattedMessages)) {
|
|
2365
2389
|
pendingSteerAnchor = true;
|
|
@@ -2390,7 +2414,7 @@ export const formatAgentMessages = (
|
|
|
2390
2414
|
'user'
|
|
2391
2415
|
);
|
|
2392
2416
|
stampSourceMessageIdentity(skillMessage, undefined, 0, 'synthetic');
|
|
2393
|
-
|
|
2417
|
+
emitFormattedMessage(skillMessage);
|
|
2394
2418
|
}
|
|
2395
2419
|
}
|
|
2396
2420
|
}
|
package/src/messages/index.ts
CHANGED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
import { inspectProviderMessageProvenance } from './provenance';
|
|
3
|
+
|
|
4
|
+
export type ProviderMessageProjectionInvariantMode =
|
|
5
|
+
| 'off'
|
|
6
|
+
| 'observe'
|
|
7
|
+
| 'assert';
|
|
8
|
+
|
|
9
|
+
export type ProviderMessageProjectionIssueCode =
|
|
10
|
+
| 'absent_provenance'
|
|
11
|
+
| 'invalid_provenance'
|
|
12
|
+
| 'unsourced_non_synthetic_part';
|
|
13
|
+
|
|
14
|
+
export interface ProviderMessageProjectionInvariantIssue {
|
|
15
|
+
readonly code: ProviderMessageProjectionIssueCode;
|
|
16
|
+
readonly messageIndex: number;
|
|
17
|
+
readonly messageType: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ProviderMessageProjectionInvariantReport {
|
|
21
|
+
readonly valid: boolean;
|
|
22
|
+
readonly messageCount: number;
|
|
23
|
+
readonly sourceBackedMessageCount: number;
|
|
24
|
+
readonly syntheticMessageCount: number;
|
|
25
|
+
readonly gapMessageCount: number;
|
|
26
|
+
readonly issues: readonly ProviderMessageProjectionInvariantIssue[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MAX_REPORTED_PROJECTION_ISSUES = 64;
|
|
30
|
+
|
|
31
|
+
function getMessageType(message: BaseMessage): string {
|
|
32
|
+
try {
|
|
33
|
+
return typeof message.type === 'string' ? message.type : 'unknown';
|
|
34
|
+
} catch {
|
|
35
|
+
return 'unknown';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function appendIssue(
|
|
40
|
+
issues: ProviderMessageProjectionInvariantIssue[],
|
|
41
|
+
code: ProviderMessageProjectionIssueCode,
|
|
42
|
+
messageIndex: number,
|
|
43
|
+
messageType: string
|
|
44
|
+
): void {
|
|
45
|
+
if (issues.length >= MAX_REPORTED_PROJECTION_ISSUES) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
issues.push(Object.freeze({ code, messageIndex, messageType }));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Resolves the opt-in provider projection invariant without enabling it for
|
|
52
|
+
* unrecognized values. */
|
|
53
|
+
export function resolveProviderMessageProjectionInvariantMode(
|
|
54
|
+
value = process.env.AGENT_MESSAGE_PROJECTION_INVARIANT
|
|
55
|
+
): ProviderMessageProjectionInvariantMode {
|
|
56
|
+
if (value === 'observe' || value === 'assert') {
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
return 'off';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Inspects provider-bound lineage without reading message content or source ids. */
|
|
63
|
+
export function inspectProviderMessageProjection(
|
|
64
|
+
messages: readonly BaseMessage[]
|
|
65
|
+
): ProviderMessageProjectionInvariantReport {
|
|
66
|
+
const issues: ProviderMessageProjectionInvariantIssue[] = [];
|
|
67
|
+
let sourceBackedMessageCount = 0;
|
|
68
|
+
let syntheticMessageCount = 0;
|
|
69
|
+
let gapMessageCount = 0;
|
|
70
|
+
|
|
71
|
+
for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
|
|
72
|
+
const message = messages[messageIndex];
|
|
73
|
+
const messageType = getMessageType(message);
|
|
74
|
+
const state = inspectProviderMessageProvenance(message);
|
|
75
|
+
if (state.status !== 'valid') {
|
|
76
|
+
gapMessageCount++;
|
|
77
|
+
appendIssue(
|
|
78
|
+
issues,
|
|
79
|
+
state.status === 'absent'
|
|
80
|
+
? 'absent_provenance'
|
|
81
|
+
: 'invalid_provenance',
|
|
82
|
+
messageIndex,
|
|
83
|
+
messageType
|
|
84
|
+
);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let hasSourceBackedPart = false;
|
|
89
|
+
let hasGap = false;
|
|
90
|
+
for (const part of state.provenance.parts) {
|
|
91
|
+
if (part.sourceMessageId != null) {
|
|
92
|
+
hasSourceBackedPart = true;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (part.attribution === 'synthetic') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
hasGap = true;
|
|
99
|
+
appendIssue(
|
|
100
|
+
issues,
|
|
101
|
+
'unsourced_non_synthetic_part',
|
|
102
|
+
messageIndex,
|
|
103
|
+
messageType
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (hasGap) {
|
|
108
|
+
gapMessageCount++;
|
|
109
|
+
} else if (hasSourceBackedPart) {
|
|
110
|
+
sourceBackedMessageCount++;
|
|
111
|
+
} else {
|
|
112
|
+
syntheticMessageCount++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return Object.freeze({
|
|
117
|
+
valid: gapMessageCount === 0,
|
|
118
|
+
messageCount: messages.length,
|
|
119
|
+
sourceBackedMessageCount,
|
|
120
|
+
syntheticMessageCount,
|
|
121
|
+
gapMessageCount,
|
|
122
|
+
issues: Object.freeze(issues),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export class ProviderMessageProjectionInvariantError extends Error {
|
|
127
|
+
readonly report: ProviderMessageProjectionInvariantReport;
|
|
128
|
+
|
|
129
|
+
constructor(report: ProviderMessageProjectionInvariantReport) {
|
|
130
|
+
super('Provider message projection invariant failed');
|
|
131
|
+
this.name = 'ProviderMessageProjectionInvariantError';
|
|
132
|
+
this.report = report;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -766,11 +766,14 @@ function publishProviderMessageProvenance(
|
|
|
766
766
|
}
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
769
|
+
interface ResolvedProvenancePublication {
|
|
770
|
+
provenance: ProviderMessageProvenance;
|
|
771
|
+
sourceMessageIds?: readonly string[];
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function resolveProvenancePublication(
|
|
772
775
|
parts: readonly ProviderMessageProvenancePart[]
|
|
773
|
-
):
|
|
776
|
+
): ResolvedProvenancePublication {
|
|
774
777
|
const normalizedParts = normalizeProvenanceParts(parts);
|
|
775
778
|
const provenance: ProviderMessageProvenance = Object.freeze({
|
|
776
779
|
version: PROVIDER_MESSAGE_PROVENANCE_VERSION,
|
|
@@ -782,21 +785,60 @@ export function setProviderMessageProvenance(
|
|
|
782
785
|
for (const part of normalizedParts) {
|
|
783
786
|
appendSourceMessageId(sourceMessageIds, seen, part.sourceMessageId);
|
|
784
787
|
}
|
|
785
|
-
if (sourceMessageIds.length
|
|
786
|
-
|
|
787
|
-
immutableProviderSourceMessageIds.add(immutableSourceMessageIds);
|
|
788
|
-
immutableProviderMessageSourceIds.set(
|
|
789
|
-
provenance,
|
|
790
|
-
immutableSourceMessageIds
|
|
791
|
-
);
|
|
792
|
-
publishProviderMessageProvenance(
|
|
793
|
-
message,
|
|
794
|
-
provenance,
|
|
795
|
-
immutableSourceMessageIds
|
|
796
|
-
);
|
|
797
|
-
return;
|
|
788
|
+
if (sourceMessageIds.length === 0) {
|
|
789
|
+
return { provenance };
|
|
798
790
|
}
|
|
799
|
-
|
|
791
|
+
const immutableSourceMessageIds = Object.freeze(sourceMessageIds);
|
|
792
|
+
immutableProviderSourceMessageIds.add(immutableSourceMessageIds);
|
|
793
|
+
immutableProviderMessageSourceIds.set(provenance, immutableSourceMessageIds);
|
|
794
|
+
return { provenance, sourceMessageIds: immutableSourceMessageIds };
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** Replaces typed provenance and synchronizes its stable plural source ids. */
|
|
798
|
+
export function setProviderMessageProvenance(
|
|
799
|
+
message: BaseMessage,
|
|
800
|
+
parts: readonly ProviderMessageProvenancePart[]
|
|
801
|
+
): void {
|
|
802
|
+
const resolved = resolveProvenancePublication(parts);
|
|
803
|
+
publishProviderMessageProvenance(
|
|
804
|
+
message,
|
|
805
|
+
resolved.provenance,
|
|
806
|
+
resolved.sourceMessageIds
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* {@link setProviderMessageProvenance} for a message the caller itself just
|
|
812
|
+
* constructed from locally built plain objects. Such a message cannot carry
|
|
813
|
+
* proxies, accessors, or foreign aliases in `additional_kwargs`/`lc_kwargs`,
|
|
814
|
+
* so the publication skips the hardened descriptor walks while producing the
|
|
815
|
+
* same end state: fresh replacement objects on both slots, with the
|
|
816
|
+
* serialization mirror aliasing the live kwargs. Never call this with a
|
|
817
|
+
* message received across a seam — that is what the hardened variant is for.
|
|
818
|
+
*/
|
|
819
|
+
export function setFreshProviderMessageProvenance(
|
|
820
|
+
message: BaseMessage,
|
|
821
|
+
parts: readonly ProviderMessageProvenancePart[]
|
|
822
|
+
): void {
|
|
823
|
+
const resolved = resolveProvenancePublication(parts);
|
|
824
|
+
const replacement: UntrustedProviderMessageAdditionalKwargs = {
|
|
825
|
+
...message.additional_kwargs,
|
|
826
|
+
};
|
|
827
|
+
replacement.provenance = resolved.provenance;
|
|
828
|
+
const sourceMessageIds = resolved.sourceMessageIds;
|
|
829
|
+
if (sourceMessageIds != null && sourceMessageIds.length > 0) {
|
|
830
|
+
replacement.sourceMessageIds = sourceMessageIds;
|
|
831
|
+
replacement.sourceMessageId = sourceMessageIds[sourceMessageIds.length - 1];
|
|
832
|
+
} else {
|
|
833
|
+
delete replacement.sourceMessageIds;
|
|
834
|
+
delete replacement.sourceMessageId;
|
|
835
|
+
}
|
|
836
|
+
const serializedReplacement: BaseMessage['lc_kwargs'] = {
|
|
837
|
+
...message.lc_kwargs,
|
|
838
|
+
};
|
|
839
|
+
serializedReplacement.additional_kwargs = replacement;
|
|
840
|
+
message.additional_kwargs = replacement;
|
|
841
|
+
message.lc_kwargs = serializedReplacement;
|
|
800
842
|
}
|
|
801
843
|
|
|
802
844
|
/** Publishes the canonical fail-closed marker for malformed provenance. */
|