@xalia/agent 0.6.9 → 0.6.10
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/.env.development +6 -1
- package/.env.test +7 -0
- package/README.md +11 -0
- package/context_system.md +498 -0
- package/dist/agent/src/agent/agent.js +77 -18
- package/dist/agent/src/agent/agentUtils.js +3 -2
- package/dist/agent/src/agent/documentSummarizer.js +126 -0
- package/dist/agent/src/agent/dummyLLM.js +25 -22
- package/dist/agent/src/agent/imageGenLLM.js +22 -19
- package/dist/agent/src/agent/llm.js +1 -1
- package/dist/agent/src/agent/openAILLM.js +15 -12
- package/dist/agent/src/agent/openAILLMStreaming.js +68 -37
- package/dist/agent/src/agent/repeatLLM.js +16 -7
- package/dist/agent/src/agent/tokenCounter.js +390 -0
- package/dist/agent/src/agent/tokenCounter.test.js +206 -0
- package/dist/agent/src/agent/toolSettings.js +17 -0
- package/dist/agent/src/agent/tools/calculatorTool.js +45 -0
- package/dist/agent/src/agent/tools/contentExtractors/pdfToText.js +55 -0
- package/dist/agent/src/agent/tools/datetimeTool.js +38 -0
- package/dist/agent/src/agent/tools/fileManager/fileManagerTool.js +156 -0
- package/dist/agent/src/agent/tools/fileManager/index.js +31 -0
- package/dist/agent/src/agent/tools/fileManager/memoryFileManager.js +102 -0
- package/dist/agent/src/{chat/data → agent/tools/fileManager}/mimeTypes.js +3 -1
- package/dist/agent/src/agent/tools/fileManager/prompt.js +33 -0
- package/dist/agent/src/{chat/data/dbSessionFileModels.js → agent/tools/fileManager/types.js} +7 -0
- package/dist/agent/src/agent/tools/index.js +64 -0
- package/dist/agent/src/agent/tools/openUrlTool.js +57 -0
- package/dist/agent/src/agent/tools/renderTool.js +89 -0
- package/dist/agent/src/agent/tools/utils.js +61 -0
- package/dist/agent/src/{chat/utils/search.js → agent/tools/webSearch.js} +1 -2
- package/dist/agent/src/agent/tools/webSearchTool.js +40 -0
- package/dist/agent/src/chat/client/chatClient.js +28 -0
- package/dist/agent/src/chat/client/index.js +4 -1
- package/dist/agent/src/chat/client/sessionClient.js +28 -2
- package/dist/agent/src/chat/constants.js +8 -0
- package/dist/agent/src/chat/data/dbSessionFiles.js +11 -6
- package/dist/agent/src/chat/protocol/messages.js +5 -0
- package/dist/agent/src/chat/server/chatContextManager.js +45 -25
- package/dist/agent/src/chat/server/conversation.js +3 -0
- package/dist/agent/src/chat/server/imageGeneratorTools.js +20 -8
- package/dist/agent/src/chat/server/openAIRouterLLM.js +0 -3
- package/dist/agent/src/chat/server/openSession.js +218 -55
- package/dist/agent/src/chat/server/promptRefiner.js +86 -0
- package/dist/agent/src/chat/server/server.js +5 -1
- package/dist/agent/src/chat/server/sessionFileManager.js +22 -221
- package/dist/agent/src/chat/server/sessionRegistry.js +87 -0
- package/dist/agent/src/chat/server/titleGenerator.js +112 -0
- package/dist/agent/src/chat/server/titleGenerator.test.js +113 -0
- package/dist/agent/src/chat/server/tools.js +63 -287
- package/dist/agent/src/chat/utils/approvalManager.js +6 -3
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +3 -0
- package/dist/agent/src/test/agent.test.js +16 -17
- package/dist/agent/src/test/chatContextManager.test.js +15 -3
- package/dist/agent/src/test/dbMcpServerConfigs.test.js +4 -4
- package/dist/agent/src/test/dbSessionFiles.test.js +17 -17
- package/dist/agent/src/test/testTools.js +6 -1
- package/dist/agent/src/test/tools.test.js +27 -9
- package/dist/agent/src/tool/agentChat.js +5 -2
- package/dist/agent/src/tool/chatMain.js +34 -7
- package/dist/agent/src/tool/commandPrompt.js +2 -2
- package/dist/agent/src/tool/files.js +7 -8
- package/package.json +4 -1
- package/scripts/test_chat +195 -176
- package/src/agent/agent.ts +98 -23
- package/src/agent/agentUtils.ts +3 -2
- package/src/agent/documentSummarizer.ts +157 -0
- package/src/agent/dummyLLM.ts +27 -23
- package/src/agent/imageGenLLM.ts +28 -24
- package/src/agent/llm.ts +2 -2
- package/src/agent/openAILLM.ts +17 -13
- package/src/agent/openAILLMStreaming.ts +80 -41
- package/src/agent/repeatLLM.ts +19 -7
- package/src/agent/test_data/harrypotter.txt +6065 -0
- package/src/agent/tokenCounter.test.ts +243 -0
- package/src/agent/tokenCounter.ts +483 -0
- package/src/agent/toolSettings.ts +24 -0
- package/src/agent/tools/calculatorTool.ts +50 -0
- package/src/agent/tools/contentExtractors/pdfToText.ts +60 -0
- package/src/agent/tools/datetimeTool.ts +41 -0
- package/src/agent/tools/fileManager/fileManagerTool.ts +199 -0
- package/src/agent/tools/fileManager/index.ts +50 -0
- package/src/agent/tools/fileManager/memoryFileManager.ts +120 -0
- package/src/{chat/data → agent/tools/fileManager}/mimeTypes.ts +3 -1
- package/src/agent/tools/fileManager/prompt.ts +38 -0
- package/src/{chat/data/dbSessionFileModels.ts → agent/tools/fileManager/types.ts} +76 -0
- package/src/agent/tools/index.ts +49 -0
- package/src/agent/tools/openUrlTool.ts +62 -0
- package/src/agent/tools/renderTool.ts +92 -0
- package/src/agent/tools/utils.ts +74 -0
- package/src/{chat/utils/search.ts → agent/tools/webSearch.ts} +0 -1
- package/src/agent/tools/webSearchTool.ts +44 -0
- package/src/chat/client/chatClient.ts +45 -0
- package/src/chat/client/index.ts +3 -0
- package/src/chat/client/sessionClient.ts +40 -3
- package/src/chat/client/sessionFiles.ts +1 -1
- package/src/chat/constants.ts +6 -0
- package/src/chat/data/dataModels.ts +6 -0
- package/src/chat/data/dbSessionFiles.ts +12 -4
- package/src/chat/protocol/messages.ts +60 -7
- package/src/chat/server/chatContextManager.ts +58 -37
- package/src/chat/server/conversation.ts +3 -0
- package/src/chat/server/imageGeneratorTools.ts +31 -12
- package/src/chat/server/openAIRouterLLM.ts +1 -4
- package/src/chat/server/openSession.ts +323 -67
- package/src/chat/server/promptRefiner.ts +106 -0
- package/src/chat/server/server.ts +4 -1
- package/src/chat/server/sessionFileManager.ts +35 -306
- package/src/chat/server/sessionRegistry.ts +128 -0
- package/src/chat/server/titleGenerator.test.ts +103 -0
- package/src/chat/server/titleGenerator.ts +143 -0
- package/src/chat/server/tools.ts +77 -304
- package/src/chat/utils/approvalManager.ts +9 -3
- package/src/chat/utils/multiAsyncQueue.ts +4 -0
- package/src/test/agent.test.ts +17 -23
- package/src/test/chatContextManager.test.ts +29 -4
- package/src/test/dbMcpServerConfigs.test.ts +4 -4
- package/src/test/dbSessionFiles.test.ts +16 -16
- package/src/test/testTools.ts +8 -3
- package/src/test/tools.test.ts +30 -5
- package/src/tool/agentChat.ts +12 -3
- package/src/tool/chatMain.ts +33 -6
- package/src/tool/commandPrompt.ts +2 -2
- package/src/tool/files.ts +1 -3
- package/dist/agent/src/agent/tools.js +0 -44
- package/src/agent/tools.ts +0 -57
- /package/dist/agent/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.js +0 -0
- /package/src/{chat/utils → agent/tools/contentExtractors}/htmlToText.ts +0 -0
package/scripts/test_chat
CHANGED
|
@@ -45,8 +45,9 @@ function stop_chat_server() {
|
|
|
45
45
|
stop_chat_server
|
|
46
46
|
|
|
47
47
|
# Start the server with full logging and short heartbeat timeout.
|
|
48
|
-
export DEFAULT_LLM_MODEL=repeat
|
|
49
|
-
export
|
|
48
|
+
export DEFAULT_LLM_MODEL="repeat:Main agent message"
|
|
49
|
+
export RACE_MODE_MODEL="repeat:Agent B message"
|
|
50
|
+
export TEST=1
|
|
50
51
|
export GEN_IMAGE_MODEL=dummy:test_data/dummyllm_script_image_gen.json
|
|
51
52
|
|
|
52
53
|
LOG_LEVEL=debug \
|
|
@@ -99,27 +100,27 @@ pushd _test_chat
|
|
|
99
100
|
${client} agent-profile set profile0 --profile agent_profile.json | jq -r .uuid > agent_profile.uuid
|
|
100
101
|
agent_profile_id=`cat agent_profile.uuid`
|
|
101
102
|
|
|
102
|
-
# User 0 tries to join with invalid API key - fails with "invalid api key" error
|
|
103
|
+
# # User 0 tries to join with invalid API key - fails with "invalid api key" error
|
|
103
104
|
|
|
104
|
-
invalid_apikey0="invalid_api_key"
|
|
105
|
-
${agent} chat client --session-id no_such_session --api-key "${invalid_apikey0}" 2>&1 | tee invalid_apikey_test.log | grep -i "invalid api key" || \
|
|
106
|
-
|
|
105
|
+
# invalid_apikey0="invalid_api_key"
|
|
106
|
+
# ${agent} chat client --session-id no_such_session --api-key "${invalid_apikey0}" 2>&1 | tee invalid_apikey_test.log | grep -i "invalid api key" || \
|
|
107
|
+
# (echo "Expected 'invalid api key' error message"; cat invalid_apikey_test.log; exit 1)
|
|
107
108
|
|
|
108
|
-
# User 0 tries to join non-existant session - fails
|
|
109
|
+
# # User 0 tries to join non-existant session - fails
|
|
109
110
|
|
|
110
|
-
${agent} chat clear-sessions
|
|
111
|
-
${agent} chat client --session-id no_such_session && \
|
|
112
|
-
|
|
111
|
+
# ${agent} chat clear-sessions
|
|
112
|
+
# ${agent} chat client --session-id no_such_session && \
|
|
113
|
+
# (echo "Should not be able to join"; exit 1)
|
|
113
114
|
|
|
114
|
-
# User 0 creates a new session with `crash_agent_profile` and interacts with
|
|
115
|
-
# it, exercising LLM error conditions.
|
|
115
|
+
# # User 0 creates a new session with `crash_agent_profile` and interacts with
|
|
116
|
+
# # it, exercising LLM error conditions.
|
|
116
117
|
|
|
117
|
-
echo ':si crash_session.id' > crash_script
|
|
118
|
-
echo 'Hello' >> crash_script
|
|
119
|
-
echo 'Hello again' >> crash_script
|
|
120
|
-
${agent} chat client --session-title crash_test_session \
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
# echo ':si crash_session.id' > crash_script
|
|
119
|
+
# echo 'Hello' >> crash_script
|
|
120
|
+
# echo 'Hello again' >> crash_script
|
|
121
|
+
# ${agent} chat client --session-title crash_test_session \
|
|
122
|
+
# --agent-profile-id ${crash_agent_profile_id} \
|
|
123
|
+
# --script crash_script
|
|
123
124
|
|
|
124
125
|
# User 0 creates empty session, which should not appear in the DB.
|
|
125
126
|
|
|
@@ -133,165 +134,183 @@ pushd _test_chat
|
|
|
133
134
|
|
|
134
135
|
# User 0 creates a user session, sends message and writes session id to file
|
|
135
136
|
|
|
136
|
-
echo ':
|
|
137
|
-
echo 'tell me a joke'
|
|
138
|
-
echo ':
|
|
139
|
-
echo ':
|
|
140
|
-
echo '
|
|
141
|
-
echo 'now please' >> init_session
|
|
142
|
-
${agent} chat client --session-title test_session \
|
|
143
|
-
--agent-profile-id ${agent_profile_id} \
|
|
144
|
-
--script init_session
|
|
145
|
-
session_id=`cat session.id`
|
|
146
|
-
guest_key=`cat guest.key`
|
|
147
|
-
|
|
148
|
-
# Check that the session got saved with the updated title
|
|
149
|
-
|
|
150
|
-
${agent} chat list-sessions | grep '"title":"tell me a joke"'
|
|
151
|
-
|
|
152
|
-
# Check that a guest can join with the guest key
|
|
153
|
-
|
|
154
|
-
echo ":sleep 1000" > guest_script
|
|
155
|
-
echo "Hello from the guest" >> guest_script
|
|
137
|
+
echo ':si session.id' > race_mode
|
|
138
|
+
echo 'tell me a joke' > race_mode
|
|
139
|
+
echo 'race: tell me a longer joke' >> race_mode
|
|
140
|
+
echo ':sleep 10000' >> race_mode
|
|
141
|
+
echo 'B' >> race_mode
|
|
156
142
|
${agent} chat client \
|
|
157
|
-
--session-
|
|
158
|
-
--
|
|
159
|
-
--script
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
echo '
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
#
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
grep
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
#
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
#
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
|
|
239
|
-
#
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
echo ':
|
|
243
|
-
echo
|
|
244
|
-
echo ':
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
#
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
#
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
echo
|
|
264
|
-
|
|
265
|
-
echo
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
#
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
#
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
${agent} chat
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
#
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
143
|
+
--session-title test_session \
|
|
144
|
+
--agent-profile-id ${agent_profile_id} \
|
|
145
|
+
--script race_mode
|
|
146
|
+
|
|
147
|
+
# echo ':pause-agent 1' > init_session
|
|
148
|
+
# echo 'tell me a joke' >> init_session
|
|
149
|
+
# echo ':si session.id' >> init_session
|
|
150
|
+
# echo ':share-session guest.key' >> init_session
|
|
151
|
+
# echo ':pause-agent 0' >> init_session
|
|
152
|
+
# echo 'now please' >> init_session
|
|
153
|
+
# ${agent} chat client --session-title test_session \
|
|
154
|
+
# --agent-profile-id ${agent_profile_id} \
|
|
155
|
+
# --script init_session
|
|
156
|
+
# session_id=`cat session.id`
|
|
157
|
+
# guest_key=`cat guest.key`
|
|
158
|
+
|
|
159
|
+
# # Check that the session got saved with the updated title
|
|
160
|
+
|
|
161
|
+
# ${agent} chat list-sessions | grep '"title":"tell me a joke"'
|
|
162
|
+
|
|
163
|
+
# # Try race-mode
|
|
164
|
+
# echo 'race: tell me a better joke' > race_mode
|
|
165
|
+
# echo ':sleep 5000' >> race_mode
|
|
166
|
+
# echo 'B' >> race_mode
|
|
167
|
+
# ${agent} chat client \
|
|
168
|
+
# --session-id ${session_id} \
|
|
169
|
+
# --script race_mode | tee race_mode_output.txt
|
|
170
|
+
|
|
171
|
+
# # Check that a guest can join with the guest key
|
|
172
|
+
|
|
173
|
+
# echo ":sleep 1000" > guest_script
|
|
174
|
+
# echo "Hello from the guest" >> guest_script
|
|
175
|
+
# ${agent} chat client \
|
|
176
|
+
# --session-id ${session_id} \
|
|
177
|
+
# --api-key ${guest_key} \
|
|
178
|
+
# --script guest_script | tee guest_output.txt
|
|
179
|
+
# grep "joke" guest_output.txt
|
|
180
|
+
# grep "[Guest]" guest_output.txt
|
|
181
|
+
|
|
182
|
+
# # Dummy session to invoke the image generation
|
|
183
|
+
|
|
184
|
+
# echo '{"system_prompt":"prompt","model":"dummy:test_data/dummyllm_script_invoke_image_gen_tool.json","mcp_settings":{}}' > invoke_gen_image_profile.json
|
|
185
|
+
# ${client} agent-profile set invoke_gen_image_agent_profile \
|
|
186
|
+
# --profile invoke_gen_image_profile.json \
|
|
187
|
+
# | jq -r .uuid > invoke_gen_image_profile.uuid
|
|
188
|
+
# invoke_gen_image_profile_id=`cat invoke_gen_image_profile.uuid`
|
|
189
|
+
|
|
190
|
+
# echo "Choose an animal and generate an image of it" > invoke_gen_image_script
|
|
191
|
+
# echo ":list-files" >> invoke_gen_image_script
|
|
192
|
+
# echo ":si invoke_gen_image.id" >> invoke_gen_image_script
|
|
193
|
+
# echo ':add-custom-mcp-server custom-mcp http://localhost:8002' >> invoke_gen_image_script
|
|
194
|
+
# echo ':list-custom-mcp-servers' >> invoke_gen_image_script
|
|
195
|
+
|
|
196
|
+
# LOG_LEVEL=debug \
|
|
197
|
+
# ${agent} chat client \
|
|
198
|
+
# --session-title invoke_gen_image_session \
|
|
199
|
+
# --agent-profile-id ${invoke_gen_image_profile_id} \
|
|
200
|
+
# --script invoke_gen_image_script | tee invoke_gen_image_output.txt
|
|
201
|
+
# grep 'frog.png' invoke_gen_image_output.txt
|
|
202
|
+
# grep '"xalia/fileMimeType":"image/png"' invoke_gen_image_output.txt
|
|
203
|
+
# grep 'structuredContent example' invoke_gen_image_output.txt
|
|
204
|
+
# grep '_meta example' invoke_gen_image_output.txt
|
|
205
|
+
# grep 'custom-mcp": ' invoke_gen_image_output.txt
|
|
206
|
+
|
|
207
|
+
# # User 1 tries to join the session. Should be rejected.
|
|
208
|
+
|
|
209
|
+
# ${agent} chat client --session-id ${session_id} --api-key ${apikey1} \
|
|
210
|
+
# --script init_session > join_unauthorized.log 2>&1 && \
|
|
211
|
+
# (echo "Should exit with error when joining invalid session"; exit 1)
|
|
212
|
+
|
|
213
|
+
# grep -i "not authorized" join_unauthorized.log || \
|
|
214
|
+
# (echo "Should include error message"; exit 1)
|
|
215
|
+
|
|
216
|
+
# # User 0 create a team, and write team id to a file
|
|
217
|
+
|
|
218
|
+
# echo ':ct team0' > script_create_team
|
|
219
|
+
# echo ':ci team0.id' >> script_create_team
|
|
220
|
+
# ${agent} chat client \
|
|
221
|
+
# --session-id ${session_id} \
|
|
222
|
+
# --script script_create_team
|
|
223
|
+
# team_id=`cat team0.id`
|
|
224
|
+
|
|
225
|
+
# # User 0 create a new team session (including a new paused agent), and write
|
|
226
|
+
# # session id to a file. Extract the agent_profile UUID.
|
|
227
|
+
|
|
228
|
+
# echo ':pause-agent 1' > script_team_chat
|
|
229
|
+
# echo 'tell me a joke' >> script_team_chat
|
|
230
|
+
# echo ':si team_session.id' >> script_team_chat
|
|
231
|
+
# ${agent} chat client --session-title test_team_session \
|
|
232
|
+
# --team-id ${team_id} \
|
|
233
|
+
# --script script_team_chat | tee team_chat_1
|
|
234
|
+
# team_session_id=`cat team_session.id`
|
|
235
|
+
|
|
236
|
+
# get_profile_id_query='.[] | select(.uuid=="'${team_session_id}'") | .agent_profile_uuid'
|
|
237
|
+
# ${agent} chat list-sessions | jq -r "${get_profile_id_query}" > team_agent.id
|
|
238
|
+
# team_agent_id=`cat team_agent.id`
|
|
239
|
+
|
|
240
|
+
# # Check that the agent has not said anything
|
|
241
|
+
|
|
242
|
+
# grep AGENT team_chat_1 && \
|
|
243
|
+
# (echo "Unexpected AGENT messages"; exit 1)
|
|
244
|
+
|
|
245
|
+
# # User 1 tries to join the team session. Should be rejected.
|
|
246
|
+
|
|
247
|
+
# ${agent} chat client --session-id ${team_session_id} --api-key ${apikey1} \
|
|
248
|
+
# --script init_session > join_unauthorized_team.log 2>&1 && \
|
|
249
|
+
# (echo "Should exit with error when joining invalid team session"; exit 1)
|
|
250
|
+
# grep -i "not authorized" join_unauthorized_team.log || \
|
|
251
|
+
# (echo "Should include error message (no auth team session)"; exit 1)
|
|
252
|
+
|
|
253
|
+
# # User0 sets up the session
|
|
254
|
+
# # add charuser1 to the team
|
|
255
|
+
# # add duckduckgo-search
|
|
256
|
+
# # add a session file (md file)
|
|
257
|
+
# # add an image to the workspace
|
|
258
|
+
# # unpause the agent
|
|
259
|
+
|
|
260
|
+
# echo ":ap ${team_id} chatuser1" > script_setup_team_session
|
|
261
|
+
# echo ':lp' >> script_setup_team_session
|
|
262
|
+
# echo ":as duckduckgo-search" >> script_setup_team_session
|
|
263
|
+
# echo ':put-file plan.md data:text/markdown,#PLAN\n##STEP1\n##STEP2\n' \
|
|
264
|
+
# >> script_setup_team_session
|
|
265
|
+
# echo ':sw file:../test_data/frog.png For context, this image represents our shared workspace' >> script_setup_team_session
|
|
266
|
+
# echo ':pause-agent 0' >> script_setup_team_session
|
|
267
|
+
# ${agent} chat client \
|
|
268
|
+
# --session-id ${team_session_id} \
|
|
269
|
+
# --script script_setup_team_session
|
|
270
|
+
|
|
271
|
+
# # checks the DB for chatuser2 participant and duckduckgo mcp server
|
|
272
|
+
|
|
273
|
+
# ${agent} chat list-participants --session ${team_session_id} | grep chatuser1
|
|
274
|
+
# ${client} agent-profile get ${team_agent_id} | grep duckduckgo
|
|
275
|
+
|
|
276
|
+
# # Check the state. Clean the workspace, file and mcp servers
|
|
277
|
+
|
|
278
|
+
# echo ':sleep 1000' > script_check_state
|
|
279
|
+
# echo ':list-files filelist.json' >> script_check_state
|
|
280
|
+
# echo 'Tell me about the image in our workspace?' >> script_check_state
|
|
281
|
+
# echo ':sw' >> script_check_state
|
|
282
|
+
# echo 'How many steps are in plan.md?' >> script_check_state
|
|
283
|
+
# echo ':sleep 5000' >> script_check_state
|
|
284
|
+
# echo ':delete-file plan.md' >> script_check_state
|
|
285
|
+
# echo ':rs duckduckgo-search' >> script_check_state
|
|
286
|
+
# ${agent} chat client \
|
|
287
|
+
# --session-id ${team_session_id} \
|
|
288
|
+
# --script script_check_state
|
|
289
|
+
|
|
290
|
+
# sleep 1
|
|
291
|
+
|
|
292
|
+
# # Check duckduckgo-search got removed
|
|
293
|
+
# (${client} agent-profile get ${team_agent_id} | grep duckduckgo) && ( \
|
|
294
|
+
# echo "ERROR: expected duckduckgo to be removed from agent profile" ; \
|
|
295
|
+
# exit 1 \
|
|
296
|
+
# )
|
|
297
|
+
|
|
298
|
+
# # Check the file manager list
|
|
299
|
+
# grep 'plan.md' filelist.json
|
|
300
|
+
|
|
301
|
+
# # Run both clients with a script that continues the conversation
|
|
302
|
+
|
|
303
|
+
# echo -e "why is that funny?\ntell me another.\nwhat is my name?\nhow old are you?\nwhere are your parents?" > script1
|
|
304
|
+
# ${agent} chat client --session-id ${team_session_id} --script script1 &
|
|
305
|
+
# pid0=$!
|
|
306
|
+
# ${agent} chat client --session-id ${team_session_id} --api-key ${apikey1} --script script1 >chatuser1.output
|
|
307
|
+
# wait ${pid0}
|
|
308
|
+
|
|
309
|
+
# # The name chatuser0 and AGENT should appear in chatuser1's session output
|
|
310
|
+
|
|
311
|
+
# grep "Chat User0" chatuser1.output
|
|
312
|
+
# grep "Chat User1" chatuser1.output
|
|
313
|
+
# grep AGENT chatuser1.output
|
|
295
314
|
|
|
296
315
|
popd
|
|
297
316
|
|
package/src/agent/agent.ts
CHANGED
|
@@ -20,9 +20,15 @@ import { IAgentEventHandler } from "./iAgentEventHandler";
|
|
|
20
20
|
import { IContextManager, IContextTransaction } from "./context";
|
|
21
21
|
import { ChatCompletionContentPartImage } from "openai/resources";
|
|
22
22
|
|
|
23
|
+
import { MAX_TOOL_CALL_RESPONSE_LENGTH } from "./toolSettings";
|
|
24
|
+
|
|
23
25
|
export const DEFAULT_LLM_URL = "http://localhost:5001/v1";
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
/**
|
|
28
|
+
* The message to append to the agent output if the agent is interrupted by a
|
|
29
|
+
* signal from the user.
|
|
30
|
+
*/
|
|
31
|
+
export const USER_STOP_MESSAGE = " AGENT INTERRUPTED";
|
|
26
32
|
|
|
27
33
|
/**
|
|
28
34
|
* An agent's response, with optional extra image data.
|
|
@@ -102,10 +108,21 @@ type RegisteredTools = {
|
|
|
102
108
|
handler: ToolHandler;
|
|
103
109
|
};
|
|
104
110
|
|
|
111
|
+
/**
|
|
112
|
+
* An agent attached to an ILLM which updates a context via an
|
|
113
|
+
* IContextTransaction interface (where IContextTransaction is like a DB tx or
|
|
114
|
+
* DB writer, for staging changes and reading back state as-if those changes
|
|
115
|
+
* were applied).
|
|
116
|
+
*/
|
|
105
117
|
export class AgentEx {
|
|
106
118
|
mcpServerManager: McpServerManager;
|
|
107
119
|
llm: ILLM;
|
|
108
120
|
|
|
121
|
+
/// Flag to stop the Agent loop.
|
|
122
|
+
stopFlag: boolean;
|
|
123
|
+
/// Function to stop the LLM (only present while it is active)
|
|
124
|
+
stopFn: ((msg: string) => void) | undefined;
|
|
125
|
+
|
|
109
126
|
/// The full list of tools, ready to pass to the LLM
|
|
110
127
|
private tools: ToolDescriptor[] = [];
|
|
111
128
|
|
|
@@ -116,12 +133,22 @@ export class AgentEx {
|
|
|
116
133
|
constructor(mcpServerManager: McpServerManager, llm: ILLM) {
|
|
117
134
|
this.mcpServerManager = mcpServerManager;
|
|
118
135
|
this.llm = llm;
|
|
136
|
+
this.stopFlag = false;
|
|
137
|
+
this.stopFn = undefined;
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
public async shutdown(): Promise<void> {
|
|
141
|
+
this.stop("shutting down");
|
|
122
142
|
return this.mcpServerManager.shutdown();
|
|
123
143
|
}
|
|
124
144
|
|
|
145
|
+
public stop(msg?: string) {
|
|
146
|
+
this.stopFlag = true;
|
|
147
|
+
if (this.stopFn) {
|
|
148
|
+
this.stopFn(msg || USER_STOP_MESSAGE);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
125
152
|
public getMcpServerManager(): McpServerManager {
|
|
126
153
|
return this.mcpServerManager;
|
|
127
154
|
}
|
|
@@ -131,6 +158,8 @@ export class AgentEx {
|
|
|
131
158
|
contextTx: IContextTransaction,
|
|
132
159
|
eventHandler: IAgentEventHandler
|
|
133
160
|
): Promise<AssistantResponse | undefined> {
|
|
161
|
+
this.stopFlag = false;
|
|
162
|
+
|
|
134
163
|
// New user messages have already been added to the `contextTx`.
|
|
135
164
|
|
|
136
165
|
// Image and audio handling
|
|
@@ -157,6 +186,12 @@ export class AgentEx {
|
|
|
157
186
|
// While there are tool calls to make, invoke them and loop
|
|
158
187
|
|
|
159
188
|
while (message.tool_calls && message.tool_calls.length > 0) {
|
|
189
|
+
// Signal the event handler of the assistant message with tool calls
|
|
190
|
+
// BEFORE processing tool results. This ensures the order of messages
|
|
191
|
+
// in pendingMessages matches the order in the LLM context:
|
|
192
|
+
// [user, assistant(tool_calls), tool_result, assistant(final)]
|
|
193
|
+
eventHandler.onCompletion(message);
|
|
194
|
+
|
|
160
195
|
// TODO: Execute all tool calls in parallel
|
|
161
196
|
|
|
162
197
|
// [indexInContext, ToolCallResult][]
|
|
@@ -180,6 +215,12 @@ export class AgentEx {
|
|
|
180
215
|
const toolResultHandle = contextTx.addMessage(toolResult);
|
|
181
216
|
toolCallResults.push([toolResultHandle, result]);
|
|
182
217
|
|
|
218
|
+
// Immediately broadcast the tool result to the frontend for UI
|
|
219
|
+
// feedback. This ensures the frontend knows the tool executed
|
|
220
|
+
// successfully without waiting for the next LLM completion to
|
|
221
|
+
// finish streaming
|
|
222
|
+
eventHandler.onToolCallResult(toolResult);
|
|
223
|
+
|
|
183
224
|
// If the tool call requested that its args be redacted, this can be
|
|
184
225
|
// done now - before the next LLM invocation.
|
|
185
226
|
|
|
@@ -192,11 +233,6 @@ export class AgentEx {
|
|
|
192
233
|
}
|
|
193
234
|
}
|
|
194
235
|
|
|
195
|
-
// Now that any args have been overwritten, signal the event handler of
|
|
196
|
-
// the prevoius completion.
|
|
197
|
-
|
|
198
|
-
eventHandler.onCompletion(message);
|
|
199
|
-
|
|
200
236
|
// Get a new completion using the untouched tool call results. Note
|
|
201
237
|
// that, since we are deferring the `onToolCallResult` calls (so they
|
|
202
238
|
// can be redacted), we must take care that the errors in
|
|
@@ -212,19 +248,16 @@ export class AgentEx {
|
|
|
212
248
|
contextTx.addMessage(message);
|
|
213
249
|
} finally {
|
|
214
250
|
// Now that the tool call results have been passed to the LLM, perform
|
|
215
|
-
// any updates on them.
|
|
216
|
-
//
|
|
217
|
-
// error occured, so that the caller has an up-to-date picture of the
|
|
218
|
-
// context state when the error occured.
|
|
251
|
+
// any updates on them if overwriteResponse was requested. If so, send
|
|
252
|
+
// the updated tool result to the frontend to replace the original.
|
|
219
253
|
|
|
220
254
|
toolCallResults.forEach(([handle, tcr]) => {
|
|
221
|
-
const ctxMsg = contextTx.getMessage(handle);
|
|
222
255
|
if (tcr.overwriteResponse) {
|
|
256
|
+
const ctxMsg = contextTx.getMessage(handle);
|
|
223
257
|
ctxMsg.content = tcr.overwriteResponse;
|
|
258
|
+
assert(ctxMsg.role === "tool");
|
|
259
|
+
eventHandler.onToolCallResult(ctxMsg);
|
|
224
260
|
}
|
|
225
|
-
|
|
226
|
-
assert(ctxMsg.role === "tool");
|
|
227
|
-
eventHandler.onToolCallResult(ctxMsg);
|
|
228
261
|
});
|
|
229
262
|
|
|
230
263
|
// Note, if an error DID occur, the ContextManager does not see any of
|
|
@@ -242,6 +275,27 @@ export class AgentEx {
|
|
|
242
275
|
context: MessageParam[],
|
|
243
276
|
eventHandler: IAgentEventHandler
|
|
244
277
|
): Promise<Completion> {
|
|
278
|
+
if (this.stopFlag) {
|
|
279
|
+
return {
|
|
280
|
+
id: "user_stopped",
|
|
281
|
+
choices: [
|
|
282
|
+
{
|
|
283
|
+
finish_reason: "stop",
|
|
284
|
+
index: 0,
|
|
285
|
+
message: {
|
|
286
|
+
content: USER_STOP_MESSAGE,
|
|
287
|
+
role: "assistant",
|
|
288
|
+
refusal: null,
|
|
289
|
+
},
|
|
290
|
+
logprobs: null,
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
created: Date.now(),
|
|
294
|
+
model: this.llm.getModel(),
|
|
295
|
+
object: "chat.completion",
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
245
299
|
// Compute the full list of available tools
|
|
246
300
|
|
|
247
301
|
let tools: ToolDescriptor[] | undefined;
|
|
@@ -252,12 +306,27 @@ export class AgentEx {
|
|
|
252
306
|
tools = enabledTools;
|
|
253
307
|
}
|
|
254
308
|
logger.debug(`[chatCompletion] tools: ${JSON.stringify(tools)}`);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
309
|
+
|
|
310
|
+
// Log system prompt length
|
|
311
|
+
if (context.length > 0 && context[0].role === "system") {
|
|
312
|
+
const systemPrompt = context[0].content as string;
|
|
313
|
+
logger.info(
|
|
314
|
+
`[chatCompletion] System prompt length: ${String(systemPrompt.length)}`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const { stop, completion: completionP } =
|
|
319
|
+
await this.llm.getConversationResponse(
|
|
320
|
+
context,
|
|
321
|
+
tools,
|
|
322
|
+
eventHandler.onAgentMessage.bind(eventHandler),
|
|
323
|
+
eventHandler.onReasoning.bind(eventHandler)
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
this.stopFn = stop;
|
|
327
|
+
const completion = await completionP;
|
|
328
|
+
this.stopFn = undefined;
|
|
329
|
+
|
|
261
330
|
logger.debug(`Received chat completion ${JSON.stringify(completion)}`);
|
|
262
331
|
return completion;
|
|
263
332
|
}
|
|
@@ -310,6 +379,10 @@ export class AgentEx {
|
|
|
310
379
|
toolCall: MessageToolCall,
|
|
311
380
|
eventHandler: IAgentEventHandler
|
|
312
381
|
): Promise<ToolCallResult> {
|
|
382
|
+
if (this.stopFlag) {
|
|
383
|
+
return { response: USER_STOP_MESSAGE };
|
|
384
|
+
}
|
|
385
|
+
|
|
313
386
|
// If the tool is and "agent" (internal) tool, we can just execute it.
|
|
314
387
|
// Otherwise, call the event handler to get permission and invoke the
|
|
315
388
|
// external tool handler.
|
|
@@ -390,8 +463,10 @@ export class AgentEx {
|
|
|
390
463
|
}
|
|
391
464
|
|
|
392
465
|
/**
|
|
393
|
-
* Higher-level abstraction over AgentEx, which abstracts out the
|
|
394
|
-
*
|
|
466
|
+
* Higher-level abstraction over AgentEx, which abstracts out the context
|
|
467
|
+
* transactions. A single agent is associated with an IContextManager and
|
|
468
|
+
* internally creates and commits transactions during each call to
|
|
469
|
+
* `userMessage*`.
|
|
395
470
|
*/
|
|
396
471
|
export class Agent implements IConversation {
|
|
397
472
|
private eventHandler: IAgentEventHandler;
|
|
@@ -564,7 +639,7 @@ export function createUserMessageEnsure(
|
|
|
564
639
|
name?: string
|
|
565
640
|
): UserMessageParam {
|
|
566
641
|
const userMsg = createUserMessage(msg, imageB64, name);
|
|
567
|
-
assert(userMsg);
|
|
642
|
+
assert(userMsg, "createUserMessageEnsure");
|
|
568
643
|
return userMsg;
|
|
569
644
|
}
|
|
570
645
|
|
package/src/agent/agentUtils.ts
CHANGED
|
@@ -125,8 +125,9 @@ export async function createSpecializedLLM(
|
|
|
125
125
|
|
|
126
126
|
if (model && model.startsWith("dummy:")) {
|
|
127
127
|
llm = await DummyLLM.initFromModelUrl(model, platform);
|
|
128
|
-
} else if (model
|
|
129
|
-
|
|
128
|
+
} else if (model && model.startsWith("repeat")) {
|
|
129
|
+
const prefix = model.startsWith("repeat:") ? model.slice(7) : "";
|
|
130
|
+
llm = new RepeatLLM(prefix);
|
|
130
131
|
}
|
|
131
132
|
return llm;
|
|
132
133
|
}
|