@xalia/agent 0.6.4 → 0.6.5
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/agent/src/agent/agent.js +12 -11
- package/dist/agent/src/agent/dummyLLM.js +24 -15
- package/dist/agent/src/agent/llm.js +0 -22
- package/dist/agent/src/agent/mcpServerManager.js +73 -15
- package/dist/agent/src/agent/openAI.js +32 -0
- package/dist/agent/src/agent/openAILLM.js +25 -1
- package/dist/agent/src/agent/openAILLMStreaming.js +8 -3
- package/dist/agent/src/agent/sudoMcpServerManager.js +22 -9
- package/dist/agent/src/chat/client/chatClient.js +2 -1
- package/dist/agent/src/chat/client/sessionClient.js +28 -3
- package/dist/agent/src/chat/data/dbSessionFileModels.js +10 -0
- package/dist/agent/src/chat/protocol/messages.js +1 -0
- package/dist/agent/src/chat/server/chatContextManager.js +4 -4
- package/dist/agent/src/chat/server/conversation.js +1 -1
- package/dist/agent/src/chat/server/imageGeneratorTools.js +7 -4
- package/dist/agent/src/chat/server/openSession.js +85 -12
- package/dist/agent/src/chat/server/sessionFileManager.js +17 -6
- package/dist/agent/src/chat/server/sessionRegistry.js +1 -1
- package/dist/agent/src/chat/server/tools.js +58 -10
- package/dist/agent/src/test/agent.test.js +26 -2
- package/dist/agent/src/test/chatContextManager.test.js +5 -5
- package/dist/agent/src/test/mcpServerManager.test.js +5 -1
- package/dist/agent/src/test/testTools.js +5 -2
- package/dist/agent/src/tool/chatMain.js +23 -3
- package/dist/agent/src/tool/files.js +0 -27
- package/package.json +3 -3
- package/scripts/test_chat +3 -1
- package/src/agent/agent.ts +53 -47
- package/src/agent/agentUtils.ts +7 -7
- package/src/agent/compressingContextManager.ts +4 -9
- package/src/agent/context.ts +28 -37
- package/src/agent/dummyLLM.ts +38 -28
- package/src/agent/iAgentEventHandler.ts +6 -9
- package/src/agent/imageGenLLM.ts +11 -5
- package/src/agent/llm.ts +41 -106
- package/src/agent/mcpServerManager.ts +145 -29
- package/src/agent/openAI.ts +123 -0
- package/src/agent/openAILLM.ts +52 -5
- package/src/agent/openAILLMStreaming.ts +36 -32
- package/src/agent/repeatLLM.ts +5 -6
- package/src/agent/sudoMcpServerManager.ts +48 -16
- package/src/agent/tools.ts +3 -5
- package/src/chat/client/chatClient.ts +3 -1
- package/src/chat/client/sessionClient.ts +47 -7
- package/src/chat/data/dataModels.ts +3 -3
- package/src/chat/data/dbSessionFileModels.ts +22 -0
- package/src/chat/protocol/messages.ts +39 -13
- package/src/chat/server/chatContextManager.ts +20 -24
- package/src/chat/server/conversation.ts +10 -10
- package/src/chat/server/imageGeneratorTools.ts +18 -9
- package/src/chat/server/openSession.ts +111 -22
- package/src/chat/server/sessionFileManager.ts +33 -10
- package/src/chat/server/sessionRegistry.ts +1 -1
- package/src/chat/server/tools.ts +77 -18
- package/src/chat/utils/approvalManager.ts +2 -2
- package/src/test/agent.test.ts +56 -31
- package/src/test/approvalManager.test.ts +2 -2
- package/src/test/chatContextManager.test.ts +11 -14
- package/src/test/compressingContextManager.test.ts +3 -3
- package/src/test/context.test.ts +3 -3
- package/src/test/conversation.test.ts +7 -7
- package/src/test/dbSessionMessages.test.ts +3 -3
- package/src/test/mcpServerManager.test.ts +10 -1
- package/src/test/testTools.ts +44 -33
- package/src/tool/agentChat.ts +10 -8
- package/src/tool/agentMain.ts +2 -2
- package/src/tool/chatMain.ts +38 -6
- package/src/tool/commandPrompt.ts +2 -4
- package/src/tool/files.ts +0 -34
- package/test_data/dummyllm_script_image_gen.json +27 -17
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +9 -2
- package/test_data/dummyllm_script_render_tool.json +29 -0
- package/test_data/dummyllm_script_test_auto_approve.json +81 -0
- package/test_data/dummyllm_script_test_simplecalc_addition.json +29 -0
|
@@ -4,14 +4,14 @@ exports.Agent = exports.DEFAULT_LLM_URL = exports.AgentProfile = void 0;
|
|
|
4
4
|
exports.createUserMessage = createUserMessage;
|
|
5
5
|
exports.createUserMessageEnsure = createUserMessageEnsure;
|
|
6
6
|
exports.completionToAssistantMessageParam = completionToAssistantMessageParam;
|
|
7
|
-
const mcpServerManager_1 = require("./mcpServerManager");
|
|
8
7
|
const assert_1 = require("assert");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
var sdk_1 = require("@xalia/xmcp/sdk");
|
|
9
|
+
Object.defineProperty(exports, "AgentProfile", { enumerable: true, get: function () { return sdk_1.AgentProfile; } });
|
|
10
|
+
const sdk_2 = require("@xalia/xmcp/sdk");
|
|
11
|
+
const mcpServerManager_1 = require("./mcpServerManager");
|
|
12
12
|
exports.DEFAULT_LLM_URL = "http://localhost:5001/v1";
|
|
13
13
|
const MAX_TOOL_CALL_RESPONSE_LENGTH = 4000;
|
|
14
|
-
const logger = (0,
|
|
14
|
+
const logger = (0, sdk_2.getLogger)();
|
|
15
15
|
class Agent {
|
|
16
16
|
constructor(eventHandler, mcpServerManager, llm, contextManager) {
|
|
17
17
|
/// The full list of tools, ready to pass to the LLM
|
|
@@ -31,7 +31,7 @@ class Agent {
|
|
|
31
31
|
return this.mcpServerManager.shutdown();
|
|
32
32
|
}
|
|
33
33
|
getAgentProfile() {
|
|
34
|
-
return new
|
|
34
|
+
return new sdk_2.AgentProfile(this.llm.getModel(), this.getSystemPrompt(), this.mcpServerManager.getMcpServerSettings());
|
|
35
35
|
}
|
|
36
36
|
getConversation() {
|
|
37
37
|
const llmMessages = this.contextManager.getLLMContext();
|
|
@@ -89,10 +89,11 @@ class Agent {
|
|
|
89
89
|
role: "tool",
|
|
90
90
|
tool_call_id: toolCall.id,
|
|
91
91
|
content: result.response,
|
|
92
|
+
...(result._meta ? { _meta: result._meta } : {}),
|
|
93
|
+
...(result.structuredContent
|
|
94
|
+
? { structuredContent: result.structuredContent }
|
|
95
|
+
: {}),
|
|
92
96
|
};
|
|
93
|
-
if (result.metadata) {
|
|
94
|
-
toolResult.metadata = result.metadata;
|
|
95
|
-
}
|
|
96
97
|
context.push(toolResult);
|
|
97
98
|
// If the tool call requested that its args be redacted, this can be
|
|
98
99
|
// done now - before the next LLM invocation.
|
|
@@ -207,7 +208,7 @@ class Agent {
|
|
|
207
208
|
/**
|
|
208
209
|
* Handle the details of getting approval (if required), invoking the tool
|
|
209
210
|
* handler, informing the IAgentEventHandler of the result, and returns the
|
|
210
|
-
*
|
|
211
|
+
* ChatCompletionToolMessageParam to be used in the conversation.
|
|
211
212
|
*/
|
|
212
213
|
async doToolCall(toolCall) {
|
|
213
214
|
// If the tool is and "agent" (internal) tool, we can just execute it.
|
|
@@ -237,7 +238,7 @@ class Agent {
|
|
|
237
238
|
result = { response: "User denied tool request." };
|
|
238
239
|
}
|
|
239
240
|
else {
|
|
240
|
-
result =
|
|
241
|
+
result = await this.mcpServerManager.invoke(tc);
|
|
241
242
|
}
|
|
242
243
|
logger.debug(`tool call result ${JSON.stringify(result)}`);
|
|
243
244
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DummyLLM = void 0;
|
|
4
|
+
// import { OpenAI } from "openai";
|
|
4
5
|
const assert_1 = require("assert");
|
|
5
6
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
6
7
|
const logger = (0, sdk_1.getLogger)();
|
|
@@ -47,25 +48,33 @@ class DummyLLM {
|
|
|
47
48
|
getUrl() {
|
|
48
49
|
return "";
|
|
49
50
|
}
|
|
50
|
-
async getConversationResponse(messages, _tools, onMessage) {
|
|
51
|
+
async getConversationResponse(messages, _tools, onMessage, onReasoning) {
|
|
51
52
|
await new Promise((r) => setTimeout(r, 0));
|
|
52
53
|
(0, assert_1.strict)(this.idx < this.responses.length);
|
|
53
54
|
this.lastRequest = messages;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
for (;;) {
|
|
56
|
+
const response = this.responses[this.idx++];
|
|
57
|
+
if (response.finish_reason === "error") {
|
|
58
|
+
throw new Error(response.message);
|
|
59
|
+
}
|
|
60
|
+
if (response.finish_reason === "reasoning") {
|
|
61
|
+
if (onReasoning) {
|
|
62
|
+
await onReasoning(response.message);
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (onMessage) {
|
|
67
|
+
const message = response.message;
|
|
68
|
+
void onMessage(message.content || "", true);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
id: String(this.idx),
|
|
72
|
+
choices: [response],
|
|
73
|
+
created: Date.now(),
|
|
74
|
+
model: "dummyLlmModel",
|
|
75
|
+
object: "chat.completion",
|
|
76
|
+
};
|
|
61
77
|
}
|
|
62
|
-
return {
|
|
63
|
-
id: String(this.idx),
|
|
64
|
-
choices: [response],
|
|
65
|
-
created: Date.now(),
|
|
66
|
-
model: "dummyLlmModel",
|
|
67
|
-
object: "chat.completion",
|
|
68
|
-
};
|
|
69
78
|
}
|
|
70
79
|
setModel(_model) {
|
|
71
80
|
(0, assert_1.strict)(false, "unexpected call to setModel");
|
|
@@ -1,29 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.XALIA_APP_HEADER = void 0;
|
|
4
|
-
exports.choiceDeltaExtractReasoning = choiceDeltaExtractReasoning;
|
|
5
4
|
exports.XALIA_APP_HEADER = {
|
|
6
5
|
"HTTP-Referer": "xalia.ai",
|
|
7
6
|
"X-Title": "Xalia",
|
|
8
7
|
};
|
|
9
|
-
function choiceDeltaExtractReasoning(delta) {
|
|
10
|
-
if (delta.reasoning) {
|
|
11
|
-
return delta.reasoning;
|
|
12
|
-
}
|
|
13
|
-
if (delta.reasoning_details) {
|
|
14
|
-
let reasoning = "";
|
|
15
|
-
for (const details of delta.reasoning_details) {
|
|
16
|
-
if (details.type !== "reasoning.text") {
|
|
17
|
-
throw new Error(`unexpected details.type: ${details.type}`);
|
|
18
|
-
}
|
|
19
|
-
if (details.text) {
|
|
20
|
-
if (typeof details.text !== "string") {
|
|
21
|
-
throw new Error(`unexpected typeof details.text: ${typeof details.text}`);
|
|
22
|
-
}
|
|
23
|
-
reasoning += details.text;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return reasoning;
|
|
27
|
-
}
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
@@ -1,38 +1,56 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.McpServerManager = exports.McpServerInfoRW = exports.McpServerInfo = void 0;
|
|
4
|
+
exports.isMcpServerToolCallMetaData = isMcpServerToolCallMetaData;
|
|
4
5
|
exports.computeQualifiedName = computeQualifiedName;
|
|
5
6
|
exports.splitQualifiedName = splitQualifiedName;
|
|
6
7
|
exports.computeOpenAIToolList = computeOpenAIToolList;
|
|
7
8
|
exports.mcpToolToOpenAITool = mcpToolToOpenAITool;
|
|
8
9
|
const sse_js_1 = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
10
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
9
11
|
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
12
|
+
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
10
13
|
const tokenAuth_1 = require("./tokenAuth");
|
|
11
14
|
const assert_1 = require("assert");
|
|
12
|
-
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
13
15
|
const logger = (0, sdk_1.getLogger)();
|
|
16
|
+
const mcpServerToolCallMetaKeys = [
|
|
17
|
+
"xalia/mcpServerName",
|
|
18
|
+
];
|
|
19
|
+
function isMcpServerToolCallMetaData(meta) {
|
|
20
|
+
return (!!meta &&
|
|
21
|
+
typeof meta === "object" &&
|
|
22
|
+
mcpServerToolCallMetaKeys.every((k) => k in meta));
|
|
23
|
+
}
|
|
14
24
|
/**
|
|
15
25
|
* The (read-only) McpServerInfo to expose to external classes. Callers
|
|
16
26
|
* should not modify this data directly. Only through the McpServerManager
|
|
17
27
|
* class.
|
|
18
28
|
*/
|
|
19
29
|
class McpServerInfo {
|
|
20
|
-
constructor(tools) {
|
|
30
|
+
constructor(name, tools, resources) {
|
|
21
31
|
const toolsMap = {};
|
|
22
32
|
for (const mcpTool of tools) {
|
|
23
33
|
const toolName = mcpTool.name;
|
|
24
34
|
toolsMap[toolName] = mcpTool;
|
|
25
35
|
}
|
|
36
|
+
this.name = name;
|
|
26
37
|
this.tools = tools;
|
|
38
|
+
this.resources = resources;
|
|
27
39
|
this.toolsMap = toolsMap;
|
|
28
40
|
this.enabledToolsMap = new Map();
|
|
29
41
|
}
|
|
42
|
+
getName() {
|
|
43
|
+
return this.name;
|
|
44
|
+
}
|
|
30
45
|
getEnabledTools() {
|
|
31
46
|
return this.enabledToolsMap;
|
|
32
47
|
}
|
|
33
48
|
getTools() {
|
|
34
49
|
return this.tools;
|
|
35
50
|
}
|
|
51
|
+
getResources() {
|
|
52
|
+
return this.resources;
|
|
53
|
+
}
|
|
36
54
|
getTool(toolName) {
|
|
37
55
|
return this.toolsMap[toolName];
|
|
38
56
|
}
|
|
@@ -57,8 +75,9 @@ exports.McpServerInfoRW = McpServerInfoRW;
|
|
|
57
75
|
* McpServerManager.
|
|
58
76
|
*/
|
|
59
77
|
class McpServerInfoInternal extends McpServerInfoRW {
|
|
60
|
-
constructor(client, tools) {
|
|
61
|
-
super(tools);
|
|
78
|
+
constructor(name, client, tools, resources) {
|
|
79
|
+
super(name, tools, resources);
|
|
80
|
+
logger.debug(`[McpServerInfoInternal] tools: ${JSON.stringify(tools)}`);
|
|
62
81
|
const callbacks = new Map();
|
|
63
82
|
for (const mcpTool of tools) {
|
|
64
83
|
const toolName = mcpTool.name;
|
|
@@ -80,7 +99,17 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
80
99
|
(0, assert_1.strict)(typeof content0 === "object");
|
|
81
100
|
const content0Text = content0.text;
|
|
82
101
|
(0, assert_1.strict)(typeof content0Text === "string");
|
|
83
|
-
|
|
102
|
+
const meta = (toolResult._meta ||
|
|
103
|
+
{});
|
|
104
|
+
meta["xalia/mcpServerName"] = this.getName();
|
|
105
|
+
(0, assert_1.strict)(isMcpServerToolCallMetaData(meta));
|
|
106
|
+
return {
|
|
107
|
+
response: content0Text,
|
|
108
|
+
_meta: meta,
|
|
109
|
+
...(toolResult.structuredContent
|
|
110
|
+
? { structuredContent: toolResult.structuredContent }
|
|
111
|
+
: {}),
|
|
112
|
+
};
|
|
84
113
|
};
|
|
85
114
|
callbacks.set(toolName, callback);
|
|
86
115
|
}
|
|
@@ -93,6 +122,13 @@ class McpServerInfoInternal extends McpServerInfoRW {
|
|
|
93
122
|
getCallback(toolName) {
|
|
94
123
|
return this.callbacks.get(toolName);
|
|
95
124
|
}
|
|
125
|
+
async readResource(uri) {
|
|
126
|
+
const res = await this.client.readResource({
|
|
127
|
+
uri,
|
|
128
|
+
});
|
|
129
|
+
logger.info(`readResource: got: ${JSON.stringify(res)}`);
|
|
130
|
+
return res.contents;
|
|
131
|
+
}
|
|
96
132
|
}
|
|
97
133
|
/**
|
|
98
134
|
* Manage a set of MCP servers, where the tools for each server have an
|
|
@@ -121,14 +157,7 @@ class McpServerManager {
|
|
|
121
157
|
getMcpServer(mcpServerName) {
|
|
122
158
|
return this.getMcpServerInternal(mcpServerName);
|
|
123
159
|
}
|
|
124
|
-
async
|
|
125
|
-
logger.debug(`Adding mcp server ${mcpServerName}: ${url}`);
|
|
126
|
-
const sseTransportOptions = {};
|
|
127
|
-
if (apiKey) {
|
|
128
|
-
sseTransportOptions.authProvider = new tokenAuth_1.TokenAuth(apiKey);
|
|
129
|
-
}
|
|
130
|
-
const urlO = new URL(url);
|
|
131
|
-
const transport = new sse_js_1.SSEClientTransport(urlO, sseTransportOptions);
|
|
160
|
+
async addMcpServerWithTransport(mcpServerName, transport, tools) {
|
|
132
161
|
const client = new index_js_1.Client({
|
|
133
162
|
name: "@xalia/agent",
|
|
134
163
|
version: "1.0.0",
|
|
@@ -144,17 +173,42 @@ class McpServerManager {
|
|
|
144
173
|
}
|
|
145
174
|
await this.addMcpServerWithClient(client, mcpServerName, tools);
|
|
146
175
|
}
|
|
176
|
+
async addMcpServerWithSSEUrl(mcpServerName, url, apiKey, tools) {
|
|
177
|
+
logger.debug(`Adding mcp server ${mcpServerName}: ${url}`);
|
|
178
|
+
const sseTransportOptions = {};
|
|
179
|
+
if (apiKey) {
|
|
180
|
+
sseTransportOptions.authProvider = new tokenAuth_1.TokenAuth(apiKey);
|
|
181
|
+
}
|
|
182
|
+
const urlO = new URL(url);
|
|
183
|
+
const transport = new sse_js_1.SSEClientTransport(urlO, sseTransportOptions);
|
|
184
|
+
return this.addMcpServerWithTransport(mcpServerName, transport, tools);
|
|
185
|
+
}
|
|
186
|
+
async addMcpServerWithStreamableHTTPUrl(mcpServerName, url, apiKey) {
|
|
187
|
+
logger.debug(`Adding mcp (s-http) server ${mcpServerName}: ${url}`);
|
|
188
|
+
const transportOptions = {};
|
|
189
|
+
if (apiKey) {
|
|
190
|
+
transportOptions.authProvider = new tokenAuth_1.TokenAuth(apiKey);
|
|
191
|
+
}
|
|
192
|
+
const urlO = new URL(url);
|
|
193
|
+
const transport = new streamableHttp_js_1.StreamableHTTPClientTransport(urlO, transportOptions);
|
|
194
|
+
return this.addMcpServerWithTransport(mcpServerName, transport);
|
|
195
|
+
}
|
|
147
196
|
/**
|
|
148
197
|
* Add MCP server from an already connected McpClient.
|
|
149
198
|
*/
|
|
150
199
|
async addMcpServerWithClient(client, mcpServerName, tools) {
|
|
151
200
|
try {
|
|
152
|
-
// TODO
|
|
201
|
+
// TODO: require the tools to be passed in.
|
|
202
|
+
const resourcesP = client.listResources().catch((err) => {
|
|
203
|
+
logger.warn(`resources ${mcpServerName}: ${JSON.stringify(err)} ${String(err)}`);
|
|
204
|
+
return { resources: [] };
|
|
205
|
+
});
|
|
153
206
|
if (!tools) {
|
|
154
207
|
const mcpTools = await client.listTools();
|
|
155
208
|
tools = mcpTools.tools;
|
|
156
209
|
}
|
|
157
|
-
|
|
210
|
+
const resources = (await resourcesP).resources;
|
|
211
|
+
this.mcpServers.set(mcpServerName, new McpServerInfoInternal(mcpServerName, client, tools, resources));
|
|
158
212
|
}
|
|
159
213
|
catch (e) {
|
|
160
214
|
await client.close();
|
|
@@ -231,6 +285,10 @@ class McpServerManager {
|
|
|
231
285
|
}
|
|
232
286
|
return cb(JSON.stringify(toolCall.args));
|
|
233
287
|
}
|
|
288
|
+
async getResource(serverName, uri) {
|
|
289
|
+
const server = this.getMcpServerInternal(serverName);
|
|
290
|
+
return server.readResource(uri);
|
|
291
|
+
}
|
|
234
292
|
/**
|
|
235
293
|
* "Settings" refers to the set of added servers and enabled tools.
|
|
236
294
|
*/
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.choiceDeltaExtractReasoning = choiceDeltaExtractReasoning;
|
|
4
|
+
// /**
|
|
5
|
+
// * A chat completion message with extra reasoning tokens.
|
|
6
|
+
// */
|
|
7
|
+
// export interface ChatCompletionMessageWithReasoning
|
|
8
|
+
// extends OpenAI.Chat.Completions.ChatCompletionMessage {
|
|
9
|
+
// reasoning?: string;
|
|
10
|
+
// }
|
|
11
|
+
// Util function to extract reasoning tokens
|
|
12
|
+
function choiceDeltaExtractReasoning(delta) {
|
|
13
|
+
if (delta.reasoning) {
|
|
14
|
+
return delta.reasoning;
|
|
15
|
+
}
|
|
16
|
+
if (delta.reasoning_details) {
|
|
17
|
+
let reasoning = "";
|
|
18
|
+
for (const details of delta.reasoning_details) {
|
|
19
|
+
if (details.type !== "reasoning.text") {
|
|
20
|
+
throw new Error(`unexpected details.type: ${details.type}`);
|
|
21
|
+
}
|
|
22
|
+
if (details.text) {
|
|
23
|
+
if (typeof details.text !== "string") {
|
|
24
|
+
throw new Error(`unexpected typeof details.text: ${typeof details.text}`);
|
|
25
|
+
}
|
|
26
|
+
reasoning += details.text;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return reasoning;
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OpenAILLM = void 0;
|
|
4
|
+
const assert_1 = require("assert");
|
|
4
5
|
const llm_1 = require("./llm");
|
|
5
6
|
const openai_1 = require("openai");
|
|
7
|
+
function toolCallFromOpenAi(toolCall) {
|
|
8
|
+
(0, assert_1.strict)(toolCall.type === "function");
|
|
9
|
+
return toolCall;
|
|
10
|
+
}
|
|
11
|
+
function messageFromOpenAi(message) {
|
|
12
|
+
return {
|
|
13
|
+
...message,
|
|
14
|
+
tool_calls: message.tool_calls?.map(toolCallFromOpenAi),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function choicesFromOpenAI(choice) {
|
|
18
|
+
return {
|
|
19
|
+
...choice,
|
|
20
|
+
message: messageFromOpenAi(choice.message),
|
|
21
|
+
// tool_calls: choice.tool_calls?.map(toolCallFromOpenAi),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function completionFromOpenAI(completion) {
|
|
25
|
+
return {
|
|
26
|
+
...completion,
|
|
27
|
+
choices: completion.choices.map(choicesFromOpenAI),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
6
30
|
class OpenAILLM {
|
|
7
31
|
constructor(apiKey, apiUrl, model) {
|
|
8
32
|
this.openai = new openai_1.OpenAI({
|
|
@@ -34,7 +58,7 @@ class OpenAILLM {
|
|
|
34
58
|
await onMessage(message.content, true);
|
|
35
59
|
}
|
|
36
60
|
}
|
|
37
|
-
return completion;
|
|
61
|
+
return completionFromOpenAI(completion);
|
|
38
62
|
}
|
|
39
63
|
}
|
|
40
64
|
exports.OpenAILLM = OpenAILLM;
|
|
@@ -7,6 +7,7 @@ const openai_1 = require("openai");
|
|
|
7
7
|
const assert_1 = require("assert");
|
|
8
8
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
9
9
|
const llm_1 = require("./llm");
|
|
10
|
+
const openAI_1 = require("./openAI");
|
|
10
11
|
const logger = (0, sdk_1.getLogger)();
|
|
11
12
|
function initialToolCallFunction(deltaFn) {
|
|
12
13
|
// export interface ChatCompletionChunk.Choice.Delta.ToolCall.Function {
|
|
@@ -25,7 +26,8 @@ function initialToolCallFunction(deltaFn) {
|
|
|
25
26
|
name: deltaFn?.name || "",
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
|
-
function updateToolCallFunction(existingFn,
|
|
29
|
+
function updateToolCallFunction(existingFn, // eslint-disable-line
|
|
30
|
+
deltaFn // eslint-disable-line
|
|
29
31
|
) {
|
|
30
32
|
// export interface ChatCompletionChunk.Choice.Delta.ToolCall.Function {
|
|
31
33
|
// arguments?: string;
|
|
@@ -221,7 +223,7 @@ function updateCompletionMessage(message, delta) {
|
|
|
221
223
|
message.tool_calls = updateToolCalls(message.tool_calls, t);
|
|
222
224
|
}
|
|
223
225
|
}
|
|
224
|
-
const reasoning = (0,
|
|
226
|
+
const reasoning = (0, openAI_1.choiceDeltaExtractReasoning)(delta);
|
|
225
227
|
if (reasoning) {
|
|
226
228
|
updateReasoning(message, reasoning);
|
|
227
229
|
}
|
|
@@ -355,6 +357,7 @@ function initializeCompletion(chunk) {
|
|
|
355
357
|
model: chunk.model,
|
|
356
358
|
object: "chat.completion",
|
|
357
359
|
service_tier: chunk.service_tier,
|
|
360
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
358
361
|
system_fingerprint: chunk.system_fingerprint,
|
|
359
362
|
usage: chunk.usage ?? undefined,
|
|
360
363
|
},
|
|
@@ -388,7 +391,9 @@ function updateCompletion(completion, chunk) {
|
|
|
388
391
|
(0, assert_1.strict)(completion.id === chunk.id);
|
|
389
392
|
(0, assert_1.strict)(completion.model === chunk.model);
|
|
390
393
|
completion.service_tier = completion.service_tier || chunk.service_tier;
|
|
394
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
391
395
|
completion.system_fingerprint =
|
|
396
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
392
397
|
completion.system_fingerprint || chunk.system_fingerprint;
|
|
393
398
|
completion.usage = completion.usage || chunk.usage || undefined;
|
|
394
399
|
return updateCompletionChoices(completion.choices, chunk.choices);
|
|
@@ -460,7 +465,7 @@ class OpenAILLMStreaming {
|
|
|
460
465
|
if (onReasoning) {
|
|
461
466
|
const delta = chunk.choices[0]
|
|
462
467
|
?.delta;
|
|
463
|
-
const reasoning = (0,
|
|
468
|
+
const reasoning = (0, openAI_1.choiceDeltaExtractReasoning)(delta);
|
|
464
469
|
if (reasoning) {
|
|
465
470
|
await onReasoning(reasoning);
|
|
466
471
|
}
|
|
@@ -4,6 +4,8 @@ exports.SkillManager = exports.LOCAL_SERVER_URL = void 0;
|
|
|
4
4
|
const mcpServerManager_1 = require("./mcpServerManager");
|
|
5
5
|
const sdk_1 = require("@xalia/xmcp/sdk");
|
|
6
6
|
const index_js_1 = require("@modelcontextprotocol/sdk/client/index.js");
|
|
7
|
+
const DEVELOPMENT = process.env.DEVELOPMENT === "1";
|
|
8
|
+
const DEV_MCP_SERVER_URL = process.env.DEV_MCP_SERVER_URL || "http://localhost:8000/mcp";
|
|
7
9
|
const logger = (0, sdk_1.getLogger)();
|
|
8
10
|
exports.LOCAL_SERVER_URL = "http://localhost:5001";
|
|
9
11
|
/**
|
|
@@ -47,6 +49,10 @@ class SkillManager extends mcpServerManager_1.McpServerManager {
|
|
|
47
49
|
const apiClient = new sdk_1.ApiClient(sudoMcpUrl ?? sdk_1.DEFAULT_SERVER_URL, sudoMcpApiKey ?? "dummy_key");
|
|
48
50
|
// Fetch server list
|
|
49
51
|
const servers = await apiClient.listServers();
|
|
52
|
+
if (DEVELOPMENT) {
|
|
53
|
+
servers.push(new sdk_1.McpServerBrief("local_dev", "Local Dev Server", "A local mcp server using streamable-HTTP for development use. " +
|
|
54
|
+
`URL: ${DEV_MCP_SERVER_URL}`, false, {}, "", null, null, DEV_MCP_SERVER_URL));
|
|
55
|
+
}
|
|
50
56
|
const [mcpServers, mcpServersMap] = buildServersList(servers);
|
|
51
57
|
return new SkillManager(apiClient, mcpServers, mcpServersMap, new Map(), openUrl, authorized_url);
|
|
52
58
|
}
|
|
@@ -126,15 +132,22 @@ class SkillManager extends mcpServerManager_1.McpServerManager {
|
|
|
126
132
|
* transport. Validates the server's config schema, if applicable.
|
|
127
133
|
*/
|
|
128
134
|
async addMcpServer(serverName, enableAll) {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
const brief = this.serverBriefsMap[serverName];
|
|
136
|
+
if (brief.url_override) {
|
|
137
|
+
// Assume no api key for now
|
|
138
|
+
await this.addMcpServerWithStreamableHTTPUrl(serverName, brief.url_override);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const tools = await this.getServerTools(serverName);
|
|
142
|
+
const originalName = this.serverBriefsMap[serverName].originalName;
|
|
143
|
+
const mcpserver = await this.apiClient.getDetails(originalName, "run");
|
|
144
|
+
const client = new index_js_1.Client({
|
|
145
|
+
name: "@xalia/agent",
|
|
146
|
+
version: "1.0.0",
|
|
147
|
+
});
|
|
148
|
+
await connectServer(client, this.apiClient, mcpserver, this.openUrl, this.authorized_url);
|
|
149
|
+
await this.addMcpServerWithClient(client, serverName, tools);
|
|
150
|
+
}
|
|
138
151
|
if (enableAll) {
|
|
139
152
|
this.enableAllTools(serverName);
|
|
140
153
|
}
|
|
@@ -366,7 +366,7 @@ class ChatClient {
|
|
|
366
366
|
* @param templateName - the template name
|
|
367
367
|
* @param teamUuid - the team uuid
|
|
368
368
|
*/
|
|
369
|
-
createNewAgent(agentName, templateName, teamUuid) {
|
|
369
|
+
createNewAgent(agentName, templateName, teamUuid, model) {
|
|
370
370
|
if (this.closed) {
|
|
371
371
|
throw new Error("ChatClient is closed");
|
|
372
372
|
}
|
|
@@ -376,6 +376,7 @@ class ChatClient {
|
|
|
376
376
|
title: agentName,
|
|
377
377
|
template_name: templateName,
|
|
378
378
|
team_uuid: teamUuid,
|
|
379
|
+
model,
|
|
379
380
|
});
|
|
380
381
|
}
|
|
381
382
|
catch (error) {
|
|
@@ -91,10 +91,10 @@ class RemoteSudoMcpServerManager {
|
|
|
91
91
|
tool: toolName,
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
onMcpServerAdded(mcpServerName, tools, enabled_tools) {
|
|
94
|
+
onMcpServerAdded(mcpServerName, tools, enabled_tools, resources) {
|
|
95
95
|
logger.debug(`[onMcpServerAdded]: ${mcpServerName}, tools: ${JSON.stringify(tools)}` +
|
|
96
96
|
`, enabled: ${JSON.stringify(enabled_tools)}`);
|
|
97
|
-
const mcpServerInfo = new mcpServerManager_1.McpServerInfoRW(tools);
|
|
97
|
+
const mcpServerInfo = new mcpServerManager_1.McpServerInfoRW(mcpServerName, tools, resources);
|
|
98
98
|
for (const tool of enabled_tools) {
|
|
99
99
|
mcpServerInfo.enableTool(tool);
|
|
100
100
|
}
|
|
@@ -205,6 +205,29 @@ class SessionClient {
|
|
|
205
205
|
paused,
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
|
+
addMcpServerFromUrl(server_name, url) {
|
|
209
|
+
this.sendSessionMessage({
|
|
210
|
+
type: "add_mcp_server_from_url",
|
|
211
|
+
server_name,
|
|
212
|
+
url,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async getMcpResource(server_name, uri) {
|
|
216
|
+
const client_message_id = (0, uuid_1.v4)();
|
|
217
|
+
const resourceP = this.responseHandler.waitForResponse(client_message_id);
|
|
218
|
+
this.sender.send({
|
|
219
|
+
type: "get_mcp_resource",
|
|
220
|
+
session_id: this.sessionUUID,
|
|
221
|
+
client_message_id,
|
|
222
|
+
server_name,
|
|
223
|
+
uri,
|
|
224
|
+
});
|
|
225
|
+
const resourceMsg = await resourceP;
|
|
226
|
+
if (resourceMsg.type !== "mcp_resource") {
|
|
227
|
+
throw new Error(`unexpected response to resource req: ${JSON.stringify(resourceMsg)}`);
|
|
228
|
+
}
|
|
229
|
+
return resourceMsg.contents;
|
|
230
|
+
}
|
|
208
231
|
userMessage(msg, imageB64) {
|
|
209
232
|
(0, assert_1.strict)(msg || imageB64, "Either message or image must be provided");
|
|
210
233
|
this.sendSessionMessage({ type: "msg", message: msg, imageB64 });
|
|
@@ -313,7 +336,7 @@ class SessionClient {
|
|
|
313
336
|
// State updates
|
|
314
337
|
//
|
|
315
338
|
case "mcp_server_added":
|
|
316
|
-
this.smsm.onMcpServerAdded(message.server_name, message.tools, message.enabled_tools);
|
|
339
|
+
this.smsm.onMcpServerAdded(message.server_name, message.tools, message.enabled_tools, message.resources);
|
|
317
340
|
break;
|
|
318
341
|
case "mcp_server_removed":
|
|
319
342
|
this.smsm.onMcpServerRemoved(message.server_name);
|
|
@@ -344,6 +367,8 @@ class SessionClient {
|
|
|
344
367
|
case "user_removed":
|
|
345
368
|
this.participants.delete(message.user_uuid);
|
|
346
369
|
break;
|
|
370
|
+
// Responses to requests which can be awaited
|
|
371
|
+
case "mcp_resource":
|
|
347
372
|
case "session_shared":
|
|
348
373
|
this.responseHandler.onMessage(message);
|
|
349
374
|
break;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EXTENSION_TO_SESSION_FILE_MIME_TYPE = exports.SESSION_FILE_TEXT_MIME_TYPES = exports.SESSION_FILE_MIME_TYPES = void 0;
|
|
4
4
|
exports.isSessionFileMimeType = isSessionFileMimeType;
|
|
5
5
|
exports.isSessionFileTextMimeType = isSessionFileTextMimeType;
|
|
6
|
+
exports.isFileMetaData = isFileMetaData;
|
|
6
7
|
exports.getSessionFileMimeTypeFromDataUrl = getSessionFileMimeTypeFromDataUrl;
|
|
7
8
|
exports.createSessionFileDataUrl = createSessionFileDataUrl;
|
|
8
9
|
const mimeTypes_1 = require("./mimeTypes");
|
|
@@ -10,6 +11,7 @@ exports.SESSION_FILE_MIME_TYPES = [
|
|
|
10
11
|
...mimeTypes_1.IMAGE_MIME_TYPES,
|
|
11
12
|
"text/plain",
|
|
12
13
|
"text/markdown",
|
|
14
|
+
"text/html",
|
|
13
15
|
"application/pdf",
|
|
14
16
|
"image/svg+xml",
|
|
15
17
|
];
|
|
@@ -32,6 +34,14 @@ exports.EXTENSION_TO_SESSION_FILE_MIME_TYPE = {
|
|
|
32
34
|
txt: "text/plain",
|
|
33
35
|
md: "text/markdown",
|
|
34
36
|
};
|
|
37
|
+
// Checks that the array of keys matches FileMetaData
|
|
38
|
+
const fileMetaKeys = [
|
|
39
|
+
"xalia/fileUri",
|
|
40
|
+
"xalia/fileMimeType",
|
|
41
|
+
];
|
|
42
|
+
function isFileMetaData(meta) {
|
|
43
|
+
return (!!meta && typeof meta === "object" && fileMetaKeys.every((k) => k in meta));
|
|
44
|
+
}
|
|
35
45
|
function getSessionFileMimeTypeFromDataUrl(data_url) {
|
|
36
46
|
const mimeType = (0, mimeTypes_1.getMimeTypeFromDataUrl)(data_url);
|
|
37
47
|
if (!isSessionFileMimeType(mimeType)) {
|
|
@@ -55,9 +55,9 @@ class ChatContextManager {
|
|
|
55
55
|
// IContextManager.getLLMContext
|
|
56
56
|
getLLMContext() {
|
|
57
57
|
if (this.fileManagerDescriptionsDirty) {
|
|
58
|
-
const
|
|
59
|
-
logger.debug(`[ChatContextManager] filemanager
|
|
60
|
-
this.llmContext.setPromptFragment("file_manager",
|
|
58
|
+
const prompt = (0, sessionFileManager_1.createSessionFilesManagerPrompt)(this.fileManager);
|
|
59
|
+
logger.debug(`[ChatContextManager] filemanager prompt:\n${prompt}`);
|
|
60
|
+
this.llmContext.setPromptFragment("file_manager", prompt);
|
|
61
61
|
this.fileManagerDescriptionsDirty = false;
|
|
62
62
|
}
|
|
63
63
|
return this.llmContext.getLLMContext();
|
|
@@ -150,7 +150,7 @@ class ChatContextManager {
|
|
|
150
150
|
// Compute the new llm messages
|
|
151
151
|
const llmUserMessages = [];
|
|
152
152
|
for (const msg of pendingUserMessages) {
|
|
153
|
-
const userMsg = (0, agent_1.createUserMessage)(msg.message, msg.imageB64, msg.
|
|
153
|
+
const userMsg = (0, agent_1.createUserMessage)(msg.message, msg.imageB64, msg.user_uuid);
|
|
154
154
|
if (userMsg) {
|
|
155
155
|
llmUserMessages.push(userMsg);
|
|
156
156
|
}
|
|
@@ -193,7 +193,7 @@ function chatToolResultMessageToSessionMessage(chatMessage) {
|
|
|
193
193
|
};
|
|
194
194
|
}
|
|
195
195
|
function chatUserMessageToSessionMessage(chatMessage) {
|
|
196
|
-
const userMsg = (0, agent_1.createUserMessage)(chatMessage.message, chatMessage.imageB64, chatMessage.
|
|
196
|
+
const userMsg = (0, agent_1.createUserMessage)(chatMessage.message, chatMessage.imageB64, chatMessage.user_uuid);
|
|
197
197
|
(0, assert_1.strict)(userMsg);
|
|
198
198
|
return {
|
|
199
199
|
message_idx: chatMessage.message_idx,
|