@librechat/agents 3.1.85 → 3.1.87
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/README.md +69 -0
- package/dist/cjs/agents/AgentContext.cjs +7 -2
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/events.cjs +23 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +133 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +251 -53
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -5
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +113 -24
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +3 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -5
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs +253 -0
- package/dist/cjs/openai/index.cjs.map +1 -0
- package/dist/cjs/responses/index.cjs +448 -0
- package/dist/cjs/responses/index.cjs.map +1 -0
- package/dist/cjs/run.cjs +108 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1057 -0
- package/dist/cjs/session/AgentSession.cjs.map +1 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
- package/dist/cjs/session/handlers.cjs +221 -0
- package/dist/cjs/session/handlers.cjs.map +1 -0
- package/dist/cjs/session/ids.cjs +22 -0
- package/dist/cjs/session/ids.cjs.map +1 -0
- package/dist/cjs/session/messageSerialization.cjs +179 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -0
- package/dist/cjs/stream.cjs +472 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +177 -59
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
- package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +7 -2
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/events.mjs +23 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +133 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +251 -53
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +113 -25
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/openai/index.mjs +246 -0
- package/dist/esm/openai/index.mjs.map +1 -0
- package/dist/esm/responses/index.mjs +440 -0
- package/dist/esm/responses/index.mjs.map +1 -0
- package/dist/esm/run.mjs +108 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1054 -0
- package/dist/esm/session/AgentSession.mjs.map +1 -0
- package/dist/esm/session/JsonlSessionStore.mjs +422 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
- package/dist/esm/session/handlers.mjs +219 -0
- package/dist/esm/session/handlers.mjs.map +1 -0
- package/dist/esm/session/ids.mjs +17 -0
- package/dist/esm/session/ids.mjs.map +1 -0
- package/dist/esm/session/messageSerialization.mjs +173 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -0
- package/dist/esm/stream.mjs +473 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +177 -59
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +107 -0
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
- package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +24 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +1 -0
- package/dist/types/openai/index.d.ts +75 -0
- package/dist/types/responses/index.d.ts +97 -0
- package/dist/types/run.d.ts +2 -0
- package/dist/types/session/AgentSession.d.ts +32 -0
- package/dist/types/session/JsonlSessionStore.d.ts +67 -0
- package/dist/types/session/handlers.d.ts +8 -0
- package/dist/types/session/ids.d.ts +4 -0
- package/dist/types/session/index.d.ts +5 -0
- package/dist/types/session/messageSerialization.d.ts +7 -0
- package/dist/types/session/types.d.ts +191 -0
- package/dist/types/tools/ToolNode.d.ts +12 -1
- package/dist/types/tools/eagerEventExecution.d.ts +23 -0
- package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
- package/dist/types/types/hitl.d.ts +4 -0
- package/dist/types/types/run.d.ts +11 -1
- package/dist/types/types/tools.d.ts +36 -0
- package/package.json +19 -2
- package/src/__tests__/stream.eagerEventExecution.test.ts +2458 -0
- package/src/agents/AgentContext.ts +7 -2
- package/src/agents/__tests__/AgentContext.test.ts +254 -5
- package/src/events.ts +29 -0
- package/src/graphs/Graph.ts +224 -50
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/index.ts +356 -84
- package/src/llm/anthropic/llm.spec.ts +64 -0
- package/src/llm/custom-chat-models.smoke.test.ts +175 -4
- package/src/llm/openai/contentBlocks.test.ts +35 -0
- package/src/llm/openai/deepseek.test.ts +201 -2
- package/src/llm/openai/index.ts +171 -26
- package/src/llm/openai/utils/index.ts +22 -0
- package/src/llm/openrouter/index.ts +4 -2
- package/src/openai/__tests__/openai.test.ts +337 -0
- package/src/openai/index.ts +404 -0
- package/src/responses/__tests__/responses.test.ts +652 -0
- package/src/responses/index.ts +677 -0
- package/src/run.ts +158 -8
- package/src/scripts/compare_pi_vs_ours.ts +592 -173
- package/src/scripts/session_live.ts +548 -0
- package/src/session/AgentSession.ts +1432 -0
- package/src/session/JsonlSessionStore.ts +572 -0
- package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
- package/src/session/__tests__/handlers.test.ts +161 -0
- package/src/session/handlers.ts +272 -0
- package/src/session/ids.ts +17 -0
- package/src/session/index.ts +44 -0
- package/src/session/messageSerialization.ts +207 -0
- package/src/session/types.ts +275 -0
- package/src/specs/custom-event-await.test.ts +89 -0
- package/src/specs/summarization.test.ts +1 -1
- package/src/stream.ts +755 -48
- package/src/summarization/node.ts +1 -1
- package/src/tools/ToolNode.ts +299 -126
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
- package/src/tools/__tests__/handlers.test.ts +2 -1
- package/src/tools/__tests__/hitl.test.ts +206 -110
- package/src/tools/eagerEventExecution.ts +153 -0
- package/src/tools/handlers.ts +8 -4
- package/src/tools/streamedToolCallSeals.ts +57 -0
- package/src/types/hitl.ts +4 -0
- package/src/types/run.ts +11 -0
- package/src/types/tools.ts +36 -0
- package/dist/cjs/llm/text.cjs +0 -69
- package/dist/cjs/llm/text.cjs.map +0 -1
- package/dist/esm/llm/text.mjs +0 -67
- package/dist/esm/llm/text.mjs.map +0 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { ContentTypes, GraphEvents, StepTypes } from '@/common';
|
|
2
|
+
import { composeEventHandlers } from '@/events';
|
|
3
|
+
import { createRunHandlers } from '@/session';
|
|
4
|
+
import type { AgentSessionStreamEvent } from '@/session';
|
|
5
|
+
import type * as t from '@/types';
|
|
6
|
+
|
|
7
|
+
describe('createRunHandlers', () => {
|
|
8
|
+
it('emits live session events through the same graph handlers before user handlers', async () => {
|
|
9
|
+
const liveEvents: AgentSessionStreamEvent[] = [];
|
|
10
|
+
let liveEventCountSeenByUserHandler = 0;
|
|
11
|
+
const handlerResult = createRunHandlers({
|
|
12
|
+
runId: 'run_live',
|
|
13
|
+
threadId: 'thread_live',
|
|
14
|
+
onEvent: (event) => {
|
|
15
|
+
liveEvents.push(event);
|
|
16
|
+
},
|
|
17
|
+
userHandlers: {
|
|
18
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
19
|
+
handle: (): void => {
|
|
20
|
+
liveEventCountSeenByUserHandler = liveEvents.length;
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await handlerResult.handlers[GraphEvents.ON_RUN_STEP].handle(
|
|
27
|
+
GraphEvents.ON_RUN_STEP,
|
|
28
|
+
{
|
|
29
|
+
stepIndex: 0,
|
|
30
|
+
id: 'message_step',
|
|
31
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
32
|
+
index: 0,
|
|
33
|
+
stepDetails: {
|
|
34
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
35
|
+
message_creation: { message_id: 'message_step' },
|
|
36
|
+
},
|
|
37
|
+
usage: null,
|
|
38
|
+
} satisfies t.RunStep
|
|
39
|
+
);
|
|
40
|
+
await handlerResult.handlers[GraphEvents.ON_MESSAGE_DELTA].handle(
|
|
41
|
+
GraphEvents.ON_MESSAGE_DELTA,
|
|
42
|
+
{
|
|
43
|
+
id: 'message_step',
|
|
44
|
+
delta: { content: [{ type: 'text', text: 'hello' }] },
|
|
45
|
+
} satisfies t.MessageDeltaEvent
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(liveEvents.map((event) => event.type)).toEqual([
|
|
49
|
+
'run.started',
|
|
50
|
+
'message.delta',
|
|
51
|
+
]);
|
|
52
|
+
expect(liveEventCountSeenByUserHandler).toBe(2);
|
|
53
|
+
expect(handlerResult.contentParts[0]).toEqual({
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: 'hello',
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('composes adapter and host handlers in order for the same graph event', async () => {
|
|
60
|
+
const calls: string[] = [];
|
|
61
|
+
const handlers = composeEventHandlers(
|
|
62
|
+
{
|
|
63
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
64
|
+
handle: (): void => {
|
|
65
|
+
calls.push('adapter');
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
71
|
+
handle: (): void => {
|
|
72
|
+
calls.push('host');
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
await handlers[GraphEvents.ON_MESSAGE_DELTA].handle(
|
|
79
|
+
GraphEvents.ON_MESSAGE_DELTA,
|
|
80
|
+
{
|
|
81
|
+
id: 'message_step',
|
|
82
|
+
delta: { content: [{ type: 'text', text: 'hello' }] },
|
|
83
|
+
} satisfies t.MessageDeltaEvent
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(calls).toEqual(['adapter', 'host']);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('accumulates partial usage metadata without corrupting totals', async () => {
|
|
90
|
+
jest.spyOn(console, 'warn').mockImplementation(() => undefined);
|
|
91
|
+
const handlerResult = createRunHandlers({
|
|
92
|
+
runId: 'run_usage',
|
|
93
|
+
threadId: 'thread_usage',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await handlerResult.handlers[GraphEvents.CHAT_MODEL_END].handle(
|
|
97
|
+
GraphEvents.CHAT_MODEL_END,
|
|
98
|
+
{
|
|
99
|
+
output: { usage_metadata: { input_tokens: 4 } },
|
|
100
|
+
} as t.ModelEndData
|
|
101
|
+
);
|
|
102
|
+
await handlerResult.handlers[GraphEvents.CHAT_MODEL_END].handle(
|
|
103
|
+
GraphEvents.CHAT_MODEL_END,
|
|
104
|
+
{
|
|
105
|
+
output: { usage_metadata: { output_tokens: 6 } },
|
|
106
|
+
} as t.ModelEndData
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(handlerResult.usage).toEqual({
|
|
110
|
+
inputTokens: 4,
|
|
111
|
+
outputTokens: 6,
|
|
112
|
+
totalTokens: 10,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('does not emit tool completion events for summary completions', async () => {
|
|
117
|
+
const liveEvents: AgentSessionStreamEvent[] = [];
|
|
118
|
+
const handlerResult = createRunHandlers({
|
|
119
|
+
runId: 'run_summary',
|
|
120
|
+
threadId: 'thread_summary',
|
|
121
|
+
onEvent: (event) => {
|
|
122
|
+
liveEvents.push(event);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
const summary: t.SummaryContentBlock = {
|
|
126
|
+
type: ContentTypes.SUMMARY,
|
|
127
|
+
content: [{ type: ContentTypes.TEXT, text: 'summary text' }],
|
|
128
|
+
tokenCount: 4,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
await handlerResult.handlers[GraphEvents.ON_RUN_STEP].handle(
|
|
132
|
+
GraphEvents.ON_RUN_STEP,
|
|
133
|
+
{
|
|
134
|
+
stepIndex: 0,
|
|
135
|
+
id: 'summary_step',
|
|
136
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
137
|
+
index: 0,
|
|
138
|
+
stepDetails: {
|
|
139
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
140
|
+
message_creation: { message_id: 'summary_step' },
|
|
141
|
+
},
|
|
142
|
+
summary,
|
|
143
|
+
usage: null,
|
|
144
|
+
} satisfies t.RunStep
|
|
145
|
+
);
|
|
146
|
+
await handlerResult.handlers[GraphEvents.ON_RUN_STEP_COMPLETED].handle(
|
|
147
|
+
GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
148
|
+
{
|
|
149
|
+
result: {
|
|
150
|
+
id: 'summary_step',
|
|
151
|
+
index: 0,
|
|
152
|
+
type: 'summary',
|
|
153
|
+
summary,
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(liveEvents.map((event) => event.type)).toEqual(['run.started']);
|
|
159
|
+
expect(handlerResult.contentParts[0]).toBe(summary);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
2
|
+
import { GraphEvents } from '@/common';
|
|
3
|
+
import { ModelEndHandler, ToolEndHandler } from '@/events';
|
|
4
|
+
import { createContentAggregator } from '@/stream';
|
|
5
|
+
import type * as t from '@/types';
|
|
6
|
+
import type {
|
|
7
|
+
AgentSessionHandlersResult,
|
|
8
|
+
AgentSessionStreamEvent,
|
|
9
|
+
AgentSessionUsage,
|
|
10
|
+
} from './types';
|
|
11
|
+
import { createTimestamp } from './ids';
|
|
12
|
+
import { toJsonValue } from './messageSerialization';
|
|
13
|
+
|
|
14
|
+
type CompletedRunStepResult =
|
|
15
|
+
| t.ToolEndEvent
|
|
16
|
+
| (t.SummaryCompleted & { id: string; index: number });
|
|
17
|
+
|
|
18
|
+
function isToolCompletion(
|
|
19
|
+
result: CompletedRunStepResult
|
|
20
|
+
): result is t.ToolEndEvent {
|
|
21
|
+
return 'tool_call' in result;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function createEventFactory(params: {
|
|
25
|
+
runId: string;
|
|
26
|
+
threadId: string;
|
|
27
|
+
}): (
|
|
28
|
+
type: AgentSessionStreamEvent['type'],
|
|
29
|
+
data?: unknown
|
|
30
|
+
) => AgentSessionStreamEvent {
|
|
31
|
+
let sequence = 0;
|
|
32
|
+
return (type, data) => ({
|
|
33
|
+
type,
|
|
34
|
+
sequence: sequence++,
|
|
35
|
+
runId: params.runId,
|
|
36
|
+
threadId: params.threadId,
|
|
37
|
+
timestamp: createTimestamp(),
|
|
38
|
+
...(typeof data !== 'undefined' ? { data: toJsonValue(data) } : {}),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function getTokenCount(value: number | null | undefined): number {
|
|
43
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function updateUsage(usage: AgentSessionUsage, data: t.ModelEndData): void {
|
|
47
|
+
const metadata = data?.output?.usage_metadata as
|
|
48
|
+
| Partial<UsageMetadata>
|
|
49
|
+
| undefined;
|
|
50
|
+
if (!metadata) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const inputTokens = getTokenCount(metadata.input_tokens);
|
|
54
|
+
const outputTokens = getTokenCount(metadata.output_tokens);
|
|
55
|
+
const totalTokens =
|
|
56
|
+
metadata.total_tokens == null
|
|
57
|
+
? inputTokens + outputTokens
|
|
58
|
+
: getTokenCount(metadata.total_tokens);
|
|
59
|
+
usage.inputTokens += inputTokens;
|
|
60
|
+
usage.outputTokens += outputTokens;
|
|
61
|
+
usage.totalTokens += totalTokens;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function callUserHandler(params: {
|
|
65
|
+
userHandlers?: Record<string, t.EventHandler>;
|
|
66
|
+
event: string;
|
|
67
|
+
data: Parameters<t.EventHandler['handle']>[1];
|
|
68
|
+
metadata?: Record<string, unknown>;
|
|
69
|
+
graph?: Parameters<t.EventHandler['handle']>[3];
|
|
70
|
+
}): Promise<void> {
|
|
71
|
+
const handler = params.userHandlers?.[params.event];
|
|
72
|
+
if (!handler) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
await handler.handle(
|
|
76
|
+
params.event,
|
|
77
|
+
params.data,
|
|
78
|
+
params.metadata,
|
|
79
|
+
params.graph
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function createRunHandlers(params: {
|
|
84
|
+
runId: string;
|
|
85
|
+
threadId: string;
|
|
86
|
+
userHandlers?: Record<string, t.EventHandler>;
|
|
87
|
+
onEvent?: (event: AgentSessionStreamEvent) => void;
|
|
88
|
+
}): AgentSessionHandlersResult {
|
|
89
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
90
|
+
const steps: t.RunStep[] = [];
|
|
91
|
+
const usage: AgentSessionUsage = {
|
|
92
|
+
inputTokens: 0,
|
|
93
|
+
outputTokens: 0,
|
|
94
|
+
totalTokens: 0,
|
|
95
|
+
};
|
|
96
|
+
const events: AgentSessionStreamEvent[] = [];
|
|
97
|
+
const createEvent = createEventFactory({
|
|
98
|
+
runId: params.runId,
|
|
99
|
+
threadId: params.threadId,
|
|
100
|
+
});
|
|
101
|
+
const emitEvent = (event: AgentSessionStreamEvent): void => {
|
|
102
|
+
events.push(event);
|
|
103
|
+
params.onEvent?.(event);
|
|
104
|
+
};
|
|
105
|
+
const toolEndHandler = new ToolEndHandler();
|
|
106
|
+
const modelEndHandler = new ModelEndHandler();
|
|
107
|
+
|
|
108
|
+
emitEvent(createEvent('run.started'));
|
|
109
|
+
|
|
110
|
+
const handlers: Record<string, t.EventHandler> = {
|
|
111
|
+
[GraphEvents.CHAT_MODEL_STREAM]: {
|
|
112
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
113
|
+
await callUserHandler({
|
|
114
|
+
userHandlers: params.userHandlers,
|
|
115
|
+
event,
|
|
116
|
+
data,
|
|
117
|
+
metadata,
|
|
118
|
+
graph,
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
[GraphEvents.CHAT_MODEL_END]: {
|
|
123
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
124
|
+
await modelEndHandler.handle(
|
|
125
|
+
event,
|
|
126
|
+
data as t.ModelEndData,
|
|
127
|
+
metadata,
|
|
128
|
+
graph
|
|
129
|
+
);
|
|
130
|
+
updateUsage(usage, data as t.ModelEndData);
|
|
131
|
+
emitEvent(createEvent('usage.updated', usage));
|
|
132
|
+
await callUserHandler({
|
|
133
|
+
userHandlers: params.userHandlers,
|
|
134
|
+
event,
|
|
135
|
+
data,
|
|
136
|
+
metadata,
|
|
137
|
+
graph,
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
[GraphEvents.TOOL_END]: {
|
|
142
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
143
|
+
await toolEndHandler.handle(
|
|
144
|
+
event,
|
|
145
|
+
data as t.StreamEventData,
|
|
146
|
+
metadata,
|
|
147
|
+
graph
|
|
148
|
+
);
|
|
149
|
+
await callUserHandler({
|
|
150
|
+
userHandlers: params.userHandlers,
|
|
151
|
+
event,
|
|
152
|
+
data,
|
|
153
|
+
metadata,
|
|
154
|
+
graph,
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
159
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
160
|
+
const runStep = data as t.RunStep;
|
|
161
|
+
steps.push(runStep);
|
|
162
|
+
aggregateContent({ event: GraphEvents.ON_RUN_STEP, data: runStep });
|
|
163
|
+
if (runStep.stepDetails.type === 'tool_calls') {
|
|
164
|
+
emitEvent(createEvent('tool.started', runStep));
|
|
165
|
+
}
|
|
166
|
+
await callUserHandler({
|
|
167
|
+
userHandlers: params.userHandlers,
|
|
168
|
+
event,
|
|
169
|
+
data,
|
|
170
|
+
metadata,
|
|
171
|
+
graph,
|
|
172
|
+
});
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
176
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
177
|
+
const delta = data as t.RunStepDeltaEvent;
|
|
178
|
+
aggregateContent({ event: GraphEvents.ON_RUN_STEP_DELTA, data: delta });
|
|
179
|
+
emitEvent(createEvent('tool.delta', delta));
|
|
180
|
+
await callUserHandler({
|
|
181
|
+
userHandlers: params.userHandlers,
|
|
182
|
+
event,
|
|
183
|
+
data,
|
|
184
|
+
metadata,
|
|
185
|
+
graph,
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
190
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
191
|
+
const completed = data as unknown as { result: CompletedRunStepResult };
|
|
192
|
+
aggregateContent({
|
|
193
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
194
|
+
data: completed as { result: t.ToolEndEvent },
|
|
195
|
+
});
|
|
196
|
+
if (isToolCompletion(completed.result)) {
|
|
197
|
+
emitEvent(createEvent('tool.completed', completed));
|
|
198
|
+
}
|
|
199
|
+
await callUserHandler({
|
|
200
|
+
userHandlers: params.userHandlers,
|
|
201
|
+
event,
|
|
202
|
+
data,
|
|
203
|
+
metadata,
|
|
204
|
+
graph,
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
209
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
210
|
+
const delta = data as t.MessageDeltaEvent;
|
|
211
|
+
aggregateContent({ event: GraphEvents.ON_MESSAGE_DELTA, data: delta });
|
|
212
|
+
emitEvent(createEvent('message.delta', delta));
|
|
213
|
+
await callUserHandler({
|
|
214
|
+
userHandlers: params.userHandlers,
|
|
215
|
+
event,
|
|
216
|
+
data,
|
|
217
|
+
metadata,
|
|
218
|
+
graph,
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
[GraphEvents.ON_REASONING_DELTA]: {
|
|
223
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
224
|
+
const delta = data as t.ReasoningDeltaEvent;
|
|
225
|
+
aggregateContent({
|
|
226
|
+
event: GraphEvents.ON_REASONING_DELTA,
|
|
227
|
+
data: delta,
|
|
228
|
+
});
|
|
229
|
+
emitEvent(createEvent('reasoning.delta', delta));
|
|
230
|
+
await callUserHandler({
|
|
231
|
+
userHandlers: params.userHandlers,
|
|
232
|
+
event,
|
|
233
|
+
data,
|
|
234
|
+
metadata,
|
|
235
|
+
graph,
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
[GraphEvents.ON_SUMMARIZE_DELTA]: {
|
|
240
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
241
|
+
aggregateContent({
|
|
242
|
+
event: GraphEvents.ON_SUMMARIZE_DELTA,
|
|
243
|
+
data: data as t.SummarizeDeltaData,
|
|
244
|
+
});
|
|
245
|
+
await callUserHandler({
|
|
246
|
+
userHandlers: params.userHandlers,
|
|
247
|
+
event,
|
|
248
|
+
data,
|
|
249
|
+
metadata,
|
|
250
|
+
graph,
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
[GraphEvents.ON_SUMMARIZE_COMPLETE]: {
|
|
255
|
+
handle: async (event, data, metadata, graph): Promise<void> => {
|
|
256
|
+
aggregateContent({
|
|
257
|
+
event: GraphEvents.ON_SUMMARIZE_COMPLETE,
|
|
258
|
+
data: data as t.SummarizeCompleteEvent,
|
|
259
|
+
});
|
|
260
|
+
await callUserHandler({
|
|
261
|
+
userHandlers: params.userHandlers,
|
|
262
|
+
event,
|
|
263
|
+
data,
|
|
264
|
+
metadata,
|
|
265
|
+
graph,
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
return { contentParts, steps, usage, events, handlers };
|
|
272
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
|
|
3
|
+
export function createSessionId(): string {
|
|
4
|
+
return randomUUID();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function createEntryId(): string {
|
|
8
|
+
return randomUUID().replace(/-/g, '').slice(0, 12);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createRunId(): string {
|
|
12
|
+
return randomUUID();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createTimestamp(): string {
|
|
16
|
+
return new Date().toISOString();
|
|
17
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export { AgentSession, createAgentSession } from './AgentSession';
|
|
2
|
+
export { JsonlSessionStore, SessionManager } from './JsonlSessionStore';
|
|
3
|
+
export { createRunHandlers } from './handlers';
|
|
4
|
+
export {
|
|
5
|
+
serializeMessage,
|
|
6
|
+
deserializeMessage,
|
|
7
|
+
extractTextFromContent,
|
|
8
|
+
} from './messageSerialization';
|
|
9
|
+
export type {
|
|
10
|
+
AgentSessionConfig,
|
|
11
|
+
AgentSessionCheckpointLookupOptions,
|
|
12
|
+
AgentSessionCheckpointReference,
|
|
13
|
+
AgentSessionCheckpointing,
|
|
14
|
+
AgentSessionCheckpointingOptions,
|
|
15
|
+
AgentSessionHandlersResult,
|
|
16
|
+
AgentSessionInput,
|
|
17
|
+
AgentSessionRunOptions,
|
|
18
|
+
AgentSessionRunResult,
|
|
19
|
+
AgentSessionStream,
|
|
20
|
+
AgentSessionStreamEvent,
|
|
21
|
+
AgentSessionUsage,
|
|
22
|
+
CreateSessionFileOptions,
|
|
23
|
+
JsonObject,
|
|
24
|
+
JsonPrimitive,
|
|
25
|
+
JsonValue,
|
|
26
|
+
SerializedSessionMessage,
|
|
27
|
+
SessionBranchOptions,
|
|
28
|
+
SessionCompactOptions,
|
|
29
|
+
SessionCheckpointEntry,
|
|
30
|
+
SessionCompactionEntry,
|
|
31
|
+
SessionEntry,
|
|
32
|
+
SessionEntryBase,
|
|
33
|
+
SessionEntryType,
|
|
34
|
+
SessionForkOptions,
|
|
35
|
+
SessionHeader,
|
|
36
|
+
SessionLabelEntry,
|
|
37
|
+
SessionListItem,
|
|
38
|
+
SessionMessageEntry,
|
|
39
|
+
SessionPosition,
|
|
40
|
+
SessionRunEventEntry,
|
|
41
|
+
SessionStateEntry,
|
|
42
|
+
SessionSummaryEntry,
|
|
43
|
+
SessionTreeNode,
|
|
44
|
+
} from './types';
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AIMessage,
|
|
3
|
+
HumanMessage,
|
|
4
|
+
RemoveMessage,
|
|
5
|
+
SystemMessage,
|
|
6
|
+
ToolMessage,
|
|
7
|
+
BaseMessage,
|
|
8
|
+
} from '@langchain/core/messages';
|
|
9
|
+
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
10
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
11
|
+
import type { JsonObject, JsonValue, SerializedSessionMessage } from './types';
|
|
12
|
+
|
|
13
|
+
type MessageExtras = {
|
|
14
|
+
id?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
tool_call_id?: string;
|
|
17
|
+
tool_calls?: ToolCall[];
|
|
18
|
+
usage_metadata?: UsageMetadata;
|
|
19
|
+
additional_kwargs?: unknown;
|
|
20
|
+
response_metadata?: unknown;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const CIRCULAR_REFERENCE = '[Circular]';
|
|
24
|
+
|
|
25
|
+
function isJsonPrimitive(
|
|
26
|
+
value: unknown
|
|
27
|
+
): value is string | number | boolean | null {
|
|
28
|
+
return (
|
|
29
|
+
value == null ||
|
|
30
|
+
typeof value === 'string' ||
|
|
31
|
+
typeof value === 'number' ||
|
|
32
|
+
typeof value === 'boolean'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function toJsonValueInternal(value: unknown, seen: WeakSet<object>): JsonValue {
|
|
37
|
+
if (isJsonPrimitive(value)) {
|
|
38
|
+
return Number.isNaN(value) ? null : value;
|
|
39
|
+
}
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
if (seen.has(value)) {
|
|
42
|
+
return CIRCULAR_REFERENCE;
|
|
43
|
+
}
|
|
44
|
+
seen.add(value);
|
|
45
|
+
const result = value.map((item) => toJsonValueInternal(item, seen));
|
|
46
|
+
seen.delete(value);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
if (value instanceof Error) {
|
|
50
|
+
if (seen.has(value)) {
|
|
51
|
+
return CIRCULAR_REFERENCE;
|
|
52
|
+
}
|
|
53
|
+
seen.add(value);
|
|
54
|
+
const result: JsonObject = {
|
|
55
|
+
name: value.name,
|
|
56
|
+
message: value.message,
|
|
57
|
+
};
|
|
58
|
+
if (value.stack != null && value.stack !== '') {
|
|
59
|
+
result.stack = value.stack;
|
|
60
|
+
}
|
|
61
|
+
if ('cause' in value && typeof value.cause !== 'undefined') {
|
|
62
|
+
result.cause = toJsonValueInternal(value.cause, seen);
|
|
63
|
+
}
|
|
64
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
65
|
+
if (typeof nested !== 'undefined') {
|
|
66
|
+
result[key] = toJsonValueInternal(nested, seen);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
seen.delete(value);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
if (value !== null && typeof value === 'object') {
|
|
73
|
+
if (seen.has(value)) {
|
|
74
|
+
return CIRCULAR_REFERENCE;
|
|
75
|
+
}
|
|
76
|
+
seen.add(value);
|
|
77
|
+
const result: JsonObject = {};
|
|
78
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
79
|
+
if (typeof nested !== 'undefined') {
|
|
80
|
+
result[key] = toJsonValueInternal(nested, seen);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
seen.delete(value);
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
return String(value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function toJsonValue(value: unknown): JsonValue {
|
|
90
|
+
return toJsonValueInternal(value, new WeakSet<object>());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function toJsonObject(value: unknown): JsonObject | undefined {
|
|
94
|
+
if (value == null || typeof value !== 'object' || Array.isArray(value)) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
return toJsonValue(value) as JsonObject;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function fromJsonValue(value: JsonValue): unknown {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function serializeMessage(
|
|
105
|
+
message: BaseMessage
|
|
106
|
+
): SerializedSessionMessage {
|
|
107
|
+
const extras = message as BaseMessage & MessageExtras;
|
|
108
|
+
const serialized: SerializedSessionMessage = {
|
|
109
|
+
messageType: message._getType(),
|
|
110
|
+
content: toJsonValue(message.content),
|
|
111
|
+
};
|
|
112
|
+
const additionalKwargs = toJsonObject(extras.additional_kwargs);
|
|
113
|
+
const responseMetadata = toJsonObject(extras.response_metadata);
|
|
114
|
+
const usageMetadata = toJsonObject(extras.usage_metadata);
|
|
115
|
+
if (additionalKwargs) {
|
|
116
|
+
serialized.additionalKwargs = additionalKwargs;
|
|
117
|
+
}
|
|
118
|
+
if (responseMetadata) {
|
|
119
|
+
serialized.responseMetadata = responseMetadata;
|
|
120
|
+
}
|
|
121
|
+
if (usageMetadata) {
|
|
122
|
+
serialized.usageMetadata = usageMetadata;
|
|
123
|
+
}
|
|
124
|
+
if (extras.id != null && extras.id !== '') {
|
|
125
|
+
serialized.id = extras.id;
|
|
126
|
+
}
|
|
127
|
+
if (extras.name != null && extras.name !== '') {
|
|
128
|
+
serialized.name = extras.name;
|
|
129
|
+
}
|
|
130
|
+
if (extras.tool_call_id != null && extras.tool_call_id !== '') {
|
|
131
|
+
serialized.toolCallId = extras.tool_call_id;
|
|
132
|
+
}
|
|
133
|
+
if (extras.tool_calls) {
|
|
134
|
+
serialized.toolCalls = toJsonValue(extras.tool_calls);
|
|
135
|
+
}
|
|
136
|
+
return serialized;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function deserializeMessage(
|
|
140
|
+
serialized: SerializedSessionMessage
|
|
141
|
+
): BaseMessage {
|
|
142
|
+
const common = {
|
|
143
|
+
content: fromJsonValue(serialized.content) as BaseMessage['content'],
|
|
144
|
+
additional_kwargs: serialized.additionalKwargs,
|
|
145
|
+
response_metadata: serialized.responseMetadata,
|
|
146
|
+
id: serialized.id,
|
|
147
|
+
name: serialized.name,
|
|
148
|
+
};
|
|
149
|
+
if (serialized.messageType === 'human') {
|
|
150
|
+
return new HumanMessage(common);
|
|
151
|
+
}
|
|
152
|
+
if (serialized.messageType === 'ai') {
|
|
153
|
+
return new AIMessage({
|
|
154
|
+
...common,
|
|
155
|
+
tool_calls: serialized.toolCalls as ToolCall[] | undefined,
|
|
156
|
+
usage_metadata: serialized.usageMetadata as UsageMetadata | undefined,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (serialized.messageType === 'system') {
|
|
160
|
+
return new SystemMessage(common);
|
|
161
|
+
}
|
|
162
|
+
if (serialized.messageType === 'tool') {
|
|
163
|
+
return new ToolMessage({
|
|
164
|
+
...common,
|
|
165
|
+
tool_call_id: serialized.toolCallId ?? '',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (serialized.messageType === 'remove') {
|
|
169
|
+
return new RemoveMessage({
|
|
170
|
+
additional_kwargs: serialized.additionalKwargs,
|
|
171
|
+
response_metadata: serialized.responseMetadata,
|
|
172
|
+
id: serialized.id ?? '',
|
|
173
|
+
name: serialized.name,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return new HumanMessage(common);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function getMessageRole(message: BaseMessage): string {
|
|
180
|
+
const type = message._getType();
|
|
181
|
+
if (type === 'human') {
|
|
182
|
+
return 'user';
|
|
183
|
+
}
|
|
184
|
+
if (type === 'ai') {
|
|
185
|
+
return 'assistant';
|
|
186
|
+
}
|
|
187
|
+
return type;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function extractTextFromContent(content: JsonValue): string {
|
|
191
|
+
if (typeof content === 'string') {
|
|
192
|
+
return content;
|
|
193
|
+
}
|
|
194
|
+
if (!Array.isArray(content)) {
|
|
195
|
+
return '';
|
|
196
|
+
}
|
|
197
|
+
const chunks: string[] = [];
|
|
198
|
+
for (const part of content) {
|
|
199
|
+
if (part != null && typeof part === 'object' && !Array.isArray(part)) {
|
|
200
|
+
const text = part.text;
|
|
201
|
+
if (typeof text === 'string') {
|
|
202
|
+
chunks.push(text);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return chunks.join('');
|
|
207
|
+
}
|