@librechat/agents 3.8.0 → 3.8.2
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 +13 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +176 -55
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +16 -6
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/hitl/approvalReview.cjs +89 -0
- package/dist/cjs/hitl/approvalReview.cjs.map +1 -0
- package/dist/cjs/langfuse.cjs +42 -0
- package/dist/cjs/langfuse.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +42 -0
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/langfuseTraceShaping.cjs +7 -1
- package/dist/cjs/langfuseTraceShaping.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +6 -10
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +2 -7
- package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/contextPressureMeter.cjs +51 -10
- package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +2 -2
- package/dist/cjs/llm/openai/index.cjs +4 -3
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +5 -4
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/preempt.cjs +3 -2
- package/dist/cjs/llm/preempt.cjs.map +1 -1
- package/dist/cjs/llm/prepareProviderRequest.cjs +9 -7
- package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -1
- package/dist/cjs/llm/providers.cjs +1 -1
- package/dist/cjs/main.cjs +14 -8
- package/dist/cjs/messages/alternation.cjs +1 -5
- package/dist/cjs/messages/alternation.cjs.map +1 -1
- package/dist/cjs/messages/budget.cjs +206 -7
- package/dist/cjs/messages/budget.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +3 -9
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +26 -38
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +132 -43
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/index.cjs +2 -0
- package/dist/cjs/messages/prune.cjs +14 -9
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/reasoningTypes.cjs +21 -0
- package/dist/cjs/messages/reasoningTypes.cjs.map +1 -0
- package/dist/cjs/messages/recency.cjs +15 -94
- package/dist/cjs/messages/recency.cjs.map +1 -1
- package/dist/cjs/messages/toolHistoryProjection.cjs +243 -0
- package/dist/cjs/messages/toolHistoryProjection.cjs.map +1 -0
- package/dist/cjs/messages/toolResultTypes.cjs +145 -4
- package/dist/cjs/messages/toolResultTypes.cjs.map +1 -1
- package/dist/cjs/run.cjs +22 -10
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +7 -5
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/session/JsonlSessionStore.cjs +3 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -1
- package/dist/cjs/session/index.cjs +1 -1
- package/dist/cjs/session/sessionProjection.cjs +75 -0
- package/dist/cjs/session/sessionProjection.cjs.map +1 -0
- package/dist/cjs/stream.cjs +20 -19
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/index.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +19 -9
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/summarization/shared.cjs +9 -0
- package/dist/cjs/summarization/shared.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +256 -84
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/local/CompileCheckTool.cjs +1 -1
- package/dist/cjs/tools/local/LocalCodingTools.cjs +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +24 -9
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentReplay.cjs +2 -0
- package/dist/cjs/tools/subagent/SubagentReplay.cjs.map +1 -1
- package/dist/cjs/tools/toolBatchReplay.cjs +187 -0
- package/dist/cjs/tools/toolBatchReplay.cjs.map +1 -0
- package/dist/cjs/tools/toolOutputReferences.cjs +12 -0
- package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
- package/dist/cjs/types/hitl.cjs +4 -0
- package/dist/cjs/types/hitl.cjs.map +1 -1
- package/dist/cjs/utils/events.cjs +13 -0
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +105 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +13 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +178 -57
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +16 -6
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/hitl/approvalReview.mjs +83 -0
- package/dist/esm/hitl/approvalReview.mjs.map +1 -0
- package/dist/esm/langfuse.mjs +43 -2
- package/dist/esm/langfuse.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +41 -1
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/langfuseTraceShaping.mjs +7 -1
- package/dist/esm/langfuseTraceShaping.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +6 -10
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs +2 -7
- package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/contextPressureMeter.mjs +52 -11
- package/dist/esm/llm/contextPressureMeter.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +2 -2
- package/dist/esm/llm/openai/index.mjs +2 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +5 -4
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/preempt.mjs +2 -1
- package/dist/esm/llm/preempt.mjs.map +1 -1
- package/dist/esm/llm/prepareProviderRequest.mjs +9 -7
- package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -1
- package/dist/esm/llm/providers.mjs +1 -1
- package/dist/esm/main.mjs +13 -10
- package/dist/esm/messages/alternation.mjs +2 -6
- package/dist/esm/messages/alternation.mjs.map +1 -1
- package/dist/esm/messages/budget.mjs +206 -8
- package/dist/esm/messages/budget.mjs.map +1 -1
- package/dist/esm/messages/cache.mjs +3 -9
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +24 -34
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +133 -44
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/index.mjs +2 -0
- package/dist/esm/messages/prune.mjs +14 -9
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/reasoningTypes.mjs +20 -0
- package/dist/esm/messages/reasoningTypes.mjs.map +1 -0
- package/dist/esm/messages/recency.mjs +16 -95
- package/dist/esm/messages/recency.mjs.map +1 -1
- package/dist/esm/messages/toolHistoryProjection.mjs +237 -0
- package/dist/esm/messages/toolHistoryProjection.mjs.map +1 -0
- package/dist/esm/messages/toolResultTypes.mjs +141 -4
- package/dist/esm/messages/toolResultTypes.mjs.map +1 -1
- package/dist/esm/run.mjs +23 -11
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +7 -5
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/session/JsonlSessionStore.mjs +3 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -1
- package/dist/esm/session/index.mjs +1 -1
- package/dist/esm/session/sessionProjection.mjs +72 -0
- package/dist/esm/session/sessionProjection.mjs.map +1 -0
- package/dist/esm/stream.mjs +17 -16
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/index.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +20 -10
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/summarization/shared.mjs +9 -1
- package/dist/esm/summarization/shared.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +256 -84
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/local/CompileCheckTool.mjs +1 -1
- package/dist/esm/tools/local/LocalCodingTools.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +24 -9
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs +1 -1
- package/dist/esm/tools/subagent/SubagentReplay.mjs.map +1 -1
- package/dist/esm/tools/toolBatchReplay.mjs +176 -0
- package/dist/esm/tools/toolBatchReplay.mjs.map +1 -0
- package/dist/esm/tools/toolOutputReferences.mjs +12 -0
- package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
- package/dist/esm/types/hitl.mjs +4 -1
- package/dist/esm/types/hitl.mjs.map +1 -1
- package/dist/esm/utils/events.mjs +13 -0
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +105 -1
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +13 -1
- package/dist/types/graphs/Graph.d.ts +27 -0
- package/dist/types/hitl/approvalReview.d.ts +32 -0
- package/dist/types/hooks/types.d.ts +4 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/langfuse.d.ts +5 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +2 -0
- package/dist/types/llm/contextPressureMeter.d.ts +2 -1
- package/dist/types/llm/prepareProviderRequest.d.ts +7 -1
- package/dist/types/messages/budget.d.ts +15 -10
- package/dist/types/messages/core.d.ts +8 -17
- package/dist/types/messages/format.d.ts +3 -2
- package/dist/types/messages/index.d.ts +1 -1
- package/dist/types/messages/prune.d.ts +1 -1
- package/dist/types/messages/reasoningTypes.d.ts +7 -0
- package/dist/types/messages/recency.d.ts +3 -0
- package/dist/types/messages/toolHistoryProjection.d.ts +65 -0
- package/dist/types/messages/toolResultTypes.d.ts +31 -1
- package/dist/types/session/sessionProjection.d.ts +10 -0
- package/dist/types/summarization/index.d.ts +2 -1
- package/dist/types/summarization/node.d.ts +1 -0
- package/dist/types/summarization/shared.d.ts +12 -0
- package/dist/types/tools/ToolNode.d.ts +12 -6
- package/dist/types/tools/subagent/SubagentReplay.d.ts +2 -0
- package/dist/types/tools/toolBatchReplay.d.ts +54 -0
- package/dist/types/tools/toolOutputReferences.d.ts +2 -0
- package/dist/types/types/graph.d.ts +20 -0
- package/dist/types/types/run.d.ts +4 -0
- package/dist/types/types/summarize.d.ts +4 -1
- package/dist/types/utils/tokens.d.ts +2 -1
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +34 -3
- package/src/graphs/Graph.ts +465 -147
- package/src/graphs/MultiAgentGraph.ts +28 -6
- package/src/hitl/approvalReview.ts +209 -0
- package/src/hooks/types.ts +4 -1
- package/src/index.ts +1 -0
- package/src/langfuse.ts +67 -9
- package/src/langfuseToolOutputTracing.ts +91 -0
- package/src/langfuseTraceShaping.ts +17 -4
- package/src/llm/anthropic/utils/message_inputs.ts +5 -10
- package/src/llm/bedrock/utils/message_inputs.ts +2 -8
- package/src/llm/contextPressureMeter.ts +80 -12
- package/src/llm/openai/utils/index.ts +5 -4
- package/src/llm/prepareProviderRequest.ts +21 -16
- package/src/messages/alternation.ts +2 -15
- package/src/messages/budget.ts +439 -23
- package/src/messages/cache.ts +8 -9
- package/src/messages/core.ts +65 -95
- package/src/messages/format.ts +262 -78
- package/src/messages/index.ts +1 -1
- package/src/messages/prune.ts +31 -12
- package/src/messages/reasoningTypes.ts +23 -0
- package/src/messages/recency.ts +35 -179
- package/src/messages/toolHistoryProjection.ts +460 -0
- package/src/messages/toolResultTypes.ts +331 -100
- package/src/run.ts +44 -22
- package/src/session/AgentSession.ts +33 -16
- package/src/session/JsonlSessionStore.ts +6 -0
- package/src/session/sessionProjection.ts +126 -0
- package/src/stream.ts +14 -19
- package/src/summarization/index.ts +2 -0
- package/src/summarization/node.ts +62 -13
- package/src/summarization/shared.ts +23 -0
- package/src/tools/ToolNode.ts +518 -184
- package/src/tools/subagent/SubagentExecutor.ts +35 -6
- package/src/tools/subagent/SubagentReplay.ts +2 -2
- package/src/tools/toolBatchReplay.ts +395 -0
- package/src/tools/toolOutputReferences.ts +16 -0
- package/src/types/graph.ts +20 -0
- package/src/types/run.ts +4 -0
- package/src/types/summarize.ts +4 -1
- package/src/utils/events.ts +19 -0
- package/src/utils/tokens.ts +183 -0
|
@@ -1267,6 +1267,11 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1267
1267
|
reducer: (_current, update) => update,
|
|
1268
1268
|
default: () => undefined,
|
|
1269
1269
|
}),
|
|
1270
|
+
/** Surfaced from the summarizing agent's subgraph on a summarize-only run. */
|
|
1271
|
+
manualSummary: Annotation<string | undefined>({
|
|
1272
|
+
reducer: (_current, update) => update,
|
|
1273
|
+
default: () => undefined,
|
|
1274
|
+
}),
|
|
1270
1275
|
runStepState: this.createRunStepStateAnnotation(),
|
|
1271
1276
|
});
|
|
1272
1277
|
|
|
@@ -1281,14 +1286,29 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1281
1286
|
builder.addEdge(source, destination);
|
|
1282
1287
|
};
|
|
1283
1288
|
|
|
1289
|
+
/**
|
|
1290
|
+
* A summarize-only run compiles to the opted-in agent alone: no other
|
|
1291
|
+
* node, no handoff or direct edge, START → agent → END. Anything else
|
|
1292
|
+
* would run the summarizer twice (once from START, once through its
|
|
1293
|
+
* predecessor) or append routing prompts to the checkpoint it just
|
|
1294
|
+
* produced.
|
|
1295
|
+
*/
|
|
1296
|
+
const summarizeOnlyAgentId = this.summarizeOnlyAgentId;
|
|
1297
|
+
const agentIds =
|
|
1298
|
+
summarizeOnlyAgentId != null
|
|
1299
|
+
? [summarizeOnlyAgentId]
|
|
1300
|
+
: [...this.agentContexts.keys()];
|
|
1301
|
+
const handoffEdges = summarizeOnlyAgentId != null ? [] : this.handoffEdges;
|
|
1302
|
+
const directEdges = summarizeOnlyAgentId != null ? [] : this.directEdges;
|
|
1303
|
+
|
|
1284
1304
|
// Add all agents as complete subgraphs
|
|
1285
|
-
for (const
|
|
1305
|
+
for (const agentId of agentIds) {
|
|
1286
1306
|
// Get all possible destinations for this agent
|
|
1287
1307
|
const handoffDestinations = new Set<string>();
|
|
1288
1308
|
const directDestinations = new Set<string>();
|
|
1289
1309
|
|
|
1290
1310
|
// Check handoff edges for destinations
|
|
1291
|
-
for (const edge of
|
|
1311
|
+
for (const edge of handoffEdges) {
|
|
1292
1312
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
1293
1313
|
if (sources.includes(agentId) === true) {
|
|
1294
1314
|
const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
@@ -1297,7 +1317,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1297
1317
|
}
|
|
1298
1318
|
|
|
1299
1319
|
// Check direct edges for destinations
|
|
1300
|
-
for (const edge of
|
|
1320
|
+
for (const edge of directEdges) {
|
|
1301
1321
|
const sources = Array.isArray(edge.from) ? edge.from : [edge.from];
|
|
1302
1322
|
if (sources.includes(agentId) === true) {
|
|
1303
1323
|
const dests = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
@@ -1500,6 +1520,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1500
1520
|
} else {
|
|
1501
1521
|
result = await agentSubgraph.invoke(state, memberConfig);
|
|
1502
1522
|
}
|
|
1523
|
+
result = this.propagateManualCompaction(result);
|
|
1503
1524
|
|
|
1504
1525
|
if (this.resultAgentId === agentId) {
|
|
1505
1526
|
result = {
|
|
@@ -1560,8 +1581,9 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1560
1581
|
});
|
|
1561
1582
|
}
|
|
1562
1583
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1584
|
+
const startingNodes =
|
|
1585
|
+
summarizeOnlyAgentId != null ? [summarizeOnlyAgentId] : this.startingNodes;
|
|
1586
|
+
for (const startNode of startingNodes) {
|
|
1565
1587
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1566
1588
|
/** @ts-ignore */
|
|
1567
1589
|
builder.addEdge(START, startNode);
|
|
@@ -1573,7 +1595,7 @@ export class MultiAgentGraph extends StandardGraph {
|
|
|
1573
1595
|
*/
|
|
1574
1596
|
const edgesByDestination = new Map<string, t.GraphEdge[]>();
|
|
1575
1597
|
|
|
1576
|
-
for (const edge of
|
|
1598
|
+
for (const edge of directEdges) {
|
|
1577
1599
|
const destinations = Array.isArray(edge.to) ? edge.to : [edge.to];
|
|
1578
1600
|
for (const destination of destinations) {
|
|
1579
1601
|
if (!edgesByDestination.has(destination)) {
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
2
|
+
import type {
|
|
3
|
+
ToolApprovalInterruptPayload,
|
|
4
|
+
ToolApprovalRequest,
|
|
5
|
+
ToolApprovalReviewConfig,
|
|
6
|
+
} from '@/types/hitl';
|
|
7
|
+
import { stableStringify } from '@/tools/eagerEventExecution';
|
|
8
|
+
import { isToolApprovalInterrupt } from '@/types/hitl';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Private resume-only config entry populated from the checkpointed interrupt.
|
|
12
|
+
* Hosts must never need to construct or inspect this value.
|
|
13
|
+
*/
|
|
14
|
+
export const TOOL_APPROVAL_REVIEW_CONFIG_KEY =
|
|
15
|
+
'__librechat_tool_approval_review';
|
|
16
|
+
|
|
17
|
+
export interface ToolApprovalReviewEvidence {
|
|
18
|
+
interruptId: string;
|
|
19
|
+
payload: ToolApprovalInterruptPayload;
|
|
20
|
+
owner?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ReviewedToolApproval {
|
|
24
|
+
request: ToolApprovalRequest;
|
|
25
|
+
reviewConfig: ToolApprovalReviewConfig;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const APPROVAL_DECISIONS = new Set(['approve', 'reject', 'edit', 'respond']);
|
|
29
|
+
|
|
30
|
+
/** Detach approval payloads from host- or transport-owned object graphs. */
|
|
31
|
+
export function cloneToolApprovalInterruptPayload<T>(payload: T): T {
|
|
32
|
+
return isToolApprovalInterrupt(payload) ? structuredClone(payload) : payload;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
36
|
+
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function hasValidApprovalShape(payload: ToolApprovalInterruptPayload): boolean {
|
|
40
|
+
if (
|
|
41
|
+
!Array.isArray(payload.action_requests) ||
|
|
42
|
+
!Array.isArray(payload.review_configs) ||
|
|
43
|
+
payload.action_requests.length !== payload.review_configs.length
|
|
44
|
+
) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const toolCallIds = new Set<string>();
|
|
49
|
+
return payload.action_requests.every((request, index) => {
|
|
50
|
+
const reviewConfig = payload.review_configs[index];
|
|
51
|
+
if (!isRecord(request) || !isRecord(reviewConfig)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const toolCallId = request.tool_call_id;
|
|
55
|
+
const toolName = request.name;
|
|
56
|
+
const allowedDecisions = reviewConfig.allowed_decisions;
|
|
57
|
+
if (
|
|
58
|
+
typeof toolCallId !== 'string' ||
|
|
59
|
+
toolCallId.length === 0 ||
|
|
60
|
+
toolCallIds.has(toolCallId) ||
|
|
61
|
+
typeof toolName !== 'string' ||
|
|
62
|
+
toolName.length === 0 ||
|
|
63
|
+
!isRecord(request.arguments) ||
|
|
64
|
+
(request.description != null &&
|
|
65
|
+
typeof request.description !== 'string') ||
|
|
66
|
+
reviewConfig.tool_call_id !== toolCallId ||
|
|
67
|
+
reviewConfig.action_name !== toolName ||
|
|
68
|
+
!Array.isArray(allowedDecisions) ||
|
|
69
|
+
!allowedDecisions.every(
|
|
70
|
+
(decision) =>
|
|
71
|
+
typeof decision === 'string' && APPROVAL_DECISIONS.has(decision)
|
|
72
|
+
)
|
|
73
|
+
) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
toolCallIds.add(toolCallId);
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Build trusted review evidence from the interrupt restored by `Run`. */
|
|
82
|
+
export function createToolApprovalReviewEvidence(
|
|
83
|
+
interruptId: string | undefined,
|
|
84
|
+
payload: unknown,
|
|
85
|
+
owner?: string
|
|
86
|
+
): ToolApprovalReviewEvidence | undefined {
|
|
87
|
+
if (
|
|
88
|
+
typeof interruptId !== 'string' ||
|
|
89
|
+
interruptId.length === 0 ||
|
|
90
|
+
!isToolApprovalInterrupt(payload) ||
|
|
91
|
+
!hasValidApprovalShape(payload)
|
|
92
|
+
) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
interruptId,
|
|
97
|
+
payload: cloneToolApprovalInterruptPayload(payload),
|
|
98
|
+
...(owner == null ? {} : { owner }),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Read only well-shaped evidence from a ToolNode's runnable config. */
|
|
103
|
+
export function getToolApprovalReviewEvidence(
|
|
104
|
+
config: RunnableConfig,
|
|
105
|
+
owner?: string
|
|
106
|
+
): ToolApprovalReviewEvidence | undefined {
|
|
107
|
+
const candidate = config.configurable?.[TOOL_APPROVAL_REVIEW_CONFIG_KEY];
|
|
108
|
+
if (candidate == null || typeof candidate !== 'object') {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const {
|
|
112
|
+
interruptId,
|
|
113
|
+
payload,
|
|
114
|
+
owner: evidenceOwner,
|
|
115
|
+
} = candidate as {
|
|
116
|
+
interruptId?: unknown;
|
|
117
|
+
payload?: unknown;
|
|
118
|
+
owner?: unknown;
|
|
119
|
+
};
|
|
120
|
+
if (
|
|
121
|
+
evidenceOwner != null &&
|
|
122
|
+
(typeof evidenceOwner !== 'string' ||
|
|
123
|
+
(owner != null && owner !== evidenceOwner))
|
|
124
|
+
) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
return createToolApprovalReviewEvidence(
|
|
128
|
+
typeof interruptId === 'string' ? interruptId : undefined,
|
|
129
|
+
payload,
|
|
130
|
+
typeof evidenceOwner === 'string' ? evidenceOwner : undefined
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function getReviewedToolApproval(
|
|
135
|
+
payload: ToolApprovalInterruptPayload | undefined,
|
|
136
|
+
toolCallId: string | undefined
|
|
137
|
+
): ReviewedToolApproval | undefined {
|
|
138
|
+
if (payload == null || toolCallId == null || toolCallId === '') {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
const request = payload.action_requests.find(
|
|
142
|
+
(candidate) => candidate.tool_call_id === toolCallId
|
|
143
|
+
);
|
|
144
|
+
const reviewConfig = payload.review_configs.find(
|
|
145
|
+
(candidate) => candidate.tool_call_id === toolCallId
|
|
146
|
+
);
|
|
147
|
+
if (request == null || reviewConfig == null) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
return { request, reviewConfig };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function decisionsEqual(
|
|
154
|
+
left: ReadonlyArray<string>,
|
|
155
|
+
right: ReadonlyArray<string>
|
|
156
|
+
): boolean {
|
|
157
|
+
if (left.length !== right.length) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const sortedLeft = [...left].sort();
|
|
161
|
+
const sortedRight = [...right].sort();
|
|
162
|
+
return sortedLeft.every((value, index) => value === sortedRight[index]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Bind a resume decision to the exact proposal the reviewer saw. Description
|
|
167
|
+
* text is deliberately excluded: it explains policy but cannot change the
|
|
168
|
+
* side effect. Tool identity, normalized arguments and available decisions
|
|
169
|
+
* are execution-authoritative.
|
|
170
|
+
*/
|
|
171
|
+
export function toolApprovalProposalMatches(
|
|
172
|
+
current: ReviewedToolApproval,
|
|
173
|
+
reviewed: ReviewedToolApproval
|
|
174
|
+
): boolean {
|
|
175
|
+
return (
|
|
176
|
+
current.request.tool_call_id === reviewed.request.tool_call_id &&
|
|
177
|
+
current.request.name === reviewed.request.name &&
|
|
178
|
+
current.reviewConfig.tool_call_id === reviewed.reviewConfig.tool_call_id &&
|
|
179
|
+
current.reviewConfig.action_name === reviewed.reviewConfig.action_name &&
|
|
180
|
+
stableStringify(current.request.arguments) ===
|
|
181
|
+
stableStringify(reviewed.request.arguments) &&
|
|
182
|
+
decisionsEqual(
|
|
183
|
+
current.reviewConfig.allowed_decisions,
|
|
184
|
+
reviewed.reviewConfig.allowed_decisions
|
|
185
|
+
)
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Preserve batch order as part of the decision-to-request binding. */
|
|
190
|
+
export function toolApprovalPayloadMatches(
|
|
191
|
+
current: ToolApprovalInterruptPayload,
|
|
192
|
+
reviewed: ToolApprovalInterruptPayload
|
|
193
|
+
): boolean {
|
|
194
|
+
if (
|
|
195
|
+
current.action_requests.length !== reviewed.action_requests.length ||
|
|
196
|
+
current.review_configs.length !== reviewed.review_configs.length
|
|
197
|
+
) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
return current.action_requests.every((request, index) => {
|
|
201
|
+
const currentReviewConfig = current.review_configs[index];
|
|
202
|
+
const reviewedRequest = reviewed.action_requests[index];
|
|
203
|
+
const reviewedReviewConfig = reviewed.review_configs[index];
|
|
204
|
+
return toolApprovalProposalMatches(
|
|
205
|
+
{ request, reviewConfig: currentReviewConfig },
|
|
206
|
+
{ request: reviewedRequest, reviewConfig: reviewedReviewConfig }
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
}
|
package/src/hooks/types.ts
CHANGED
|
@@ -252,13 +252,16 @@ export interface PreCompactHookInput extends BaseHookInput {
|
|
|
252
252
|
/**
|
|
253
253
|
* What triggered compaction. Matches `SummarizationTrigger.type` from the
|
|
254
254
|
* agent's summarization config. `'default'` means no trigger was
|
|
255
|
-
* configured and compaction fired because messages were pruned
|
|
255
|
+
* configured and compaction fired because messages were pruned;
|
|
256
|
+
* `'manual'` means the host asked for the summary outright and no trigger
|
|
257
|
+
* was evaluated.
|
|
256
258
|
*/
|
|
257
259
|
trigger:
|
|
258
260
|
| 'token_ratio'
|
|
259
261
|
| 'remaining_tokens'
|
|
260
262
|
| 'messages_to_refine'
|
|
261
263
|
| 'default'
|
|
264
|
+
| 'manual'
|
|
262
265
|
| (string & {});
|
|
263
266
|
}
|
|
264
267
|
|
package/src/index.ts
CHANGED
package/src/langfuse.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tool } from '@langchain/core/tools';
|
|
1
2
|
import { CallbackHandler } from '@langfuse/langchain';
|
|
2
3
|
import { LangfuseOtelContextKeys } from '@langfuse/core';
|
|
3
4
|
import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
|
|
@@ -18,6 +19,7 @@ import type {
|
|
|
18
19
|
LLMResult,
|
|
19
20
|
} from '@langchain/core/outputs';
|
|
20
21
|
import type { PropagateAttributesParams } from '@langfuse/tracing';
|
|
22
|
+
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
21
23
|
import type { Context, SpanContext } from '@opentelemetry/api';
|
|
22
24
|
import type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';
|
|
23
25
|
import type * as t from '@/types';
|
|
@@ -39,10 +41,15 @@ import {
|
|
|
39
41
|
registerLangfuseManagedSpan,
|
|
40
42
|
resolveLangfuseDestinationKey,
|
|
41
43
|
} from '@/langfuseSpanRegistry';
|
|
44
|
+
import {
|
|
45
|
+
getToolObservationMetadata,
|
|
46
|
+
LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,
|
|
47
|
+
} from '@/langfuseToolOutputTracing';
|
|
42
48
|
import {
|
|
43
49
|
readPreemptRestartedRun,
|
|
44
50
|
PREEMPT_RESTART_CONTROL_FLOW,
|
|
45
51
|
} from '@/llm/preempt';
|
|
52
|
+
import { filterCallbacks, findCallback } from '@/utils/callbacks';
|
|
46
53
|
import { isPresent, parseBooleanEnv } from '@/utils/misc';
|
|
47
54
|
|
|
48
55
|
export {
|
|
@@ -306,10 +313,10 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
306
313
|
private deferredRootStarted = false;
|
|
307
314
|
private deferredRootOutcome:
|
|
308
315
|
| {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
316
|
+
type: 'end';
|
|
317
|
+
output: Parameters<CallbackHandler['handleChainEnd']>[0];
|
|
318
|
+
parentRunId?: string;
|
|
319
|
+
}
|
|
313
320
|
| { type: 'error'; error: Error; parentRunId?: string }
|
|
314
321
|
| undefined;
|
|
315
322
|
|
|
@@ -615,11 +622,7 @@ class ScopedLangfuseCallbackHandler extends CallbackHandler {
|
|
|
615
622
|
this.deferredRootStarted = false;
|
|
616
623
|
this.deferredRootOutcome = undefined;
|
|
617
624
|
if (outcome.type === 'error') {
|
|
618
|
-
await super.handleChainError(
|
|
619
|
-
outcome.error,
|
|
620
|
-
runId,
|
|
621
|
-
outcome.parentRunId
|
|
622
|
-
);
|
|
625
|
+
await super.handleChainError(outcome.error, runId, outcome.parentRunId);
|
|
623
626
|
return;
|
|
624
627
|
}
|
|
625
628
|
await super.handleChainEnd(outcome.output, runId, outcome.parentRunId);
|
|
@@ -1023,6 +1026,61 @@ export function isLangfuseCallbackHandler(value: unknown): boolean {
|
|
|
1023
1026
|
return value instanceof CallbackHandler;
|
|
1024
1027
|
}
|
|
1025
1028
|
|
|
1029
|
+
/** Host results have passed host output filters. Record only reserved metadata,
|
|
1030
|
+
* not arguments, rows, errors, or attachments. These completion observations
|
|
1031
|
+
* do not measure tool latency; execution timing belongs in the metadata. */
|
|
1032
|
+
export async function traceHostToolResults(
|
|
1033
|
+
request: t.ToolExecuteBatchRequest,
|
|
1034
|
+
results: t.ToolExecuteResult[],
|
|
1035
|
+
config?: RunnableConfig
|
|
1036
|
+
): Promise<void> {
|
|
1037
|
+
if (findCallback(config?.callbacks, isLangfuseCallbackHandler) == null) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const callbacks = filterCallbacks(
|
|
1041
|
+
config?.callbacks,
|
|
1042
|
+
isLangfuseCallbackHandler
|
|
1043
|
+
);
|
|
1044
|
+
const callsById = new Map(request.toolCalls.map((call) => [call.id, call]));
|
|
1045
|
+
for (const result of results) {
|
|
1046
|
+
const metadata = getToolObservationMetadata(result.artifact);
|
|
1047
|
+
const call = callsById.get(result.toolCallId);
|
|
1048
|
+
if (call == null || Object.keys(metadata).length === 0) {
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
const failure =
|
|
1052
|
+
result.status === 'error'
|
|
1053
|
+
? new Error('Host tool execution failed')
|
|
1054
|
+
: undefined;
|
|
1055
|
+
const observation = tool(
|
|
1056
|
+
async () => {
|
|
1057
|
+
if (failure != null) {
|
|
1058
|
+
throw failure;
|
|
1059
|
+
}
|
|
1060
|
+
return LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT;
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
name: call.name,
|
|
1064
|
+
description: 'Host tool execution metadata',
|
|
1065
|
+
schema: { type: 'object', properties: {} },
|
|
1066
|
+
}
|
|
1067
|
+
);
|
|
1068
|
+
try {
|
|
1069
|
+
await observation.invoke(
|
|
1070
|
+
{ type: 'tool_call', id: call.id, name: call.name, args: {} },
|
|
1071
|
+
{
|
|
1072
|
+
callbacks,
|
|
1073
|
+
metadata: { ...metadata, ...request.metadata },
|
|
1074
|
+
}
|
|
1075
|
+
);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
if (failure == null || error !== failure) {
|
|
1078
|
+
throw error;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1026
1084
|
export async function disposeLangfuseHandler(value: unknown): Promise<void> {
|
|
1027
1085
|
if (value instanceof ScopedLangfuseCallbackHandler) {
|
|
1028
1086
|
await value.finishDeferredRoot();
|
|
@@ -24,6 +24,9 @@ import { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';
|
|
|
24
24
|
|
|
25
25
|
export { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };
|
|
26
26
|
|
|
27
|
+
export const LANGFUSE_OBSERVATION_METADATA_ARTIFACT_KEY =
|
|
28
|
+
'librechatLangfuseObservationMetadata';
|
|
29
|
+
|
|
27
30
|
const LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';
|
|
28
31
|
const SERVER_TOOL_RESULT_PREFIX = '{"serverToolResult":';
|
|
29
32
|
const SERVER_TOOL_RESULT_REPLAY_MARKER_KEY = 'librechatResponsesReplay';
|
|
@@ -52,6 +55,9 @@ type RedactionContext = {
|
|
|
52
55
|
};
|
|
53
56
|
|
|
54
57
|
const TOOL_OUTPUT_FIELD_KEYS = ['content', 'artifact'];
|
|
58
|
+
const MAX_OBSERVATION_METADATA_FIELDS = 32;
|
|
59
|
+
const MAX_OBSERVATION_METADATA_STRING_LENGTH = 200;
|
|
60
|
+
const OBSERVATION_METADATA_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/;
|
|
55
61
|
|
|
56
62
|
type ResponsesReplayOutputDescriptor = {
|
|
57
63
|
nestedOutputFields?: Readonly<Record<string, readonly string[]>>;
|
|
@@ -744,6 +750,90 @@ export function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {
|
|
|
744
750
|
classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);
|
|
745
751
|
}
|
|
746
752
|
|
|
753
|
+
function parseSerializedValue(value: unknown): unknown {
|
|
754
|
+
if (typeof value !== 'string') {
|
|
755
|
+
return value;
|
|
756
|
+
}
|
|
757
|
+
try {
|
|
758
|
+
return JSON.parse(value) as unknown;
|
|
759
|
+
} catch {
|
|
760
|
+
return undefined;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function getToolMessageArtifact(
|
|
765
|
+
value: unknown
|
|
766
|
+
): Record<string, unknown> | undefined {
|
|
767
|
+
const parsed = parseSerializedValue(value);
|
|
768
|
+
if (!isRecord(parsed)) {
|
|
769
|
+
return undefined;
|
|
770
|
+
}
|
|
771
|
+
if (isRecord(parsed.artifact)) {
|
|
772
|
+
return parsed.artifact;
|
|
773
|
+
}
|
|
774
|
+
if (isRecord(parsed.kwargs) && isRecord(parsed.kwargs.artifact)) {
|
|
775
|
+
return parsed.kwargs.artifact;
|
|
776
|
+
}
|
|
777
|
+
return undefined;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
export function getToolObservationMetadata(
|
|
781
|
+
artifact: unknown
|
|
782
|
+
): Record<string, string | number | boolean> {
|
|
783
|
+
// This reserved artifact field is for trusted host adapters. Never copy
|
|
784
|
+
// arbitrary tool or model output into it without an explicit allowlist.
|
|
785
|
+
const metadata = isRecord(artifact)
|
|
786
|
+
? artifact[LANGFUSE_OBSERVATION_METADATA_ARTIFACT_KEY]
|
|
787
|
+
: undefined;
|
|
788
|
+
const result: Record<string, string | number | boolean> = {};
|
|
789
|
+
if (!isRecord(metadata)) {
|
|
790
|
+
return result;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
let promoted = 0;
|
|
794
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
795
|
+
if (
|
|
796
|
+
promoted >= MAX_OBSERVATION_METADATA_FIELDS ||
|
|
797
|
+
!OBSERVATION_METADATA_KEY_PATTERN.test(key) ||
|
|
798
|
+
!(
|
|
799
|
+
typeof value === 'number' ||
|
|
800
|
+
typeof value === 'boolean' ||
|
|
801
|
+
(typeof value === 'string' &&
|
|
802
|
+
value.length <= MAX_OBSERVATION_METADATA_STRING_LENGTH)
|
|
803
|
+
)
|
|
804
|
+
) {
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
result[key] = value;
|
|
811
|
+
promoted += 1;
|
|
812
|
+
}
|
|
813
|
+
return result;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function promoteToolObservationMetadata(
|
|
817
|
+
span: ReadableSpan,
|
|
818
|
+
attributes: Record<string, unknown>,
|
|
819
|
+
config: ResolvedLangfuseToolOutputTracingConfig
|
|
820
|
+
): void {
|
|
821
|
+
if (!isToolObservation(attributes) || !shouldRedactTool(span.name, config)) {
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const metadata = getToolObservationMetadata(
|
|
825
|
+
getToolMessageArtifact(
|
|
826
|
+
attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT]
|
|
827
|
+
)
|
|
828
|
+
);
|
|
829
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
830
|
+
const attributeKey = `${LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.${key}`;
|
|
831
|
+
if (!Object.prototype.hasOwnProperty.call(attributes, attributeKey)) {
|
|
832
|
+
attributes[attributeKey] = value;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
|
|
747
837
|
function redactToolObservationOutput(
|
|
748
838
|
span: ReadableSpan,
|
|
749
839
|
attributes: Record<string, unknown>,
|
|
@@ -774,6 +864,7 @@ export function redactLangfuseSpanToolOutputs(
|
|
|
774
864
|
return;
|
|
775
865
|
}
|
|
776
866
|
|
|
867
|
+
promoteToolObservationMetadata(span, attributes, config);
|
|
777
868
|
redactToolObservationOutput(span, attributes, config);
|
|
778
869
|
|
|
779
870
|
for (const key of [
|
|
@@ -246,6 +246,18 @@ function findLastMessageText(value: unknown, role: string): string | undefined {
|
|
|
246
246
|
return undefined;
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
/**
|
|
250
|
+
* A summarize-only run answers with its summary, carried in state as
|
|
251
|
+
* `manualSummary`, rather than with an assistant message.
|
|
252
|
+
*/
|
|
253
|
+
function getManualSummary(value: unknown): string | undefined {
|
|
254
|
+
if (!isRecord(value) || typeof value.manualSummary !== 'string') {
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
const text = value.manualSummary.trim();
|
|
258
|
+
return text === '' ? undefined : text;
|
|
259
|
+
}
|
|
260
|
+
|
|
249
261
|
function normalizeToolCall(value: unknown): SerializedToolCall | undefined {
|
|
250
262
|
if (!isRecord(value)) {
|
|
251
263
|
return undefined;
|
|
@@ -534,10 +546,11 @@ function shapeConversationPayload(span: MutableSpan): void {
|
|
|
534
546
|
parseAttributeValue(span.attributes[inputKey]),
|
|
535
547
|
'user'
|
|
536
548
|
);
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
549
|
+
const output = parseAttributeValue(span.attributes[outputKey]);
|
|
550
|
+
/** A summarize-only run's summary is its answer; any assistant message
|
|
551
|
+
* still in state is an older reply the run retained, not its output. */
|
|
552
|
+
const answer =
|
|
553
|
+
getManualSummary(output) ?? findLastMessageText(output, 'assistant');
|
|
541
554
|
/** A generation that IS the trace root — a bare `model.invoke` with no
|
|
542
555
|
* wrapping chain, i.e. the activity-label path — is also the only
|
|
543
556
|
* observation carrying its own prompt: reducing its observation input
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
serializeStructuredValueBounded,
|
|
40
40
|
} from '@/utils/toolContent';
|
|
41
41
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
42
|
+
import { isReasoningContentBlock } from '@/messages/reasoningTypes';
|
|
42
43
|
import { Constants } from '@/common';
|
|
43
44
|
|
|
44
45
|
type StandardTextBlock = Data.StandardTextBlock;
|
|
@@ -656,7 +657,6 @@ function _formatContent(message: BaseMessage) {
|
|
|
656
657
|
* cross-provider handoff (e.g. Bedrock → Anthropic) we drop them rather than
|
|
657
658
|
* forwarding an unusable block. The receiving model produces its own thinking.
|
|
658
659
|
*/
|
|
659
|
-
const foreignReasoningTypes = ['reasoning_content', 'reasoning', 'think'];
|
|
660
660
|
/**
|
|
661
661
|
* Google server-side tool blocks (`toolCall`/`toolResponse` parts from e.g.
|
|
662
662
|
* URL context or Google Search). Only Google can execute these and validate
|
|
@@ -1049,7 +1049,7 @@ function _formatContent(message: BaseMessage) {
|
|
|
1049
1049
|
};
|
|
1050
1050
|
} else if (
|
|
1051
1051
|
isAIMessage(message) &&
|
|
1052
|
-
|
|
1052
|
+
isReasoningContentBlock(contentPart)
|
|
1053
1053
|
) {
|
|
1054
1054
|
// Foreign reasoning on an ASSISTANT turn (Bedrock `reasoning_content`,
|
|
1055
1055
|
// Google `reasoning`, LibreChat `think`) carries provider-specific
|
|
@@ -1240,12 +1240,6 @@ function messagesHaveCacheControl(
|
|
|
1240
1240
|
);
|
|
1241
1241
|
}
|
|
1242
1242
|
|
|
1243
|
-
/** Anthropic rejects cache_control on these reasoning blocks. */
|
|
1244
|
-
const NON_CACHEABLE_PAYLOAD_BLOCK_TYPES = new Set([
|
|
1245
|
-
'thinking',
|
|
1246
|
-
'redacted_thinking',
|
|
1247
|
-
]);
|
|
1248
|
-
|
|
1249
1243
|
/**
|
|
1250
1244
|
* Place one ephemeral `cache_control` on the last cacheable block of the final
|
|
1251
1245
|
* message of an already-converted Anthropic payload. Used to re-anchor the tail
|
|
@@ -1287,8 +1281,9 @@ function reanchorTailCacheControl(
|
|
|
1287
1281
|
|
|
1288
1282
|
let anchor = -1;
|
|
1289
1283
|
for (let i = 0; i < content.length; i++) {
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1284
|
+
const block = content[i] as { type?: string };
|
|
1285
|
+
const type = block.type;
|
|
1286
|
+
if (type == null || isReasoningContentBlock(block)) {
|
|
1292
1287
|
continue;
|
|
1293
1288
|
}
|
|
1294
1289
|
if (
|
|
@@ -30,6 +30,7 @@ import type {
|
|
|
30
30
|
} from '../types';
|
|
31
31
|
import { serializeStructuredValueBounded } from '@/utils/toolContent';
|
|
32
32
|
import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
33
|
+
import { isReasoningContentBlock } from '@/messages/reasoningTypes';
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Reasoning blocks from other providers, relative to Bedrock. Bedrock's native
|
|
@@ -37,13 +38,6 @@ import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
|
|
|
37
38
|
* signatures Bedrock cannot validate, so they are dropped on a cross-provider
|
|
38
39
|
* handoff (e.g. Anthropic → Bedrock) rather than crashing the conversion.
|
|
39
40
|
*/
|
|
40
|
-
const FOREIGN_REASONING_TYPES = [
|
|
41
|
-
'thinking',
|
|
42
|
-
'redacted_thinking',
|
|
43
|
-
'reasoning',
|
|
44
|
-
'think',
|
|
45
|
-
];
|
|
46
|
-
|
|
47
41
|
/**
|
|
48
42
|
* Google server-side tool blocks (`toolCall`/`toolResponse` parts from e.g.
|
|
49
43
|
* URL context or Google Search). Only Google can execute these and validate
|
|
@@ -797,7 +791,7 @@ function convertAIMessageToConverseMessage(msg: BaseMessage): BedrockMessage {
|
|
|
797
791
|
contentBlocks.push({
|
|
798
792
|
cachePoint,
|
|
799
793
|
} as BedrockContentBlock);
|
|
800
|
-
} else if (
|
|
794
|
+
} else if (isReasoningContentBlock(block)) {
|
|
801
795
|
// Reasoning from another provider (Anthropic `thinking`/
|
|
802
796
|
// `redacted_thinking`, Google `reasoning`, LibreChat `think`).
|
|
803
797
|
// Bedrock's native reasoning is `reasoning_content` (handled above); a
|