@xalia/agent 0.6.1 → 0.6.2
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 +103 -54
- package/dist/agent/src/agent/agentUtils.js +22 -21
- package/dist/agent/src/agent/compressingContextManager.js +3 -2
- package/dist/agent/src/agent/dummyLLM.js +1 -3
- package/dist/agent/src/agent/imageGenLLM.js +67 -0
- package/dist/agent/src/agent/imageGenerator.js +43 -0
- package/dist/agent/src/agent/llm.js +27 -0
- package/dist/agent/src/agent/mcpServerManager.js +18 -6
- package/dist/agent/src/agent/nullAgentEventHandler.js +6 -0
- package/dist/agent/src/agent/openAILLM.js +3 -3
- package/dist/agent/src/agent/openAILLMStreaming.js +41 -6
- package/dist/agent/src/chat/client/chatClient.js +84 -13
- package/dist/agent/src/chat/client/sessionClient.js +47 -6
- package/dist/agent/src/chat/client/sessionFiles.js +102 -0
- package/dist/agent/src/chat/data/apiKeyManager.js +38 -7
- package/dist/agent/src/chat/data/database.js +83 -70
- package/dist/agent/src/chat/data/dbSessionFileModels.js +49 -0
- package/dist/agent/src/chat/data/dbSessionFiles.js +76 -0
- package/dist/agent/src/chat/data/dbSessionMessages.js +57 -0
- package/dist/agent/src/chat/data/mimeTypes.js +44 -0
- package/dist/agent/src/chat/protocol/messages.js +21 -0
- package/dist/agent/src/chat/server/chatContextManager.js +14 -7
- package/dist/agent/src/chat/server/connectionManager.js +14 -36
- package/dist/agent/src/chat/server/connectionManager.test.js +2 -16
- package/dist/agent/src/chat/server/conversation.js +69 -45
- package/dist/agent/src/chat/server/imageGeneratorTools.js +111 -0
- package/dist/agent/src/chat/server/openSession.js +205 -43
- package/dist/agent/src/chat/server/server.js +5 -8
- package/dist/agent/src/chat/server/sessionFileManager.js +171 -38
- package/dist/agent/src/chat/server/sessionRegistry.js +199 -32
- package/dist/agent/src/chat/server/test-utils/mockFactories.js +12 -11
- package/dist/agent/src/chat/server/tools.js +27 -6
- package/dist/agent/src/chat/utils/multiAsyncQueue.js +9 -1
- package/dist/agent/src/test/agent.test.js +15 -11
- package/dist/agent/src/test/chatContextManager.test.js +4 -0
- package/dist/agent/src/test/clientServerConnection.test.js +2 -2
- package/dist/agent/src/test/db.test.js +33 -70
- package/dist/agent/src/test/dbSessionFiles.test.js +179 -0
- package/dist/agent/src/test/dbSessionMessages.test.js +67 -0
- package/dist/agent/src/test/dbTestTools.js +6 -5
- package/dist/agent/src/test/imageLoad.test.js +1 -1
- package/dist/agent/src/test/mcpServerManager.test.js +1 -1
- package/dist/agent/src/test/multiAsyncQueue.test.js +50 -0
- package/dist/agent/src/test/testTools.js +12 -0
- package/dist/agent/src/tool/agentChat.js +25 -6
- package/dist/agent/src/tool/agentMain.js +1 -1
- package/dist/agent/src/tool/chatMain.js +113 -4
- package/dist/agent/src/tool/commandPrompt.js +7 -3
- package/dist/agent/src/tool/files.js +23 -15
- package/dist/agent/src/tool/options.js +2 -2
- package/package.json +1 -1
- package/scripts/test_chat +124 -66
- package/src/agent/agent.ts +145 -38
- package/src/agent/agentUtils.ts +27 -21
- package/src/agent/compressingContextManager.ts +5 -4
- package/src/agent/context.ts +1 -1
- package/src/agent/dummyLLM.ts +1 -3
- package/src/agent/iAgentEventHandler.ts +15 -2
- package/src/agent/imageGenLLM.ts +99 -0
- package/src/agent/imageGenerator.ts +60 -0
- package/src/agent/llm.ts +128 -4
- package/src/agent/mcpServerManager.ts +26 -7
- package/src/agent/nullAgentEventHandler.ts +6 -0
- package/src/agent/openAILLM.ts +3 -8
- package/src/agent/openAILLMStreaming.ts +60 -14
- package/src/chat/client/chatClient.ts +119 -14
- package/src/chat/client/sessionClient.ts +75 -9
- package/src/chat/client/sessionFiles.ts +145 -0
- package/src/chat/data/apiKeyManager.ts +55 -7
- package/src/chat/data/dataModels.ts +16 -7
- package/src/chat/data/database.ts +107 -92
- package/src/chat/data/dbSessionFileModels.ts +91 -0
- package/src/chat/data/dbSessionFiles.ts +99 -0
- package/src/chat/data/dbSessionMessages.ts +68 -0
- package/src/chat/data/mimeTypes.ts +58 -0
- package/src/chat/protocol/messages.ts +127 -13
- package/src/chat/server/chatContextManager.ts +36 -13
- package/src/chat/server/connectionManager.test.ts +1 -22
- package/src/chat/server/connectionManager.ts +18 -53
- package/src/chat/server/conversation.ts +96 -57
- package/src/chat/server/imageGeneratorTools.ts +138 -0
- package/src/chat/server/openSession.ts +287 -49
- package/src/chat/server/server.ts +5 -11
- package/src/chat/server/sessionFileManager.ts +223 -63
- package/src/chat/server/sessionRegistry.ts +285 -41
- package/src/chat/server/test-utils/mockFactories.ts +13 -13
- package/src/chat/server/tools.ts +43 -8
- package/src/chat/utils/agentSessionMap.ts +2 -2
- package/src/chat/utils/multiAsyncQueue.ts +11 -1
- package/src/test/agent.test.ts +23 -14
- package/src/test/chatContextManager.test.ts +7 -2
- package/src/test/clientServerConnection.test.ts +3 -3
- package/src/test/compressingContextManager.test.ts +1 -1
- package/src/test/context.test.ts +2 -1
- package/src/test/conversation.test.ts +1 -1
- package/src/test/db.test.ts +41 -83
- package/src/test/dbSessionFiles.test.ts +258 -0
- package/src/test/dbSessionMessages.test.ts +85 -0
- package/src/test/dbTestTools.ts +9 -5
- package/src/test/imageLoad.test.ts +2 -2
- package/src/test/mcpServerManager.test.ts +3 -1
- package/src/test/multiAsyncQueue.test.ts +58 -0
- package/src/test/testTools.ts +15 -1
- package/src/tool/agentChat.ts +35 -7
- package/src/tool/agentMain.ts +7 -7
- package/src/tool/chatMain.ts +126 -5
- package/src/tool/commandPrompt.ts +10 -5
- package/src/tool/files.ts +30 -13
- package/src/tool/options.ts +1 -1
- package/test_data/dummyllm_script_image_gen.json +19 -0
- package/test_data/dummyllm_script_invoke_image_gen_tool.json +30 -0
- package/test_data/image_gen_test_profile.json +5 -0
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
ServerConnectionMessage,
|
|
11
11
|
} from "../protocol/connectionMessages";
|
|
12
12
|
import { ChatErrorMessage, ChatFatalError } from "../protocol/errors";
|
|
13
|
-
import { ApiKeyManager } from "../data/apiKeyManager";
|
|
14
13
|
|
|
15
14
|
const logger = getLogger();
|
|
16
15
|
|
|
@@ -32,18 +31,15 @@ export interface IUserConnectionManager<ServerMsgT> {
|
|
|
32
31
|
* Send message to a specific connection.
|
|
33
32
|
*/
|
|
34
33
|
sendToConnection(connectionId: string, message: ServerMsgT): void;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get cached api key for a user. This key will not be available
|
|
38
|
-
* after the user disconnects.
|
|
39
|
-
*/
|
|
40
|
-
getLiveUserApiKey(userId: string): string | undefined;
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
/**
|
|
44
|
-
* Interface for processing messages from the client.
|
|
37
|
+
* Interface for processing messages from the client. Also handles guest
|
|
38
|
+
* admission.
|
|
45
39
|
*/
|
|
46
40
|
export interface IMessageProcessor<ClientMsgT> {
|
|
41
|
+
authenticate(token: string): Promise<string | undefined>;
|
|
42
|
+
|
|
47
43
|
processMessage(
|
|
48
44
|
connectionId: string,
|
|
49
45
|
userId: string,
|
|
@@ -54,25 +50,18 @@ export interface IMessageProcessor<ClientMsgT> {
|
|
|
54
50
|
* Handle user disconnect - clean up from all sessions.
|
|
55
51
|
* Called when a connection is closed to ensure proper cleanup.
|
|
56
52
|
*/
|
|
57
|
-
handleUserDisconnect(userId: string): void
|
|
53
|
+
handleUserDisconnect(userId: string): Promise<void>;
|
|
58
54
|
}
|
|
59
55
|
|
|
60
56
|
class Connection {
|
|
61
57
|
public ws: ws.WebSocket;
|
|
62
58
|
public userId: string;
|
|
63
|
-
public apiKey: string;
|
|
64
59
|
public heartbeat: NodeJS.Timeout;
|
|
65
60
|
public lastMessageTime: number;
|
|
66
61
|
|
|
67
|
-
constructor(
|
|
68
|
-
ws: ws.WebSocket,
|
|
69
|
-
userId: string,
|
|
70
|
-
apiKey: string,
|
|
71
|
-
heartbeat: NodeJS.Timeout
|
|
72
|
-
) {
|
|
62
|
+
constructor(ws: ws.WebSocket, userId: string, heartbeat: NodeJS.Timeout) {
|
|
73
63
|
this.ws = ws;
|
|
74
64
|
this.userId = userId;
|
|
75
|
-
this.apiKey = apiKey;
|
|
76
65
|
this.heartbeat = heartbeat;
|
|
77
66
|
this.lastMessageTime = Date.now();
|
|
78
67
|
}
|
|
@@ -96,18 +85,15 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
96
85
|
|
|
97
86
|
// this is a singleton object and can only be initialized by .init method
|
|
98
87
|
private constructor(
|
|
99
|
-
private apiKeyManager: ApiKeyManager,
|
|
100
88
|
private sessionMessageProcessor: IMessageProcessor<ClientMsgT>
|
|
101
89
|
) {}
|
|
102
90
|
|
|
103
91
|
static init<ClientMsgT, ServerMsgT>(
|
|
104
|
-
apiKeyManager: ApiKeyManager,
|
|
105
92
|
createMsgProcessor: (
|
|
106
93
|
cm: IUserConnectionManager<ServerMsgT>
|
|
107
94
|
) => IMessageProcessor<ClientMsgT>
|
|
108
95
|
): ConnectionManager<ClientMsgT, ServerMsgT> {
|
|
109
96
|
const connManager = new ConnectionManager(
|
|
110
|
-
apiKeyManager,
|
|
111
97
|
{} as IMessageProcessor<ClientMsgT>
|
|
112
98
|
);
|
|
113
99
|
connManager.sessionMessageProcessor = createMsgProcessor(connManager);
|
|
@@ -120,17 +106,15 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
120
106
|
*/
|
|
121
107
|
async handleConnection(
|
|
122
108
|
webSocket: ws.WebSocket,
|
|
123
|
-
|
|
109
|
+
token: string,
|
|
124
110
|
req?: { headers: { [key: string]: string | string[] | undefined } }
|
|
125
111
|
): Promise<void> {
|
|
126
112
|
const connectionId = uuidv4();
|
|
127
113
|
logger.info(`[ConnectionManager] New connection: ${connectionId}`);
|
|
128
114
|
|
|
129
115
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!userData) {
|
|
133
|
-
logger.error(`[ConnectionManager] Invalid API key: ${apiKey}`);
|
|
116
|
+
const userUUID = await this.sessionMessageProcessor.authenticate(token);
|
|
117
|
+
if (!userUUID) {
|
|
134
118
|
throw new ChatFatalError("invalid api key");
|
|
135
119
|
}
|
|
136
120
|
|
|
@@ -142,14 +126,14 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
142
126
|
// Register connection
|
|
143
127
|
this.connections.set(
|
|
144
128
|
connectionId,
|
|
145
|
-
new Connection(webSocket,
|
|
129
|
+
new Connection(webSocket, userUUID, heartbeat)
|
|
146
130
|
);
|
|
147
131
|
|
|
148
132
|
// Add to user connections
|
|
149
|
-
if (!this.userToConnections.has(
|
|
150
|
-
this.userToConnections.set(
|
|
133
|
+
if (!this.userToConnections.has(userUUID)) {
|
|
134
|
+
this.userToConnections.set(userUUID, new Set());
|
|
151
135
|
}
|
|
152
|
-
const userConnections = this.userToConnections.get(
|
|
136
|
+
const userConnections = this.userToConnections.get(userUUID);
|
|
153
137
|
if (userConnections) {
|
|
154
138
|
userConnections.add(connectionId);
|
|
155
139
|
}
|
|
@@ -164,7 +148,7 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
164
148
|
const response: ServerConnectionReady = {
|
|
165
149
|
t: "ready",
|
|
166
150
|
c_id: connectionId,
|
|
167
|
-
user_uuid:
|
|
151
|
+
user_uuid: userUUID,
|
|
168
152
|
};
|
|
169
153
|
|
|
170
154
|
// Check if connection still exists before sending
|
|
@@ -179,7 +163,7 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
179
163
|
|
|
180
164
|
logger.info(
|
|
181
165
|
`[ConnectionManager] Connection ${connectionId} registered ` +
|
|
182
|
-
`for user ${
|
|
166
|
+
`for user ${userUUID}, version: ${clientVersion}`
|
|
183
167
|
);
|
|
184
168
|
} catch (error) {
|
|
185
169
|
const errorMessage =
|
|
@@ -383,7 +367,9 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
383
367
|
userConnections.delete(connectionId);
|
|
384
368
|
if (userConnections.size === 0) {
|
|
385
369
|
this.userToConnections.delete(conn.userId);
|
|
386
|
-
this.sessionMessageProcessor.handleUserDisconnect(
|
|
370
|
+
const _ = this.sessionMessageProcessor.handleUserDisconnect(
|
|
371
|
+
conn.userId
|
|
372
|
+
);
|
|
387
373
|
}
|
|
388
374
|
}
|
|
389
375
|
|
|
@@ -517,25 +503,4 @@ export class ConnectionManager<ClientMsgT, ServerMsgT>
|
|
|
517
503
|
const connections = this.userToConnections.get(userId);
|
|
518
504
|
return connections ? Array.from(connections) : [];
|
|
519
505
|
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Implementation of IUserConnectionManager interface.
|
|
523
|
-
* Get cached api key for a user. This key will not be available
|
|
524
|
-
* after the user disconnects.
|
|
525
|
-
*/
|
|
526
|
-
public getLiveUserApiKey(userId: string): string | undefined {
|
|
527
|
-
const connectionIds = this.getUserConnections(userId);
|
|
528
|
-
|
|
529
|
-
// Return API key from the first active connection
|
|
530
|
-
// (all connections for a user should have the same API key)
|
|
531
|
-
for (const connectionId of connectionIds) {
|
|
532
|
-
const conn = this.connections.get(connectionId);
|
|
533
|
-
if (conn) {
|
|
534
|
-
return conn.apiKey;
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// No active connections for this user
|
|
539
|
-
return undefined;
|
|
540
|
-
}
|
|
541
506
|
}
|
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
import { strict as assert } from "assert";
|
|
5
5
|
|
|
6
|
+
import { createUserMessage } from "../../agent/agent";
|
|
6
7
|
import {
|
|
8
|
+
ChatCompletionAssistantMessageParam,
|
|
7
9
|
ChatCompletionMessageParam,
|
|
10
|
+
ChatCompletionToolMessageParam,
|
|
8
11
|
ChatCompletionUserMessageParam,
|
|
9
|
-
|
|
10
|
-
} from "../../agent/agent";
|
|
12
|
+
} from "../../agent/llm";
|
|
11
13
|
import { SessionMessage, UserMessageData } from "../data/dataModels";
|
|
12
14
|
import {
|
|
13
15
|
ServerAgentMessage,
|
|
@@ -96,45 +98,109 @@ export function sessionMessagesToLLMConversation(
|
|
|
96
98
|
return { firstIndex, conversation };
|
|
97
99
|
}
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Convert the DB data (SessionMessage) for a ChatCompletionUserMessageParam
|
|
103
|
+
* into a ServerUserMessage message.
|
|
104
|
+
*/
|
|
105
|
+
export function userMessageToConversationMessage(
|
|
106
|
+
userMessage: ChatCompletionUserMessageParam,
|
|
107
|
+
message_idx: number,
|
|
108
|
+
defaultUserUuid: string,
|
|
109
|
+
session_id: string
|
|
110
|
+
): ServerUserMessage {
|
|
111
|
+
// The name on the message should be the uuid
|
|
112
|
+
|
|
113
|
+
const userMsgData = llmUserMessageToUserMessageData(userMessage);
|
|
114
|
+
const user_uuid = userMessage.name || defaultUserUuid;
|
|
115
|
+
const msg: ServerUserMessage = {
|
|
116
|
+
type: "user_msg",
|
|
117
|
+
message: userMsgData.message,
|
|
118
|
+
message_idx,
|
|
119
|
+
user_uuid,
|
|
120
|
+
session_id,
|
|
121
|
+
};
|
|
122
|
+
if (userMsgData.imageB64) {
|
|
123
|
+
msg.imageB64 = userMsgData.imageB64;
|
|
124
|
+
}
|
|
125
|
+
return msg;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Convert the DB data (SessionMessage) for a
|
|
130
|
+
* ChatCompletionAssistantMessageParam into a ServerAgentMessage message.
|
|
131
|
+
*/
|
|
132
|
+
export function assistantMessageToConversationMessage(
|
|
133
|
+
msg: ChatCompletionAssistantMessageParam,
|
|
134
|
+
message_idx: number,
|
|
135
|
+
session_id: string
|
|
136
|
+
): ServerAgentMessage {
|
|
137
|
+
assert(!msg.audio);
|
|
138
|
+
return {
|
|
139
|
+
type: "agent_msg",
|
|
140
|
+
message: msg,
|
|
141
|
+
message_idx,
|
|
142
|
+
session_id,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Convert the DB data (SessionMessage) for a ChatCompletionToolMessageParam
|
|
148
|
+
* into a ServerToolCallResult message.
|
|
149
|
+
*/
|
|
150
|
+
export function toolResultToConversationMessage(
|
|
151
|
+
toolCall: ChatCompletionToolMessageParam,
|
|
152
|
+
message_idx: number,
|
|
153
|
+
session_id: string
|
|
154
|
+
): ServerToolCallResult {
|
|
155
|
+
return {
|
|
156
|
+
type: "tool_call_result",
|
|
157
|
+
result: toolCall,
|
|
158
|
+
message_idx,
|
|
159
|
+
session_id,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Convert a set of SessionMessages (stored in the DB) into Conversation
|
|
165
|
+
* messages (protocol which can be sent over the wire).
|
|
166
|
+
*/
|
|
167
|
+
export function sessionMessagesToConversationMessages(
|
|
100
168
|
sessionMessages: SessionMessage[],
|
|
101
169
|
defaultUserUuid: string,
|
|
102
170
|
session_id: string
|
|
103
|
-
):
|
|
104
|
-
const msgs:
|
|
171
|
+
): ConversationMessage[] {
|
|
172
|
+
const msgs: ConversationMessage[] = [];
|
|
105
173
|
for (const sm of sessionMessages) {
|
|
106
174
|
const ccmp = sm.content;
|
|
107
175
|
const message_idx = sm.message_idx;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
176
|
+
const msgRole = ccmp.role;
|
|
177
|
+
switch (msgRole) {
|
|
178
|
+
case "system":
|
|
179
|
+
throw new Error("cannot convert system to ConversationMessage");
|
|
111
180
|
case "assistant":
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
type: "agent_msg",
|
|
116
|
-
message: ccmp,
|
|
117
|
-
message_idx,
|
|
118
|
-
session_id,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
// TODO: do we want to convert tool calls etc?
|
|
181
|
+
msgs.push(
|
|
182
|
+
assistantMessageToConversationMessage(ccmp, message_idx, session_id)
|
|
183
|
+
);
|
|
122
184
|
break;
|
|
123
185
|
case "user":
|
|
124
|
-
|
|
125
|
-
|
|
186
|
+
msgs.push(
|
|
187
|
+
userMessageToConversationMessage(
|
|
126
188
|
ccmp,
|
|
127
189
|
message_idx,
|
|
128
190
|
defaultUserUuid,
|
|
129
191
|
session_id
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
msgs.push(msg);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
192
|
+
)
|
|
193
|
+
);
|
|
135
194
|
break;
|
|
136
|
-
|
|
195
|
+
case "tool":
|
|
196
|
+
msgs.push(
|
|
197
|
+
toolResultToConversationMessage(ccmp, message_idx, session_id)
|
|
198
|
+
);
|
|
137
199
|
break;
|
|
200
|
+
default: {
|
|
201
|
+
const _: never = ccmp;
|
|
202
|
+
throw new Error(`unexpected message role: ${msgRole as string}`);
|
|
203
|
+
}
|
|
138
204
|
}
|
|
139
205
|
}
|
|
140
206
|
|
|
@@ -143,7 +209,7 @@ export function sessionMessagesToChatMessages(
|
|
|
143
209
|
|
|
144
210
|
export function llmUserMessageToUserMessageData(
|
|
145
211
|
userMessage: ChatCompletionUserMessageParam
|
|
146
|
-
): UserMessageData
|
|
212
|
+
): UserMessageData {
|
|
147
213
|
if (typeof userMessage.content === "string") {
|
|
148
214
|
return {
|
|
149
215
|
message: userMessage.content,
|
|
@@ -163,15 +229,15 @@ export function llmUserMessageToUserMessageData(
|
|
|
163
229
|
break;
|
|
164
230
|
case "input_audio":
|
|
165
231
|
throw new ChatErrorMessage(
|
|
166
|
-
"
|
|
232
|
+
"llmUserMessageToUserMessageData: audio content not supported"
|
|
167
233
|
);
|
|
168
234
|
case "file":
|
|
169
235
|
throw new ChatErrorMessage(
|
|
170
|
-
"
|
|
236
|
+
"llmUserMessageToUserMessageData: file content not supported"
|
|
171
237
|
);
|
|
172
238
|
default:
|
|
173
239
|
throw new ChatErrorMessage(
|
|
174
|
-
"
|
|
240
|
+
"llmUserMessageToUserMessageData: unexpected content.type"
|
|
175
241
|
);
|
|
176
242
|
}
|
|
177
243
|
}
|
|
@@ -185,33 +251,6 @@ export function llmUserMessageToUserMessageData(
|
|
|
185
251
|
return finalMsg;
|
|
186
252
|
}
|
|
187
253
|
|
|
188
|
-
export function userMessageToChatMessage(
|
|
189
|
-
userMessage: ChatCompletionUserMessageParam,
|
|
190
|
-
message_idx: number,
|
|
191
|
-
defaultUserUuid: string,
|
|
192
|
-
session_id: string
|
|
193
|
-
): ServerUserMessage | undefined {
|
|
194
|
-
// The name on the message should be the uuid
|
|
195
|
-
|
|
196
|
-
const userMsgData = llmUserMessageToUserMessageData(userMessage);
|
|
197
|
-
if (!userMsgData) {
|
|
198
|
-
return undefined;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const user_uuid = userMessage.name || defaultUserUuid;
|
|
202
|
-
const msg: ServerUserMessage = {
|
|
203
|
-
type: "user_msg",
|
|
204
|
-
message: userMsgData.message,
|
|
205
|
-
message_idx,
|
|
206
|
-
user_uuid,
|
|
207
|
-
session_id,
|
|
208
|
-
};
|
|
209
|
-
if (userMsgData.imageB64) {
|
|
210
|
-
msg.imageB64 = userMsgData.imageB64;
|
|
211
|
-
}
|
|
212
|
-
return msg;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
254
|
export function chatToolResultMessageToSessionMessage(
|
|
216
255
|
chatMessage: ServerToolCallResult
|
|
217
256
|
): SessionMessage {
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { getLogger } from "@xalia/xmcp/sdk";
|
|
2
|
+
|
|
3
|
+
import { Agent, IAgentToolProvider, ToolCallResult } from "../../agent/agent";
|
|
4
|
+
import { ChatCompletionTool } from "../../agent/llm";
|
|
5
|
+
import { ImageGenerator } from "../../agent/imageGenerator";
|
|
6
|
+
|
|
7
|
+
import { ChatSessionFileManager } from "./sessionFileManager";
|
|
8
|
+
import { makeParseArgsFn } from "./tools";
|
|
9
|
+
import { getMimeTypeFromDataUrl } from "../data/mimeTypes";
|
|
10
|
+
|
|
11
|
+
const logger = getLogger();
|
|
12
|
+
|
|
13
|
+
function createImageGenerator(
|
|
14
|
+
llmUrl: string,
|
|
15
|
+
llmApiKey: string
|
|
16
|
+
): Promise<ImageGenerator> {
|
|
17
|
+
const imageGenModel = process.env["GEN_IMAGE_MODEL"];
|
|
18
|
+
logger.debug(`[genImageFileTool] model: ${imageGenModel || "(default)"}`);
|
|
19
|
+
return ImageGenerator.init(llmUrl, llmApiKey, imageGenModel);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// gen_image
|
|
23
|
+
//
|
|
24
|
+
// Simple tool to generate image output as tool output. The user can then
|
|
25
|
+
// decide how/whether to use the image.
|
|
26
|
+
|
|
27
|
+
export async function genImageTool(
|
|
28
|
+
llmUrl: string,
|
|
29
|
+
llmApiKey: string
|
|
30
|
+
): Promise<IAgentToolProvider> {
|
|
31
|
+
const GEN_IMAGE_DESC: ChatCompletionTool = {
|
|
32
|
+
type: "function",
|
|
33
|
+
function: {
|
|
34
|
+
name: "gen_image",
|
|
35
|
+
description: "Generate image",
|
|
36
|
+
parameters: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
prompt: {
|
|
40
|
+
type: "string",
|
|
41
|
+
description: "Prompt/instructions for image generator",
|
|
42
|
+
},
|
|
43
|
+
input_img: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "(Optional) input image data",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ["prompt"],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
} as const;
|
|
52
|
+
const imageGenerator = await createImageGenerator(llmUrl, llmApiKey);
|
|
53
|
+
const getPromptInputImg = makeParseArgsFn(
|
|
54
|
+
["prompt"] as const,
|
|
55
|
+
["input_img"] as const
|
|
56
|
+
);
|
|
57
|
+
const toolFn = async (_: Agent, args: unknown): Promise<ToolCallResult> => {
|
|
58
|
+
const { prompt, input_img } = getPromptInputImg(args);
|
|
59
|
+
const image = await imageGenerator.generate(prompt, input_img);
|
|
60
|
+
return {
|
|
61
|
+
response: image,
|
|
62
|
+
overwriteArgs: JSON.stringify({ prompt }),
|
|
63
|
+
// overwriteResponse: "Image generated",
|
|
64
|
+
// // For now, let the UI see the image.
|
|
65
|
+
// passOriginalResponseToEventHandler: true,
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
71
|
+
setup: async (agent: Agent) => {
|
|
72
|
+
agent.addAgentTool(GEN_IMAGE_DESC, toolFn);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// gen_image_file
|
|
78
|
+
|
|
79
|
+
export async function genImageFileTool(
|
|
80
|
+
llmUrl: string,
|
|
81
|
+
llmApiKey: string,
|
|
82
|
+
fileManager: ChatSessionFileManager
|
|
83
|
+
): Promise<IAgentToolProvider> {
|
|
84
|
+
const GEN_IMAGE_FILE_DESC: ChatCompletionTool = {
|
|
85
|
+
type: "function",
|
|
86
|
+
function: {
|
|
87
|
+
name: "gen_image_file",
|
|
88
|
+
description: "Generate image (into session file manager)",
|
|
89
|
+
parameters: {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
prompt: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: "Prompt/instructions for image generator",
|
|
95
|
+
},
|
|
96
|
+
name: {
|
|
97
|
+
type: "string",
|
|
98
|
+
description: "Name of file to (over)write",
|
|
99
|
+
},
|
|
100
|
+
summary: {
|
|
101
|
+
type: "string",
|
|
102
|
+
description: "Summary (for file manager)",
|
|
103
|
+
},
|
|
104
|
+
input_name: {
|
|
105
|
+
type: "string",
|
|
106
|
+
description: "(Optional) input image filename",
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
required: ["prompt", "name", "summary"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
} as const;
|
|
113
|
+
|
|
114
|
+
const imageGenerator = await createImageGenerator(llmUrl, llmApiKey);
|
|
115
|
+
const getPromptNameSummary = makeParseArgsFn(
|
|
116
|
+
["prompt", "name", "summary"] as const,
|
|
117
|
+
["input_name"] as const
|
|
118
|
+
);
|
|
119
|
+
const toolFn = async (_: Agent, args: unknown): Promise<ToolCallResult> => {
|
|
120
|
+
const { prompt, name, summary, input_name } = getPromptNameSummary(args);
|
|
121
|
+
const input_image = input_name
|
|
122
|
+
? await fileManager.getFileContent(input_name)
|
|
123
|
+
: undefined;
|
|
124
|
+
|
|
125
|
+
const image = await imageGenerator.generate(prompt, input_image);
|
|
126
|
+
const mimeType = getMimeTypeFromDataUrl(image);
|
|
127
|
+
await fileManager.putFileContent(name, summary, image);
|
|
128
|
+
const url = fileManager.getSessionFileRelativeUrl(name);
|
|
129
|
+
return { response: url, metadata: { type: mimeType } };
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
134
|
+
setup: async (agent: Agent) => {
|
|
135
|
+
agent.addAgentTool(GEN_IMAGE_FILE_DESC, toolFn);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|