@librechat/agents 3.3.9 → 3.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +4 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +21 -2
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +228 -16
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +14 -7
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +188 -11
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +4 -3
- package/dist/cjs/messages/core.cjs +592 -27
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/run.cjs +11 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +2 -2
- package/dist/cjs/tools/ToolNode.cjs +1 -1
- package/dist/cjs/tools/search/tool.cjs +1 -1
- package/dist/cjs/utils/index.cjs +1 -1
- package/dist/esm/agents/AgentContext.mjs +4 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +21 -2
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +228 -16
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +14 -7
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +190 -13
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -5
- package/dist/esm/messages/core.mjs +592 -28
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/run.mjs +11 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +2 -2
- package/dist/esm/tools/ToolNode.mjs +1 -1
- package/dist/esm/tools/search/tool.mjs +1 -1
- package/dist/esm/utils/index.mjs +1 -1
- package/dist/types/agents/AgentContext.d.ts +2 -0
- package/dist/types/graphs/Graph.d.ts +5 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +1 -0
- package/dist/types/llm/invoke.d.ts +1 -1
- package/dist/types/messages/core.d.ts +11 -6
- package/dist/types/run.d.ts +7 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +5 -0
- package/src/graphs/Graph.ts +39 -0
- package/src/langfuseToolOutputTracing.ts +410 -14
- package/src/llm/custom-chat-models.smoke.test.ts +747 -0
- package/src/llm/invoke.test.ts +98 -0
- package/src/llm/invoke.ts +34 -23
- package/src/llm/openai/index.ts +334 -25
- package/src/llm/openai/llm.spec.ts +107 -6
- package/src/messages/core.ts +1290 -42
- package/src/messages/formatAgentMessages.test.ts +2623 -0
- package/src/run.ts +15 -0
- package/src/specs/discovered-tools.test.ts +217 -0
- package/src/specs/langfuse-tool-output-tracing.test.ts +887 -0
- package/src/specs/preemptSeal.test.ts +374 -5
package/src/run.ts
CHANGED
|
@@ -538,6 +538,21 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
538
538
|
return this.Graph.getRunMessages();
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Returns a defensive snapshot of tools discovered by the current run.
|
|
543
|
+
* Pass an agent id for that context, or omit it for the ordered union across
|
|
544
|
+
* contexts. Interrupted state is available immediately for host persistence;
|
|
545
|
+
* completed runs retain their final snapshot through graph cleanup.
|
|
546
|
+
*/
|
|
547
|
+
getDiscoveredTools(agentId?: string): string[] {
|
|
548
|
+
if (!this.Graph) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
return this.Graph.getDiscoveredTools(agentId);
|
|
554
|
+
}
|
|
555
|
+
|
|
541
556
|
/**
|
|
542
557
|
* Returns the current calibration ratio (EMA of provider-vs-estimate token ratios).
|
|
543
558
|
* Hosts should persist this value and pass it back as `RunConfig.calibrationRatio`
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { tool } from '@langchain/core/tools';
|
|
3
|
+
import { MemorySaver } from '@langchain/langgraph';
|
|
4
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
5
|
+
import { ChatGenerationChunk } from '@langchain/core/outputs';
|
|
6
|
+
import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
|
|
7
|
+
import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
|
|
8
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
9
|
+
import type * as t from '@/types';
|
|
10
|
+
import { FakeChatModel } from '@/llm/fake';
|
|
11
|
+
import { StandardGraph } from '@/graphs';
|
|
12
|
+
import { askUserQuestion } from '@/hitl';
|
|
13
|
+
import { Providers } from '@/common';
|
|
14
|
+
import { Run } from '@/run';
|
|
15
|
+
|
|
16
|
+
const DISCOVERED_TOOL = 'save_issue_mcp_linear';
|
|
17
|
+
|
|
18
|
+
const searchTool = tool(
|
|
19
|
+
async ({ query }) => [
|
|
20
|
+
JSON.stringify({
|
|
21
|
+
found: 1,
|
|
22
|
+
tools: [{ name: DISCOVERED_TOOL }],
|
|
23
|
+
query,
|
|
24
|
+
}),
|
|
25
|
+
{ tool_references: [{ tool_name: DISCOVERED_TOOL }] },
|
|
26
|
+
],
|
|
27
|
+
{
|
|
28
|
+
name: 'tool_search',
|
|
29
|
+
description: 'Find a deferred tool.',
|
|
30
|
+
schema: z.object({ query: z.string() }),
|
|
31
|
+
responseFormat: 'content_and_artifact',
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const askTool = tool(
|
|
36
|
+
async (input) => {
|
|
37
|
+
const { answer } = askUserQuestion(input);
|
|
38
|
+
return answer;
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'ask_user_question',
|
|
42
|
+
description: 'Pause until the user answers a question.',
|
|
43
|
+
schema: z.object({ question: z.string() }),
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
class DiscoveryThenAskModel extends FakeChatModel {
|
|
48
|
+
private turn = 0;
|
|
49
|
+
|
|
50
|
+
constructor() {
|
|
51
|
+
super({ responses: ['', ''] });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async *_streamResponseChunks(
|
|
55
|
+
_messages: BaseMessage[],
|
|
56
|
+
_options: this['ParsedCallOptions'],
|
|
57
|
+
_runManager?: CallbackManagerForLLMRun
|
|
58
|
+
): AsyncGenerator<ChatGenerationChunk> {
|
|
59
|
+
const calls: ToolCall[][] = [
|
|
60
|
+
[
|
|
61
|
+
{
|
|
62
|
+
name: 'tool_search',
|
|
63
|
+
args: { query: 'save_issue' },
|
|
64
|
+
id: 'search-call',
|
|
65
|
+
type: 'tool_call',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
{
|
|
70
|
+
name: 'ask_user_question',
|
|
71
|
+
args: { question: 'Proceed?' },
|
|
72
|
+
id: 'ask-call',
|
|
73
|
+
type: 'tool_call',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
];
|
|
77
|
+
const toolCallChunks = calls[this.turn++].map(
|
|
78
|
+
(call, index): ToolCallChunk => ({
|
|
79
|
+
name: call.name,
|
|
80
|
+
args: JSON.stringify(call.args),
|
|
81
|
+
id: call.id,
|
|
82
|
+
index,
|
|
83
|
+
type: 'tool_call_chunk',
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
yield this._createResponseChunk('', toolCallChunks);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
describe('Run discovered tools', () => {
|
|
91
|
+
it('exposes discoveries made before an ask-user pause', async () => {
|
|
92
|
+
const run = await Run.create<t.IState>({
|
|
93
|
+
runId: 'discovery-pause-run',
|
|
94
|
+
graphConfig: {
|
|
95
|
+
type: 'standard',
|
|
96
|
+
llmConfig: {
|
|
97
|
+
provider: Providers.OPENAI,
|
|
98
|
+
model: 'gpt-4o-mini',
|
|
99
|
+
streaming: true,
|
|
100
|
+
streamUsage: false,
|
|
101
|
+
},
|
|
102
|
+
instructions: 'Search first, then ask the user.',
|
|
103
|
+
tools: [searchTool, askTool],
|
|
104
|
+
compileOptions: { checkpointer: new MemorySaver() },
|
|
105
|
+
},
|
|
106
|
+
returnContent: true,
|
|
107
|
+
customHandlers: {},
|
|
108
|
+
});
|
|
109
|
+
run.Graph!.overrideModel = new DiscoveryThenAskModel();
|
|
110
|
+
|
|
111
|
+
await run.processStream(
|
|
112
|
+
{ messages: [new HumanMessage('Create a Linear issue')] },
|
|
113
|
+
{
|
|
114
|
+
configurable: { thread_id: 'discovery-pause-thread' },
|
|
115
|
+
version: 'v2',
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
expect(run.getInterrupt()?.payload.type).toBe('ask_user_question');
|
|
120
|
+
// The interrupted inner subgraph has not returned its messages to the outer
|
|
121
|
+
// reducer, which is why reconstructing discoveries from run history fails.
|
|
122
|
+
expect(run.getRunMessages()).toEqual([]);
|
|
123
|
+
expect(run.getDiscoveredTools()).toEqual([DISCOVERED_TOOL]);
|
|
124
|
+
|
|
125
|
+
const snapshot = run.getDiscoveredTools();
|
|
126
|
+
snapshot.push('caller-mutation');
|
|
127
|
+
expect(run.getDiscoveredTools()).toEqual([DISCOVERED_TOOL]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('retains the last snapshot when normal cleanup resets agent contexts', async () => {
|
|
131
|
+
const run = await Run.create<t.IState>({
|
|
132
|
+
runId: 'discovery-cleanup-run',
|
|
133
|
+
graphConfig: {
|
|
134
|
+
type: 'standard',
|
|
135
|
+
llmConfig: {
|
|
136
|
+
provider: Providers.OPENAI,
|
|
137
|
+
model: 'gpt-4o-mini',
|
|
138
|
+
},
|
|
139
|
+
instructions: 'Test discovery cleanup.',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
const graph = run.Graph as StandardGraph;
|
|
143
|
+
graph.agentContexts
|
|
144
|
+
.get(graph.defaultAgentId)
|
|
145
|
+
?.markToolsAsDiscovered([DISCOVERED_TOOL]);
|
|
146
|
+
|
|
147
|
+
graph.clearHeavyState();
|
|
148
|
+
|
|
149
|
+
expect(run.getDiscoveredTools()).toEqual([DISCOVERED_TOOL]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('supports per-agent snapshots while returning a deduplicated union by default', async () => {
|
|
153
|
+
const run = await Run.create<t.IState>({
|
|
154
|
+
runId: 'multi-agent-discovery-run',
|
|
155
|
+
graphConfig: {
|
|
156
|
+
type: 'multi-agent',
|
|
157
|
+
agents: [
|
|
158
|
+
{
|
|
159
|
+
agentId: 'researcher',
|
|
160
|
+
provider: Providers.ANTHROPIC,
|
|
161
|
+
clientOptions: {
|
|
162
|
+
modelName: 'claude-haiku-4-5',
|
|
163
|
+
apiKey: 'test-key',
|
|
164
|
+
},
|
|
165
|
+
instructions: 'Research.',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
agentId: 'writer',
|
|
169
|
+
provider: Providers.ANTHROPIC,
|
|
170
|
+
clientOptions: {
|
|
171
|
+
modelName: 'claude-haiku-4-5',
|
|
172
|
+
apiKey: 'test-key',
|
|
173
|
+
},
|
|
174
|
+
instructions: 'Write.',
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
edges: [{ from: 'researcher', to: 'writer', edgeType: 'direct' }],
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
const graph = run.Graph as StandardGraph;
|
|
181
|
+
graph.agentContexts
|
|
182
|
+
.get('researcher')
|
|
183
|
+
?.markToolsAsDiscovered(['shared_tool', 'research_tool']);
|
|
184
|
+
graph.agentContexts
|
|
185
|
+
.get('writer')
|
|
186
|
+
?.markToolsAsDiscovered(['shared_tool', 'writing_tool']);
|
|
187
|
+
|
|
188
|
+
expect(run.getDiscoveredTools('researcher')).toEqual([
|
|
189
|
+
'shared_tool',
|
|
190
|
+
'research_tool',
|
|
191
|
+
]);
|
|
192
|
+
expect(run.getDiscoveredTools('writer')).toEqual([
|
|
193
|
+
'shared_tool',
|
|
194
|
+
'writing_tool',
|
|
195
|
+
]);
|
|
196
|
+
expect(run.getDiscoveredTools()).toEqual([
|
|
197
|
+
'shared_tool',
|
|
198
|
+
'research_tool',
|
|
199
|
+
'writing_tool',
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
graph.clearHeavyState();
|
|
203
|
+
expect(run.getDiscoveredTools('researcher')).toEqual([
|
|
204
|
+
'shared_tool',
|
|
205
|
+
'research_tool',
|
|
206
|
+
]);
|
|
207
|
+
expect(run.getDiscoveredTools('writer')).toEqual([
|
|
208
|
+
'shared_tool',
|
|
209
|
+
'writing_tool',
|
|
210
|
+
]);
|
|
211
|
+
expect(run.getDiscoveredTools()).toEqual([
|
|
212
|
+
'shared_tool',
|
|
213
|
+
'research_tool',
|
|
214
|
+
'writing_tool',
|
|
215
|
+
]);
|
|
216
|
+
});
|
|
217
|
+
});
|