@librechat/agents 2.4.322 → 3.0.0-rc10
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 +218 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -0
- package/dist/cjs/common/enum.cjs +15 -5
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/events.cjs +10 -6
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +309 -213
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +507 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/index.cjs +54 -9
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +52 -6
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +22 -2
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/tools.cjs +29 -0
- package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -0
- package/dist/cjs/llm/google/index.cjs +144 -0
- package/dist/cjs/llm/google/index.cjs.map +1 -0
- package/dist/cjs/llm/google/utils/common.cjs +477 -0
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -0
- package/dist/cjs/llm/ollama/index.cjs +67 -0
- package/dist/cjs/llm/ollama/index.cjs.map +1 -0
- package/dist/cjs/llm/ollama/utils.cjs +158 -0
- package/dist/cjs/llm/ollama/utils.cjs.map +1 -0
- package/dist/cjs/llm/openai/index.cjs +422 -3
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +672 -0
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -0
- package/dist/cjs/llm/providers.cjs +15 -15
- package/dist/cjs/llm/providers.cjs.map +1 -1
- package/dist/cjs/llm/text.cjs +14 -3
- package/dist/cjs/llm/text.cjs.map +1 -1
- package/dist/cjs/llm/vertexai/index.cjs +330 -0
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -0
- package/dist/cjs/main.cjs +11 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/run.cjs +137 -85
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +86 -52
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +10 -4
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +119 -13
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/search/anthropic.cjs +40 -0
- package/dist/cjs/tools/search/anthropic.cjs.map +1 -0
- package/dist/cjs/tools/search/firecrawl.cjs +55 -9
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/format.cjs +6 -6
- package/dist/cjs/tools/search/format.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +7 -29
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +86 -16
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +4 -2
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/search/utils.cjs +1 -1
- package/dist/cjs/tools/search/utils.cjs.map +1 -1
- package/dist/cjs/utils/events.cjs +31 -0
- package/dist/cjs/utils/events.cjs.map +1 -0
- package/dist/cjs/utils/title.cjs +57 -21
- package/dist/cjs/utils/title.cjs.map +1 -1
- package/dist/cjs/utils/tokens.cjs +54 -7
- package/dist/cjs/utils/tokens.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +216 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -0
- package/dist/esm/common/enum.mjs +16 -6
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/events.mjs +10 -6
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +311 -215
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +505 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
- package/dist/esm/llm/anthropic/index.mjs +54 -9
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +52 -6
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
- package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
- package/dist/esm/llm/google/index.mjs +142 -0
- package/dist/esm/llm/google/index.mjs.map +1 -0
- package/dist/esm/llm/google/utils/common.mjs +471 -0
- package/dist/esm/llm/google/utils/common.mjs.map +1 -0
- package/dist/esm/llm/ollama/index.mjs +65 -0
- package/dist/esm/llm/ollama/index.mjs.map +1 -0
- package/dist/esm/llm/ollama/utils.mjs +155 -0
- package/dist/esm/llm/ollama/utils.mjs.map +1 -0
- package/dist/esm/llm/openai/index.mjs +421 -4
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +666 -0
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
- package/dist/esm/llm/providers.mjs +5 -5
- package/dist/esm/llm/providers.mjs.map +1 -1
- package/dist/esm/llm/text.mjs +14 -3
- package/dist/esm/llm/text.mjs.map +1 -1
- package/dist/esm/llm/vertexai/index.mjs +328 -0
- package/dist/esm/llm/vertexai/index.mjs.map +1 -0
- package/dist/esm/main.mjs +6 -5
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/run.mjs +138 -87
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +88 -55
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +10 -4
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +119 -15
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/search/anthropic.mjs +37 -0
- package/dist/esm/tools/search/anthropic.mjs.map +1 -0
- package/dist/esm/tools/search/firecrawl.mjs +55 -9
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/format.mjs +7 -7
- package/dist/esm/tools/search/format.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +7 -29
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +86 -16
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +4 -2
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/search/utils.mjs +1 -1
- package/dist/esm/tools/search/utils.mjs.map +1 -1
- package/dist/esm/utils/events.mjs +29 -0
- package/dist/esm/utils/events.mjs.map +1 -0
- package/dist/esm/utils/title.mjs +57 -22
- package/dist/esm/utils/title.mjs.map +1 -1
- package/dist/esm/utils/tokens.mjs +54 -8
- package/dist/esm/utils/tokens.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +91 -0
- package/dist/types/common/enum.d.ts +17 -7
- package/dist/types/events.d.ts +5 -4
- package/dist/types/graphs/Graph.d.ts +64 -67
- package/dist/types/graphs/MultiAgentGraph.d.ts +47 -0
- package/dist/types/graphs/index.d.ts +1 -0
- package/dist/types/llm/anthropic/index.d.ts +11 -0
- package/dist/types/llm/anthropic/types.d.ts +9 -3
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +4 -4
- package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
- package/dist/types/llm/google/index.d.ts +13 -0
- package/dist/types/llm/google/types.d.ts +32 -0
- package/dist/types/llm/google/utils/common.d.ts +19 -0
- package/dist/types/llm/google/utils/tools.d.ts +10 -0
- package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
- package/dist/types/llm/ollama/index.d.ts +7 -0
- package/dist/types/llm/ollama/utils.d.ts +7 -0
- package/dist/types/llm/openai/index.d.ts +82 -3
- package/dist/types/llm/openai/types.d.ts +10 -0
- package/dist/types/llm/openai/utils/index.d.ts +20 -0
- package/dist/types/llm/text.d.ts +1 -1
- package/dist/types/llm/vertexai/index.d.ts +293 -0
- package/dist/types/messages/reducer.d.ts +9 -0
- package/dist/types/run.d.ts +19 -12
- package/dist/types/stream.d.ts +10 -3
- package/dist/types/tools/CodeExecutor.d.ts +2 -2
- package/dist/types/tools/ToolNode.d.ts +1 -1
- package/dist/types/tools/handlers.d.ts +17 -4
- package/dist/types/tools/search/anthropic.d.ts +16 -0
- package/dist/types/tools/search/firecrawl.d.ts +15 -0
- package/dist/types/tools/search/rerankers.d.ts +0 -1
- package/dist/types/tools/search/types.d.ts +30 -9
- package/dist/types/types/graph.d.ts +129 -15
- package/dist/types/types/llm.d.ts +25 -10
- package/dist/types/types/run.d.ts +50 -8
- package/dist/types/types/stream.d.ts +16 -2
- package/dist/types/types/tools.d.ts +1 -1
- package/dist/types/utils/events.d.ts +6 -0
- package/dist/types/utils/title.d.ts +2 -1
- package/dist/types/utils/tokens.d.ts +24 -0
- package/package.json +41 -17
- package/src/agents/AgentContext.ts +315 -0
- package/src/common/enum.ts +15 -5
- package/src/events.ts +24 -13
- package/src/graphs/Graph.ts +495 -313
- package/src/graphs/MultiAgentGraph.ts +598 -0
- package/src/graphs/index.ts +2 -1
- package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
- package/src/llm/anthropic/index.ts +78 -13
- package/src/llm/anthropic/llm.spec.ts +491 -115
- package/src/llm/anthropic/types.ts +39 -3
- package/src/llm/anthropic/utils/message_inputs.ts +67 -11
- package/src/llm/anthropic/utils/message_outputs.ts +21 -2
- package/src/llm/anthropic/utils/output_parsers.ts +25 -6
- package/src/llm/anthropic/utils/tools.ts +29 -0
- package/src/llm/google/index.ts +218 -0
- package/src/llm/google/types.ts +43 -0
- package/src/llm/google/utils/common.ts +646 -0
- package/src/llm/google/utils/tools.ts +160 -0
- package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
- package/src/llm/ollama/index.ts +89 -0
- package/src/llm/ollama/utils.ts +193 -0
- package/src/llm/openai/index.ts +641 -14
- package/src/llm/openai/types.ts +24 -0
- package/src/llm/openai/utils/index.ts +912 -0
- package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
- package/src/llm/providers.ts +10 -9
- package/src/llm/text.ts +26 -7
- package/src/llm/vertexai/index.ts +360 -0
- package/src/messages/reducer.ts +80 -0
- package/src/run.ts +196 -116
- package/src/scripts/ant_web_search.ts +158 -0
- package/src/scripts/args.ts +12 -8
- package/src/scripts/cli4.ts +29 -21
- package/src/scripts/cli5.ts +29 -21
- package/src/scripts/code_exec.ts +54 -23
- package/src/scripts/code_exec_files.ts +48 -17
- package/src/scripts/code_exec_simple.ts +46 -27
- package/src/scripts/handoff-test.ts +135 -0
- package/src/scripts/image.ts +52 -20
- package/src/scripts/multi-agent-chain.ts +278 -0
- package/src/scripts/multi-agent-conditional.ts +220 -0
- package/src/scripts/multi-agent-document-review-chain.ts +197 -0
- package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
- package/src/scripts/multi-agent-parallel.ts +341 -0
- package/src/scripts/multi-agent-sequence.ts +212 -0
- package/src/scripts/multi-agent-supervisor.ts +362 -0
- package/src/scripts/multi-agent-test.ts +186 -0
- package/src/scripts/search.ts +1 -9
- package/src/scripts/simple.ts +25 -10
- package/src/scripts/test-custom-prompt-key.ts +145 -0
- package/src/scripts/test-handoff-input.ts +170 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +261 -0
- package/src/scripts/test-tools-before-handoff.ts +233 -0
- package/src/scripts/tools.ts +48 -18
- package/src/specs/anthropic.simple.test.ts +150 -34
- package/src/specs/azure.simple.test.ts +325 -0
- package/src/specs/openai.simple.test.ts +140 -33
- package/src/specs/openrouter.simple.test.ts +107 -0
- package/src/specs/prune.test.ts +4 -9
- package/src/specs/reasoning.test.ts +80 -44
- package/src/specs/token-memoization.test.ts +39 -0
- package/src/stream.test.ts +94 -0
- package/src/stream.ts +143 -61
- package/src/tools/ToolNode.ts +21 -7
- package/src/tools/handlers.ts +192 -18
- package/src/tools/search/anthropic.ts +51 -0
- package/src/tools/search/firecrawl.ts +69 -20
- package/src/tools/search/format.ts +6 -8
- package/src/tools/search/rerankers.ts +7 -40
- package/src/tools/search/search.ts +97 -16
- package/src/tools/search/tool.ts +5 -2
- package/src/tools/search/types.ts +30 -10
- package/src/tools/search/utils.ts +1 -1
- package/src/types/graph.ts +318 -103
- package/src/types/llm.ts +26 -12
- package/src/types/run.ts +56 -13
- package/src/types/stream.ts +22 -1
- package/src/types/tools.ts +16 -10
- package/src/utils/events.ts +32 -0
- package/src/utils/llmConfig.ts +19 -7
- package/src/utils/title.ts +104 -30
- package/src/utils/tokens.ts +69 -10
- package/dist/types/scripts/abort.d.ts +0 -1
- package/dist/types/scripts/args.d.ts +0 -6
- package/dist/types/scripts/caching.d.ts +0 -1
- package/dist/types/scripts/cli.d.ts +0 -1
- package/dist/types/scripts/cli2.d.ts +0 -1
- package/dist/types/scripts/cli3.d.ts +0 -1
- package/dist/types/scripts/cli4.d.ts +0 -1
- package/dist/types/scripts/cli5.d.ts +0 -1
- package/dist/types/scripts/code_exec.d.ts +0 -1
- package/dist/types/scripts/code_exec_files.d.ts +0 -1
- package/dist/types/scripts/code_exec_simple.d.ts +0 -1
- package/dist/types/scripts/content.d.ts +0 -1
- package/dist/types/scripts/empty_input.d.ts +0 -1
- package/dist/types/scripts/image.d.ts +0 -1
- package/dist/types/scripts/memory.d.ts +0 -1
- package/dist/types/scripts/search.d.ts +0 -1
- package/dist/types/scripts/simple.d.ts +0 -1
- package/dist/types/scripts/stream.d.ts +0 -1
- package/dist/types/scripts/thinking.d.ts +0 -1
- package/dist/types/scripts/tools.d.ts +0 -1
- package/dist/types/specs/spec.utils.d.ts +0 -1
|
@@ -4,12 +4,19 @@
|
|
|
4
4
|
import { config } from 'dotenv';
|
|
5
5
|
config();
|
|
6
6
|
import { Calculator } from '@langchain/community/tools/calculator';
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
HumanMessage,
|
|
9
|
+
BaseMessage,
|
|
10
|
+
UsageMetadata,
|
|
11
|
+
} from '@langchain/core/messages';
|
|
9
12
|
import type * as t from '@/types';
|
|
10
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
ToolEndHandler,
|
|
15
|
+
ModelEndHandler,
|
|
16
|
+
createMetadataAggregator,
|
|
17
|
+
} from '@/events';
|
|
18
|
+
import { ContentTypes, GraphEvents, Providers, TitleMethod } from '@/common';
|
|
11
19
|
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
12
|
-
import { ContentTypes, GraphEvents, Providers } from '@/common';
|
|
13
20
|
import { capitalizeFirstLetter } from './spec.utils';
|
|
14
21
|
import { getLLMConfig } from '@/utils/llmConfig';
|
|
15
22
|
import { getArgs } from '@/scripts/args';
|
|
@@ -36,7 +43,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
36
43
|
beforeEach(async () => {
|
|
37
44
|
conversationHistory = [];
|
|
38
45
|
collectedUsage = [];
|
|
39
|
-
const { contentParts: cp, aggregateContent: ac } =
|
|
46
|
+
const { contentParts: cp, aggregateContent: ac } =
|
|
47
|
+
createContentAggregator();
|
|
40
48
|
contentParts = cp as t.MessageContentComplex[];
|
|
41
49
|
aggregateContent = ac;
|
|
42
50
|
});
|
|
@@ -49,36 +57,62 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
49
57
|
onRunStepSpy.mockReset();
|
|
50
58
|
});
|
|
51
59
|
|
|
52
|
-
const setupCustomHandlers = (): Record<
|
|
60
|
+
const setupCustomHandlers = (): Record<
|
|
61
|
+
string | GraphEvents,
|
|
62
|
+
t.EventHandler
|
|
63
|
+
> => ({
|
|
53
64
|
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
54
65
|
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
|
55
66
|
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
56
67
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
57
|
-
handle: (
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
handle: (
|
|
69
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
70
|
+
data: t.StreamEventData
|
|
71
|
+
): void => {
|
|
72
|
+
aggregateContent({
|
|
73
|
+
event,
|
|
74
|
+
data: data as unknown as { result: t.ToolEndEvent },
|
|
75
|
+
});
|
|
76
|
+
},
|
|
60
77
|
},
|
|
61
78
|
[GraphEvents.ON_RUN_STEP]: {
|
|
62
|
-
handle: (
|
|
79
|
+
handle: (
|
|
80
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
81
|
+
data: t.StreamEventData,
|
|
82
|
+
metadata,
|
|
83
|
+
graph
|
|
84
|
+
): void => {
|
|
63
85
|
onRunStepSpy(event, data, metadata, graph);
|
|
64
86
|
aggregateContent({ event, data: data as t.RunStep });
|
|
65
|
-
}
|
|
87
|
+
},
|
|
66
88
|
},
|
|
67
89
|
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
68
|
-
handle: (
|
|
90
|
+
handle: (
|
|
91
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
92
|
+
data: t.StreamEventData
|
|
93
|
+
): void => {
|
|
69
94
|
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
70
|
-
}
|
|
95
|
+
},
|
|
71
96
|
},
|
|
72
97
|
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
73
|
-
handle: (
|
|
98
|
+
handle: (
|
|
99
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
100
|
+
data: t.StreamEventData,
|
|
101
|
+
metadata,
|
|
102
|
+
graph
|
|
103
|
+
): void => {
|
|
74
104
|
onMessageDeltaSpy(event, data, metadata, graph);
|
|
75
105
|
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
76
|
-
}
|
|
106
|
+
},
|
|
77
107
|
},
|
|
78
108
|
[GraphEvents.TOOL_START]: {
|
|
79
|
-
handle: (
|
|
109
|
+
handle: (
|
|
110
|
+
_event: string,
|
|
111
|
+
_data: t.StreamEventData,
|
|
112
|
+
_metadata?: Record<string, unknown>
|
|
113
|
+
): void => {
|
|
80
114
|
// Handle tool start
|
|
81
|
-
}
|
|
115
|
+
},
|
|
82
116
|
},
|
|
83
117
|
});
|
|
84
118
|
|
|
@@ -93,7 +127,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
93
127
|
type: 'standard',
|
|
94
128
|
llmConfig,
|
|
95
129
|
tools: [new Calculator()],
|
|
96
|
-
instructions:
|
|
130
|
+
instructions:
|
|
131
|
+
'You are a friendly AI assistant. Always address the user by their name.',
|
|
97
132
|
additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
|
|
98
133
|
},
|
|
99
134
|
returnContent: true,
|
|
@@ -109,7 +144,9 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
109
144
|
|
|
110
145
|
const finalContentParts = await run.processStream(inputs, config);
|
|
111
146
|
expect(finalContentParts).toBeDefined();
|
|
112
|
-
const allTextParts = finalContentParts?.every(
|
|
147
|
+
const allTextParts = finalContentParts?.every(
|
|
148
|
+
(part) => part.type === ContentTypes.TEXT
|
|
149
|
+
);
|
|
113
150
|
expect(allTextParts).toBe(true);
|
|
114
151
|
expect(collectedUsage.length).toBeGreaterThan(0);
|
|
115
152
|
expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
|
|
@@ -117,26 +154,30 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
117
154
|
|
|
118
155
|
const finalMessages = run.getRunMessages();
|
|
119
156
|
expect(finalMessages).toBeDefined();
|
|
120
|
-
conversationHistory.push(...finalMessages ?? []);
|
|
157
|
+
conversationHistory.push(...(finalMessages ?? []));
|
|
121
158
|
expect(conversationHistory.length).toBeGreaterThan(1);
|
|
122
159
|
runningHistory = conversationHistory.slice();
|
|
123
160
|
|
|
124
161
|
expect(onMessageDeltaSpy).toHaveBeenCalled();
|
|
125
162
|
expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
|
|
126
|
-
expect(
|
|
163
|
+
expect(onMessageDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
127
164
|
|
|
128
165
|
expect(onRunStepSpy).toHaveBeenCalled();
|
|
129
166
|
expect(onRunStepSpy.mock.calls.length).toBeGreaterThan(0);
|
|
130
|
-
expect(
|
|
167
|
+
expect(onRunStepSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
131
168
|
|
|
132
169
|
const { handleLLMEnd, collected } = createMetadataAggregator();
|
|
133
170
|
const titleResult = await run.generateTitle({
|
|
171
|
+
provider,
|
|
134
172
|
inputText: userMessage,
|
|
173
|
+
titleMethod: TitleMethod.STRUCTURED,
|
|
135
174
|
contentParts,
|
|
136
175
|
chainOptions: {
|
|
137
|
-
callbacks: [
|
|
138
|
-
|
|
139
|
-
|
|
176
|
+
callbacks: [
|
|
177
|
+
{
|
|
178
|
+
handleLLMEnd,
|
|
179
|
+
},
|
|
180
|
+
],
|
|
140
181
|
},
|
|
141
182
|
});
|
|
142
183
|
|
|
@@ -146,9 +187,66 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
146
187
|
expect(collected).toBeDefined();
|
|
147
188
|
});
|
|
148
189
|
|
|
190
|
+
test(`${capitalizeFirstLetter(provider)}: should generate title using completion method`, async () => {
|
|
191
|
+
const { userName, location } = await getArgs();
|
|
192
|
+
const llmConfig = getLLMConfig(provider);
|
|
193
|
+
const customHandlers = setupCustomHandlers();
|
|
194
|
+
|
|
195
|
+
run = await Run.create<t.IState>({
|
|
196
|
+
runId: 'test-run-id-completion',
|
|
197
|
+
graphConfig: {
|
|
198
|
+
type: 'standard',
|
|
199
|
+
llmConfig,
|
|
200
|
+
tools: [new Calculator()],
|
|
201
|
+
instructions:
|
|
202
|
+
'You are a friendly AI assistant. Always address the user by their name.',
|
|
203
|
+
additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
|
|
204
|
+
},
|
|
205
|
+
returnContent: true,
|
|
206
|
+
customHandlers,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const userMessage = 'What is the weather like today?';
|
|
210
|
+
conversationHistory = [];
|
|
211
|
+
conversationHistory.push(new HumanMessage(userMessage));
|
|
212
|
+
|
|
213
|
+
const inputs = {
|
|
214
|
+
messages: conversationHistory,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const finalContentParts = await run.processStream(inputs, config);
|
|
218
|
+
expect(finalContentParts).toBeDefined();
|
|
219
|
+
|
|
220
|
+
const { handleLLMEnd, collected } = createMetadataAggregator();
|
|
221
|
+
const titleResult = await run.generateTitle({
|
|
222
|
+
provider,
|
|
223
|
+
inputText: userMessage,
|
|
224
|
+
titleMethod: TitleMethod.COMPLETION, // Using completion method
|
|
225
|
+
contentParts,
|
|
226
|
+
chainOptions: {
|
|
227
|
+
callbacks: [
|
|
228
|
+
{
|
|
229
|
+
handleLLMEnd,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(titleResult).toBeDefined();
|
|
236
|
+
expect(titleResult.title).toBeDefined();
|
|
237
|
+
expect(titleResult.title).not.toBe('');
|
|
238
|
+
// Completion method doesn't return language
|
|
239
|
+
expect(titleResult.language).toBeUndefined();
|
|
240
|
+
expect(collected).toBeDefined();
|
|
241
|
+
console.log(`Completion method generated title: "${titleResult.title}"`);
|
|
242
|
+
});
|
|
243
|
+
|
|
149
244
|
test(`${capitalizeFirstLetter(provider)}: should follow-up`, async () => {
|
|
150
245
|
console.log('Previous conversation length:', runningHistory.length);
|
|
151
|
-
console.log(
|
|
246
|
+
console.log(
|
|
247
|
+
'Last message:',
|
|
248
|
+
runningHistory[runningHistory.length - 1].content
|
|
249
|
+
);
|
|
152
250
|
const { userName, location } = await getArgs();
|
|
153
251
|
const llmConfig = getLLMConfig(provider);
|
|
154
252
|
const customHandlers = setupCustomHandlers();
|
|
@@ -159,7 +257,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
159
257
|
type: 'standard',
|
|
160
258
|
llmConfig,
|
|
161
259
|
tools: [new Calculator()],
|
|
162
|
-
instructions:
|
|
260
|
+
instructions:
|
|
261
|
+
'You are a friendly AI assistant. Always address the user by their name.',
|
|
163
262
|
additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
|
|
164
263
|
},
|
|
165
264
|
returnContent: true,
|
|
@@ -175,7 +274,9 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
175
274
|
|
|
176
275
|
const finalContentParts = await run.processStream(inputs, config);
|
|
177
276
|
expect(finalContentParts).toBeDefined();
|
|
178
|
-
const allTextParts = finalContentParts?.every(
|
|
277
|
+
const allTextParts = finalContentParts?.every(
|
|
278
|
+
(part) => part.type === ContentTypes.TEXT
|
|
279
|
+
);
|
|
179
280
|
expect(allTextParts).toBe(true);
|
|
180
281
|
expect(collectedUsage.length).toBeGreaterThan(0);
|
|
181
282
|
expect(collectedUsage[0].input_tokens).toBeGreaterThan(0);
|
|
@@ -184,7 +285,10 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
184
285
|
const finalMessages = run.getRunMessages();
|
|
185
286
|
expect(finalMessages).toBeDefined();
|
|
186
287
|
expect(finalMessages?.length).toBeGreaterThan(0);
|
|
187
|
-
console.log(
|
|
288
|
+
console.log(
|
|
289
|
+
`${capitalizeFirstLetter(provider)} follow-up message:`,
|
|
290
|
+
finalMessages?.[finalMessages.length - 1]?.content
|
|
291
|
+
);
|
|
188
292
|
|
|
189
293
|
expect(onMessageDeltaSpy).toHaveBeenCalled();
|
|
190
294
|
expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
|
|
@@ -196,9 +300,12 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
196
300
|
test('should handle errors appropriately', async () => {
|
|
197
301
|
// Test error scenarios
|
|
198
302
|
await expect(async () => {
|
|
199
|
-
await run.processStream(
|
|
200
|
-
|
|
201
|
-
|
|
303
|
+
await run.processStream(
|
|
304
|
+
{
|
|
305
|
+
messages: [],
|
|
306
|
+
},
|
|
307
|
+
{} as any
|
|
308
|
+
);
|
|
202
309
|
}).rejects.toThrow();
|
|
203
310
|
});
|
|
204
|
-
});
|
|
311
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { config } from 'dotenv';
|
|
2
|
+
config();
|
|
3
|
+
import { Calculator } from '@langchain/community/tools/calculator';
|
|
4
|
+
import {
|
|
5
|
+
HumanMessage,
|
|
6
|
+
BaseMessage,
|
|
7
|
+
UsageMetadata,
|
|
8
|
+
} from '@langchain/core/messages';
|
|
9
|
+
import type * as t from '@/types';
|
|
10
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
11
|
+
import { ContentTypes, GraphEvents, Providers, TitleMethod } from '@/common';
|
|
12
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
13
|
+
import { capitalizeFirstLetter } from './spec.utils';
|
|
14
|
+
import { getLLMConfig } from '@/utils/llmConfig';
|
|
15
|
+
import { getArgs } from '@/scripts/args';
|
|
16
|
+
import { Run } from '@/run';
|
|
17
|
+
|
|
18
|
+
// Auto-skip if OpenRouter env is missing
|
|
19
|
+
const hasOpenRouter = (process.env.OPENROUTER_API_KEY ?? '').trim() !== '';
|
|
20
|
+
const describeIf = hasOpenRouter ? describe : describe.skip;
|
|
21
|
+
|
|
22
|
+
const provider = Providers.OPENROUTER;
|
|
23
|
+
describeIf(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
24
|
+
jest.setTimeout(60000);
|
|
25
|
+
let run: Run<t.IState>;
|
|
26
|
+
let collectedUsage: UsageMetadata[];
|
|
27
|
+
let conversationHistory: BaseMessage[];
|
|
28
|
+
let contentParts: t.MessageContentComplex[];
|
|
29
|
+
|
|
30
|
+
const configV2 = {
|
|
31
|
+
configurable: { thread_id: 'or-convo-1' },
|
|
32
|
+
streamMode: 'values',
|
|
33
|
+
version: 'v2' as const,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
beforeEach(async () => {
|
|
37
|
+
conversationHistory = [];
|
|
38
|
+
collectedUsage = [];
|
|
39
|
+
const { contentParts: cp } = createContentAggregator();
|
|
40
|
+
contentParts = cp as t.MessageContentComplex[];
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const onMessageDeltaSpy = jest.fn();
|
|
44
|
+
const onRunStepSpy = jest.fn();
|
|
45
|
+
|
|
46
|
+
afterAll(() => {
|
|
47
|
+
onMessageDeltaSpy.mockReset();
|
|
48
|
+
onRunStepSpy.mockReset();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const setupCustomHandlers = (): Record<
|
|
52
|
+
string | GraphEvents,
|
|
53
|
+
t.EventHandler
|
|
54
|
+
> => ({
|
|
55
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
56
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
|
57
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test(`${capitalizeFirstLetter(provider)}: simple stream + title`, async () => {
|
|
61
|
+
const { userName, location } = await getArgs();
|
|
62
|
+
const llmConfig = getLLMConfig(provider);
|
|
63
|
+
const customHandlers = setupCustomHandlers();
|
|
64
|
+
|
|
65
|
+
run = await Run.create<t.IState>({
|
|
66
|
+
runId: 'or-run-1',
|
|
67
|
+
graphConfig: {
|
|
68
|
+
type: 'standard',
|
|
69
|
+
llmConfig,
|
|
70
|
+
tools: [new Calculator()],
|
|
71
|
+
instructions: 'You are a friendly AI assistant.',
|
|
72
|
+
additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
|
|
73
|
+
},
|
|
74
|
+
returnContent: true,
|
|
75
|
+
customHandlers,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const userMessage = 'hi';
|
|
79
|
+
conversationHistory.push(new HumanMessage(userMessage));
|
|
80
|
+
|
|
81
|
+
const finalContentParts = await run.processStream(
|
|
82
|
+
{ messages: conversationHistory },
|
|
83
|
+
configV2
|
|
84
|
+
);
|
|
85
|
+
expect(finalContentParts).toBeDefined();
|
|
86
|
+
const allTextParts = finalContentParts?.every(
|
|
87
|
+
(part) => part.type === ContentTypes.TEXT
|
|
88
|
+
);
|
|
89
|
+
expect(allTextParts).toBe(true);
|
|
90
|
+
expect(
|
|
91
|
+
(collectedUsage[0]?.input_tokens ?? 0) +
|
|
92
|
+
(collectedUsage[0]?.output_tokens ?? 0)
|
|
93
|
+
).toBeGreaterThan(0);
|
|
94
|
+
|
|
95
|
+
const finalMessages = run.getRunMessages();
|
|
96
|
+
expect(finalMessages).toBeDefined();
|
|
97
|
+
conversationHistory.push(...(finalMessages ?? []));
|
|
98
|
+
|
|
99
|
+
const titleRes = await run.generateTitle({
|
|
100
|
+
provider,
|
|
101
|
+
inputText: userMessage,
|
|
102
|
+
titleMethod: TitleMethod.COMPLETION,
|
|
103
|
+
contentParts,
|
|
104
|
+
});
|
|
105
|
+
expect(titleRes.title).toBeDefined();
|
|
106
|
+
});
|
|
107
|
+
});
|
package/src/specs/prune.test.ts
CHANGED
|
@@ -725,8 +725,11 @@ describe('Prune Messages Tests', () => {
|
|
|
725
725
|
type: 'standard',
|
|
726
726
|
llmConfig,
|
|
727
727
|
instructions: 'You are a helpful assistant.',
|
|
728
|
+
maxContextTokens: 1000,
|
|
728
729
|
},
|
|
729
730
|
returnContent: true,
|
|
731
|
+
tokenCounter,
|
|
732
|
+
indexTokenCountMap: {},
|
|
730
733
|
});
|
|
731
734
|
|
|
732
735
|
// Override the model to use a fake LLM
|
|
@@ -734,10 +737,6 @@ describe('Prune Messages Tests', () => {
|
|
|
734
737
|
|
|
735
738
|
const messages = [new HumanMessage('Hello, how are you?')];
|
|
736
739
|
|
|
737
|
-
const indexTokenCountMap = {
|
|
738
|
-
0: tokenCounter(messages[0]),
|
|
739
|
-
};
|
|
740
|
-
|
|
741
740
|
const config: Partial<RunnableConfig> & {
|
|
742
741
|
version: 'v1' | 'v2';
|
|
743
742
|
streamMode: string;
|
|
@@ -749,11 +748,7 @@ describe('Prune Messages Tests', () => {
|
|
|
749
748
|
version: 'v2' as const,
|
|
750
749
|
};
|
|
751
750
|
|
|
752
|
-
await run.processStream({ messages }, config
|
|
753
|
-
maxContextTokens: 1000,
|
|
754
|
-
indexTokenCountMap,
|
|
755
|
-
tokenCounter,
|
|
756
|
-
});
|
|
751
|
+
await run.processStream({ messages }, config);
|
|
757
752
|
|
|
758
753
|
const finalMessages = run.getRunMessages();
|
|
759
754
|
expect(finalMessages).toBeDefined();
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
// src/scripts/cli.test.ts
|
|
4
4
|
import { config } from 'dotenv';
|
|
5
5
|
config();
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
HumanMessage,
|
|
8
|
+
BaseMessage,
|
|
9
|
+
MessageContentText,
|
|
10
|
+
} from '@langchain/core/messages';
|
|
7
11
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
8
|
-
import type { StandardGraph } from '@/graphs';
|
|
9
12
|
import type * as t from '@/types';
|
|
10
13
|
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
11
14
|
import { capitalizeFirstLetter } from './spec.utils';
|
|
@@ -28,40 +31,49 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
28
31
|
let aggregateContent: t.ContentAggregator;
|
|
29
32
|
let runSteps: Set<string>;
|
|
30
33
|
|
|
31
|
-
const config: Partial<RunnableConfig> & {
|
|
34
|
+
const config: Partial<RunnableConfig> & {
|
|
35
|
+
version: 'v1' | 'v2';
|
|
36
|
+
run_id?: string;
|
|
37
|
+
streamMode: string;
|
|
38
|
+
} = {
|
|
32
39
|
configurable: {
|
|
33
40
|
thread_id: 'conversation-num-1',
|
|
34
41
|
},
|
|
35
42
|
streamMode: 'values',
|
|
36
43
|
version: 'v2' as const,
|
|
37
|
-
callbacks: [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
44
|
+
callbacks: [
|
|
45
|
+
{
|
|
46
|
+
async handleCustomEvent(event, data, metadata): Promise<void> {
|
|
47
|
+
if (event !== GraphEvents.ON_MESSAGE_DELTA) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const messageDeltaData = data as t.MessageDeltaEvent;
|
|
51
|
+
|
|
52
|
+
// Wait until we see the run step (with timeout for safety)
|
|
53
|
+
const maxAttempts = 50; // 5 seconds total
|
|
54
|
+
let attempts = 0;
|
|
55
|
+
while (!runSteps.has(messageDeltaData.id) && attempts < maxAttempts) {
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
57
|
+
attempts++;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!runSteps.has(messageDeltaData.id)) {
|
|
61
|
+
console.warn(
|
|
62
|
+
`Timeout waiting for run step: ${messageDeltaData.id}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onMessageDeltaSpy(event, data, metadata, run.Graph);
|
|
67
|
+
aggregateContent({ event, data: messageDeltaData });
|
|
68
|
+
},
|
|
58
69
|
},
|
|
59
|
-
|
|
70
|
+
],
|
|
60
71
|
};
|
|
61
72
|
|
|
62
73
|
beforeEach(async () => {
|
|
63
74
|
conversationHistory = [];
|
|
64
|
-
const { contentParts: parts, aggregateContent: ac } =
|
|
75
|
+
const { contentParts: parts, aggregateContent: ac } =
|
|
76
|
+
createContentAggregator();
|
|
65
77
|
aggregateContent = ac;
|
|
66
78
|
runSteps = new Set();
|
|
67
79
|
contentParts = parts as t.MessageContentComplex[];
|
|
@@ -81,32 +93,54 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
81
93
|
onRunStepSpy.mockReset();
|
|
82
94
|
});
|
|
83
95
|
|
|
84
|
-
const setupCustomHandlers = (): Record<
|
|
96
|
+
const setupCustomHandlers = (): Record<
|
|
97
|
+
string | GraphEvents,
|
|
98
|
+
t.EventHandler
|
|
99
|
+
> => ({
|
|
85
100
|
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
86
101
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
87
|
-
handle: (
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
handle: (
|
|
103
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
104
|
+
data: t.StreamEventData
|
|
105
|
+
): void => {
|
|
106
|
+
aggregateContent({
|
|
107
|
+
event,
|
|
108
|
+
data: data as unknown as { result: t.ToolEndEvent },
|
|
109
|
+
});
|
|
110
|
+
},
|
|
90
111
|
},
|
|
91
112
|
[GraphEvents.ON_RUN_STEP]: {
|
|
92
|
-
handle: (
|
|
113
|
+
handle: (
|
|
114
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
115
|
+
data: t.StreamEventData,
|
|
116
|
+
metadata,
|
|
117
|
+
graph
|
|
118
|
+
): void => {
|
|
93
119
|
const runStepData = data as t.RunStep;
|
|
94
120
|
runSteps.add(runStepData.id);
|
|
95
121
|
|
|
96
122
|
onRunStepSpy(event, runStepData, metadata, graph);
|
|
97
123
|
aggregateContent({ event, data: runStepData });
|
|
98
|
-
}
|
|
124
|
+
},
|
|
99
125
|
},
|
|
100
126
|
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
101
|
-
handle: (
|
|
127
|
+
handle: (
|
|
128
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
129
|
+
data: t.StreamEventData
|
|
130
|
+
): void => {
|
|
102
131
|
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
103
|
-
}
|
|
132
|
+
},
|
|
104
133
|
},
|
|
105
134
|
[GraphEvents.ON_REASONING_DELTA]: {
|
|
106
|
-
handle: (
|
|
135
|
+
handle: (
|
|
136
|
+
event: GraphEvents.ON_REASONING_DELTA,
|
|
137
|
+
data: t.StreamEventData,
|
|
138
|
+
metadata,
|
|
139
|
+
graph
|
|
140
|
+
): void => {
|
|
107
141
|
onReasoningDeltaSpy(event, data, metadata, graph);
|
|
108
142
|
aggregateContent({ event, data: data as t.ReasoningDeltaEvent });
|
|
109
|
-
}
|
|
143
|
+
},
|
|
110
144
|
},
|
|
111
145
|
});
|
|
112
146
|
|
|
@@ -120,7 +154,8 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
120
154
|
graphConfig: {
|
|
121
155
|
type: 'standard',
|
|
122
156
|
llmConfig,
|
|
123
|
-
instructions:
|
|
157
|
+
instructions:
|
|
158
|
+
'You are a friendly AI assistant. Always address the user by their name.',
|
|
124
159
|
additional_instructions: `The user's name is ${userName} and they are located in ${location}.`,
|
|
125
160
|
},
|
|
126
161
|
returnContent: true,
|
|
@@ -141,25 +176,26 @@ describe(`${capitalizeFirstLetter(provider)} Streaming Tests`, () => {
|
|
|
141
176
|
expect(contentParts.length).toBe(2);
|
|
142
177
|
const reasoningContent = reasoningText.match(/<think>(.*)<\/think>/s)?.[0];
|
|
143
178
|
const content = reasoningText.split(/<\/think>/)[1];
|
|
144
|
-
expect((contentParts[0] as t.ReasoningContentText).think).toBe(
|
|
179
|
+
expect((contentParts[0] as t.ReasoningContentText).think).toBe(
|
|
180
|
+
reasoningContent
|
|
181
|
+
);
|
|
145
182
|
expect((contentParts[1] as MessageContentText).text).toBe(content);
|
|
146
183
|
|
|
147
184
|
const finalMessages = run.getRunMessages();
|
|
148
185
|
expect(finalMessages).toBeDefined();
|
|
149
|
-
conversationHistory.push(...finalMessages ?? []);
|
|
186
|
+
conversationHistory.push(...(finalMessages ?? []));
|
|
150
187
|
expect(conversationHistory.length).toBeGreaterThan(1);
|
|
151
188
|
|
|
152
189
|
expect(onMessageDeltaSpy).toHaveBeenCalled();
|
|
153
190
|
expect(onMessageDeltaSpy.mock.calls.length).toBeGreaterThan(1);
|
|
154
|
-
expect(
|
|
191
|
+
expect(onMessageDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
155
192
|
|
|
156
193
|
expect(onReasoningDeltaSpy).toHaveBeenCalled();
|
|
157
194
|
expect(onReasoningDeltaSpy.mock.calls.length).toBeGreaterThan(1);
|
|
158
|
-
expect(
|
|
195
|
+
expect(onReasoningDeltaSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
159
196
|
|
|
160
197
|
expect(onRunStepSpy).toHaveBeenCalled();
|
|
161
198
|
expect(onRunStepSpy.mock.calls.length).toBeGreaterThan(0);
|
|
162
|
-
expect(
|
|
163
|
-
|
|
199
|
+
expect(onRunStepSpy.mock.calls[0][3]).toBeDefined(); // Graph exists
|
|
164
200
|
});
|
|
165
|
-
});
|
|
201
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { HumanMessage } from '@langchain/core/messages';
|
|
2
|
+
import { createTokenCounter } from '@/utils/tokens';
|
|
3
|
+
|
|
4
|
+
describe('Token encoder memoization', () => {
|
|
5
|
+
jest.setTimeout(45000);
|
|
6
|
+
|
|
7
|
+
test('fetches BPE once and reuses encoder across counters', async () => {
|
|
8
|
+
const originalFetch = global.fetch;
|
|
9
|
+
let fetchCalls = 0;
|
|
10
|
+
global.fetch = (async (...args: Parameters<typeof fetch>) => {
|
|
11
|
+
fetchCalls += 1;
|
|
12
|
+
// Delegate to real fetch
|
|
13
|
+
return await originalFetch(...args);
|
|
14
|
+
}) as typeof fetch;
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
const counter1 = await createTokenCounter();
|
|
18
|
+
const counter2 = await createTokenCounter();
|
|
19
|
+
|
|
20
|
+
const m1 = new HumanMessage('hello world');
|
|
21
|
+
const m2 = new HumanMessage('another short text');
|
|
22
|
+
|
|
23
|
+
const c11 = counter1(m1);
|
|
24
|
+
const c12 = counter1(m2);
|
|
25
|
+
const c21 = counter2(m1);
|
|
26
|
+
const c22 = counter2(m2);
|
|
27
|
+
|
|
28
|
+
expect(c11).toBeGreaterThan(0);
|
|
29
|
+
expect(c12).toBeGreaterThan(0);
|
|
30
|
+
expect(c21).toBe(c11);
|
|
31
|
+
expect(c22).toBe(c12);
|
|
32
|
+
|
|
33
|
+
// Only one fetch for the shared encoder
|
|
34
|
+
expect(fetchCalls).toBe(1);
|
|
35
|
+
} finally {
|
|
36
|
+
global.fetch = originalFetch;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|