@yrpri/api 9.0.101 → 9.0.102
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/active-citizen/engine/allOurIdeas/explainAnswersAssistant.js +11 -1
- package/active-citizen/llms/baseChatBot.js +8 -75
- package/agents/assistants/agentAssistant.js +3 -2
- package/agents/assistants/baseAssistant.js +7 -30
- package/agents/assistants/baseAssistantWithVoice.js +3 -3
- package/agents/assistants/voiceAssistant.js +2 -2
- package/agents/controllers/assistantsController.js +145 -111
- package/package.json +1 -1
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { OpenAI } from "openai";
|
|
2
2
|
import { YpBaseChatBot } from "../../llms/baseChatBot.js";
|
|
3
|
+
import ioredis from "ioredis";
|
|
4
|
+
import { v4 as uuidv4 } from "uuid";
|
|
5
|
+
const tlsOptions = process.env.REDIS_MEMORY_URL?.startsWith("rediss://")
|
|
6
|
+
? { rejectUnauthorized: false }
|
|
7
|
+
: undefined;
|
|
3
8
|
export class ExplainAnswersAssistant extends YpBaseChatBot {
|
|
4
9
|
constructor(wsClientId, wsClients, languageName) {
|
|
5
|
-
|
|
10
|
+
const redisConnection = new ioredis.default(process.env.REDIS_MEMORY_URL ||
|
|
11
|
+
process.env.REDIS_URL ||
|
|
12
|
+
"redis://localhost:6379", {
|
|
13
|
+
tls: tlsOptions,
|
|
14
|
+
});
|
|
15
|
+
super(wsClientId, wsClients, redisConnection, `${YpBaseChatBot.redisMemoryKeyPrefix}-${uuidv4()}-explain-answers-assistant`);
|
|
6
16
|
this.modelName = "gpt-4o";
|
|
7
17
|
this.maxTokens = 4000;
|
|
8
18
|
this.temperature = 0.8;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { OpenAI } from "openai";
|
|
2
|
-
import { v4 as uuidv4 } from "uuid";
|
|
3
|
-
import ioredis from "ioredis";
|
|
4
2
|
const DEBUG = false;
|
|
5
3
|
const url = process.env.REDIS_MEMORY_URL ||
|
|
6
4
|
process.env.REDIS_URL ||
|
|
@@ -9,68 +7,36 @@ const tlsOptions = url.startsWith("rediss://")
|
|
|
9
7
|
? { rejectUnauthorized: false }
|
|
10
8
|
: undefined;
|
|
11
9
|
export class YpBaseChatBot {
|
|
12
|
-
get redisKey() {
|
|
13
|
-
return `${YpBaseChatBot.redisMemoryKeyPrefix}-${this.memoryId}`;
|
|
14
|
-
}
|
|
15
10
|
destroy() {
|
|
16
11
|
this.wsClientSocket = undefined;
|
|
17
12
|
}
|
|
18
|
-
static loadMemoryFromRedis(memoryId) {
|
|
19
|
-
return new Promise(async (resolve, reject) => {
|
|
20
|
-
try {
|
|
21
|
-
//@ts-ignore
|
|
22
|
-
const redis = new ioredis.default(process.env.REDIS_MEMORY_URL ||
|
|
23
|
-
process.env.REDIS_URL ||
|
|
24
|
-
"redis://localhost:6379", {
|
|
25
|
-
tls: tlsOptions,
|
|
26
|
-
});
|
|
27
|
-
const memoryString = await redis.get(`${YpBaseChatBot.redisMemoryKeyPrefix}-${memoryId}`);
|
|
28
|
-
if (memoryString) {
|
|
29
|
-
const memory = JSON.parse(memoryString);
|
|
30
|
-
resolve(memory);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
resolve(undefined);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
console.error("Can't load memory from redis", error);
|
|
38
|
-
resolve(undefined);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
static getRedisKey(memoryId) {
|
|
43
|
-
return `${YpBaseChatBot.redisMemoryKeyPrefix}-${memoryId}`;
|
|
44
|
-
}
|
|
45
13
|
loadMemory() {
|
|
46
14
|
return new Promise(async (resolve, reject) => {
|
|
47
15
|
try {
|
|
16
|
+
console.log("loadMemoryWithOwnership loadMemory: redisKey: ", this.redisKey);
|
|
48
17
|
const memoryString = await this.redis.get(this.redisKey);
|
|
49
18
|
if (memoryString) {
|
|
50
19
|
const memory = JSON.parse(memoryString);
|
|
51
20
|
resolve(memory);
|
|
52
21
|
}
|
|
53
22
|
else {
|
|
54
|
-
|
|
23
|
+
console.error("loadMemoryWithOwnership loadMemory: no memory found");
|
|
24
|
+
resolve(undefined);
|
|
55
25
|
}
|
|
56
26
|
}
|
|
57
27
|
catch (error) {
|
|
58
|
-
console.error("Can't load memory from redis", error);
|
|
59
|
-
resolve(
|
|
28
|
+
console.error("loadMemoryWithOwnership loadMemory: Can't load memory from redis", error);
|
|
29
|
+
resolve(undefined);
|
|
60
30
|
}
|
|
61
31
|
});
|
|
62
32
|
}
|
|
63
|
-
constructor(wsClientId, wsClients,
|
|
33
|
+
constructor(wsClientId, wsClients, redisConnection, redisKey) {
|
|
64
34
|
this.temperature = 0.7;
|
|
65
35
|
this.maxTokens = 16000;
|
|
66
36
|
this.llmModel = "gpt-4o";
|
|
67
37
|
this.persistMemory = false;
|
|
68
|
-
|
|
69
|
-
this.
|
|
70
|
-
process.env.REDIS_URL ||
|
|
71
|
-
"redis://localhost:6379", {
|
|
72
|
-
tls: tlsOptions,
|
|
73
|
-
});
|
|
38
|
+
this.redis = redisConnection;
|
|
39
|
+
this.redisKey = redisKey;
|
|
74
40
|
this.wsClientId = wsClientId;
|
|
75
41
|
this.wsClientSocket = wsClients.get(this.wsClientId);
|
|
76
42
|
this.wsClients = wsClients;
|
|
@@ -81,34 +47,6 @@ export class YpBaseChatBot {
|
|
|
81
47
|
this.openaiClient = new OpenAI({
|
|
82
48
|
apiKey: process.env.OPENAI_API_KEY,
|
|
83
49
|
});
|
|
84
|
-
this.memoryId = memoryId;
|
|
85
|
-
this.setupMemory(memoryId);
|
|
86
|
-
}
|
|
87
|
-
async setupMemory(memoryId = undefined) {
|
|
88
|
-
if (!this.memory) {
|
|
89
|
-
this.memory = await this.loadMemory();
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
this.memoryId = uuidv4();
|
|
93
|
-
this.memory = this.getEmptyMemory();
|
|
94
|
-
if (this.wsClientSocket) {
|
|
95
|
-
this.sendMemoryId();
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
console.error("No wsClientSocket found");
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
async getLoadedMemory() {
|
|
103
|
-
return await this.loadMemory();
|
|
104
|
-
}
|
|
105
|
-
sendMemoryId() {
|
|
106
|
-
const botMessage = {
|
|
107
|
-
sender: "assistant",
|
|
108
|
-
type: "memoryIdCreated",
|
|
109
|
-
data: this.memoryId,
|
|
110
|
-
};
|
|
111
|
-
this.wsClientSocket.send(JSON.stringify(botMessage));
|
|
112
50
|
}
|
|
113
51
|
async saveMemory() {
|
|
114
52
|
if (this.memory) {
|
|
@@ -161,11 +99,6 @@ export class YpBaseChatBot {
|
|
|
161
99
|
};
|
|
162
100
|
this.wsClientSocket.send(JSON.stringify(botMessage));
|
|
163
101
|
}
|
|
164
|
-
getEmptyMemory() {
|
|
165
|
-
return {
|
|
166
|
-
redisKey: this.redisKey,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
102
|
sendToClient(sender, message, type = "stream", uniqueToken = undefined, hiddenContextMessage = false) {
|
|
170
103
|
try {
|
|
171
104
|
if (process.env.WS_DEBUG) {
|
|
@@ -4,8 +4,8 @@ import { AgentSelectionMode } from "./modes/agentSelectionMode.js";
|
|
|
4
4
|
import { DirectConversationMode } from "./modes/agentDirectConnection.js";
|
|
5
5
|
import { SubscriptionManager } from "../managers/subscriptionManager.js";
|
|
6
6
|
export class YpAgentAssistant extends YpBaseAssistantWithVoice {
|
|
7
|
-
constructor(wsClientId, wsClients, redis, voiceEnabled,
|
|
8
|
-
super(wsClientId, wsClients, redis, voiceEnabled,
|
|
7
|
+
constructor(wsClientId, wsClients, redis, voiceEnabled, redisKey, domainId) {
|
|
8
|
+
super(wsClientId, wsClients, redis, voiceEnabled, redisKey, domainId);
|
|
9
9
|
this.availableAgents = [];
|
|
10
10
|
this.runningAgents = [];
|
|
11
11
|
this.agentSelectionMode = new AgentSelectionMode(this);
|
|
@@ -44,6 +44,7 @@ export class YpAgentAssistant extends YpBaseAssistantWithVoice {
|
|
|
44
44
|
return this.memory.currentAgentStatus?.subscriptionId != undefined;
|
|
45
45
|
}
|
|
46
46
|
get hasConfiguredcurrentAgentProduct() {
|
|
47
|
+
console.log(`configuration: ${this.redisKey}: ${JSON.stringify(this.memory.currentAgentStatus, null, 2)}`);
|
|
47
48
|
return this.memory.currentAgentStatus?.configurationState === "configured";
|
|
48
49
|
}
|
|
49
50
|
async isCurrentAgentRunning() {
|
|
@@ -13,8 +13,8 @@ export var CommonModes;
|
|
|
13
13
|
CommonModes["ErrorRecovery"] = "error_recovery";
|
|
14
14
|
})(CommonModes || (CommonModes = {}));
|
|
15
15
|
export class YpBaseAssistant extends YpBaseChatBot {
|
|
16
|
-
constructor(wsClientId, wsClients, redis,
|
|
17
|
-
super(wsClientId, wsClients,
|
|
16
|
+
constructor(wsClientId, wsClients, redis, redisKey, domainId) {
|
|
17
|
+
super(wsClientId, wsClients, redis, redisKey);
|
|
18
18
|
this.persistMemory = true;
|
|
19
19
|
this.DEBUG = false;
|
|
20
20
|
this.modes = new Map();
|
|
@@ -56,9 +56,8 @@ Never engage in off topic conversations, always politely steer the conversation
|
|
|
56
56
|
apiKey: process.env.OPENAI_API_KEY,
|
|
57
57
|
});
|
|
58
58
|
this.eventEmitter = new EventEmitter();
|
|
59
|
+
this.clientSystemMessageListener = this.handleClientSystemMessage.bind(this);
|
|
59
60
|
this.setupClientSystemMessageListener();
|
|
60
|
-
this.clientSystemMessageListener =
|
|
61
|
-
this.handleClientSystemMessage.bind(this);
|
|
62
61
|
this.on("update-ai-model-session", this.updateAiModelSession.bind(this));
|
|
63
62
|
}
|
|
64
63
|
destroy() {
|
|
@@ -89,8 +88,8 @@ Never engage in off topic conversations, always politely steer the conversation
|
|
|
89
88
|
}
|
|
90
89
|
}
|
|
91
90
|
setupClientSystemMessageListener() {
|
|
92
|
-
console.log("WebSockets: setupClientSystemMessageListener called for wsClientId:", this.wsClientId);
|
|
93
|
-
this.wsClientSocket.on("message", this.
|
|
91
|
+
console.log("WebSockets: setupClientSystemMessageListener called for wsClientId:", this.wsClientId, this.clientSystemMessageListener);
|
|
92
|
+
this.wsClientSocket.on("message", this.clientSystemMessageListener);
|
|
94
93
|
const listenerCountAfter = this.wsClientSocket.listenerCount("message");
|
|
95
94
|
console.log('Number of "message" listeners after adding:', listenerCountAfter);
|
|
96
95
|
}
|
|
@@ -272,16 +271,6 @@ Never engage in off topic conversations, always politely steer the conversation
|
|
|
272
271
|
name: toolCall.name,
|
|
273
272
|
};
|
|
274
273
|
}
|
|
275
|
-
getEmptyMemory() {
|
|
276
|
-
return {
|
|
277
|
-
redisKey: this.redisKey,
|
|
278
|
-
chatLog: [],
|
|
279
|
-
completeChatLog: [],
|
|
280
|
-
currentMode: "agent_selection_mode",
|
|
281
|
-
modeHistory: [],
|
|
282
|
-
modeData: undefined,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
274
|
/**
|
|
286
275
|
* Handle executing tool calls with results
|
|
287
276
|
*/
|
|
@@ -412,25 +401,13 @@ Never engage in off topic conversations, always politely steer the conversation
|
|
|
412
401
|
throw error;
|
|
413
402
|
}
|
|
414
403
|
}
|
|
415
|
-
async setupMemory(memoryId = undefined) {
|
|
416
|
-
// DO nothing override call from constructor
|
|
417
|
-
}
|
|
418
404
|
async setupMemoryAsync() {
|
|
419
405
|
if (!this.memory) {
|
|
420
|
-
console.log("
|
|
406
|
+
console.log("loadMemoryWithOwnership: loading memory");
|
|
421
407
|
this.memory = (await this.loadMemory());
|
|
422
408
|
}
|
|
423
409
|
if (!this.memory) {
|
|
424
|
-
console.
|
|
425
|
-
//this.memoryId = uuidv4();
|
|
426
|
-
this.memory = this.getEmptyMemory();
|
|
427
|
-
if (this.wsClientSocket) {
|
|
428
|
-
this.sendMemoryId();
|
|
429
|
-
}
|
|
430
|
-
else {
|
|
431
|
-
console.error("No wsClientSocket found");
|
|
432
|
-
}
|
|
433
|
-
await this.saveMemory();
|
|
410
|
+
console.error("loadMemoryWithOwnership: No memory found!!!");
|
|
434
411
|
}
|
|
435
412
|
}
|
|
436
413
|
async loadMemoryAsync() {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { YpBaseAssistant } from "./baseAssistant.js";
|
|
2
2
|
import { YpBaseChatBotWithVoice } from "./voiceAssistant.js";
|
|
3
3
|
export class YpBaseAssistantWithVoice extends YpBaseAssistant {
|
|
4
|
-
constructor(wsClientId, wsClients, redis, voiceEnabled,
|
|
5
|
-
super(wsClientId, wsClients, redis,
|
|
4
|
+
constructor(wsClientId, wsClients, redis, voiceEnabled, redisKey, domainId) {
|
|
5
|
+
super(wsClientId, wsClients, redis, redisKey, domainId);
|
|
6
6
|
if (!domainId) {
|
|
7
7
|
throw new Error("Domain ID is required");
|
|
8
8
|
}
|
|
@@ -34,7 +34,7 @@ export class YpBaseAssistantWithVoice extends YpBaseAssistant {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
async createVoiceBot() {
|
|
37
|
-
this.voiceBot = new YpBaseChatBotWithVoice(this.wsClientId, this.wsClients, this.
|
|
37
|
+
this.voiceBot = new YpBaseChatBotWithVoice(this.wsClientId, this.wsClients, this.redisKey, this.redis, true, this);
|
|
38
38
|
await this.voiceBot.initializeMainAssistantVoiceConnection();
|
|
39
39
|
this.setupVoiceEventForwarder();
|
|
40
40
|
await this.voiceBot.updateVoiceConfig({
|
|
@@ -2,8 +2,8 @@ import { YpBaseChatBot } from "../../active-citizen/llms/baseChatBot.js";
|
|
|
2
2
|
import WebSocket from "ws";
|
|
3
3
|
// Extend the base class with voice capabilities
|
|
4
4
|
export class YpBaseChatBotWithVoice extends YpBaseChatBot {
|
|
5
|
-
constructor(wsClientId, wsClients,
|
|
6
|
-
super(wsClientId, wsClients,
|
|
5
|
+
constructor(wsClientId, wsClients, redisKey, redisConnection, voiceEnabled = false, parentAssistant) {
|
|
6
|
+
super(wsClientId, wsClients, redisConnection, redisKey);
|
|
7
7
|
this.voiceEnabled = false;
|
|
8
8
|
this.VAD_TIMEOUT = 1000; // 1 second of silence for VAD
|
|
9
9
|
this.sendTranscriptsToClient = false;
|
|
@@ -188,7 +188,7 @@ export class AssistantController {
|
|
|
188
188
|
const { requiredQuestionsAnswers } = req.body;
|
|
189
189
|
const subscriptionId = parseInt(req.params.subscriptionId);
|
|
190
190
|
try {
|
|
191
|
-
const memoryId = this.
|
|
191
|
+
const memoryId = this.getMemoryRedisKey(req);
|
|
192
192
|
// Get subscription
|
|
193
193
|
const subscription = await YpSubscription.findOne({
|
|
194
194
|
where: {
|
|
@@ -213,80 +213,57 @@ export class AssistantController {
|
|
|
213
213
|
res.sendStatus(200);
|
|
214
214
|
};
|
|
215
215
|
this.updateAssistantMemoryLoginStatus = async (req, res) => {
|
|
216
|
-
if (req.user
|
|
217
|
-
|
|
218
|
-
let memoryId = this.getMemoryUserId(req);
|
|
219
|
-
let redisKey = YpAgentAssistant.getRedisKey(memoryId);
|
|
220
|
-
console.log(`Starting to update login status for memoryId: ${memoryId} with user: ${req.user?.name}`);
|
|
221
|
-
let memory = (await YpAgentAssistant.loadMemoryFromRedis(memoryId));
|
|
222
|
-
if (memory) {
|
|
223
|
-
memory.currentUser = req.user;
|
|
224
|
-
await req.redisClient.set(redisKey, JSON.stringify(memory));
|
|
225
|
-
//TODO: Check if needed, wait for 300ms to make sure redis is saved
|
|
226
|
-
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
227
|
-
console.log(`Updated login status for memoryId: ${memoryId} with user: ${req.user?.name}`);
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
console.error(`No memory found to update login status for id ${memoryId}`);
|
|
231
|
-
}
|
|
232
|
-
res.sendStatus(200);
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
console.error("Error updating login status:", error);
|
|
236
|
-
res.sendStatus(500);
|
|
237
|
-
}
|
|
216
|
+
if (!req.user) {
|
|
217
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
238
218
|
}
|
|
239
|
-
|
|
240
|
-
|
|
219
|
+
try {
|
|
220
|
+
const memory = await this.loadMemoryWithOwnership(req, res);
|
|
221
|
+
if (!memory)
|
|
222
|
+
return; // already 401/403 if not allowed
|
|
223
|
+
// Now memory is either newly created or already owned by this user (or still guest if you didn't upgrade)
|
|
224
|
+
memory.currentUser = req.user;
|
|
225
|
+
await req.redisClient.set(memory.redisKey, JSON.stringify(memory));
|
|
226
|
+
res.sendStatus(200);
|
|
241
227
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const userIdentifier = req.body.clientMemoryUuid || req.query.clientMemoryUuid;
|
|
246
|
-
if (!userIdentifier) {
|
|
247
|
-
throw new Error("No user identifier found");
|
|
228
|
+
catch (error) {
|
|
229
|
+
console.error("Error updating login status:", error);
|
|
230
|
+
res.sendStatus(500);
|
|
248
231
|
}
|
|
249
|
-
return `${req.params.domainId}-${userIdentifier}`;
|
|
250
232
|
};
|
|
233
|
+
this.defaultStartAgentMode = "agent_selection_mode";
|
|
251
234
|
this.clearChatLog = async (req, res) => {
|
|
252
235
|
try {
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (memory) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
console.warn("No user found to clear runs for");
|
|
283
|
-
}
|
|
284
|
-
res.sendStatus(200);
|
|
236
|
+
const memory = await this.loadMemoryWithOwnership(req, res);
|
|
237
|
+
if (!memory)
|
|
238
|
+
return; // loadMemoryWithOwnership has already sent 401/403 if needed
|
|
239
|
+
// Now we know this request is allowed to see/modify the memory
|
|
240
|
+
if (!memory.allChatLogs) {
|
|
241
|
+
memory.allChatLogs = [];
|
|
242
|
+
}
|
|
243
|
+
if (memory.chatLog) {
|
|
244
|
+
memory.allChatLogs = [...memory.allChatLogs, ...memory.chatLog];
|
|
245
|
+
}
|
|
246
|
+
memory.chatLog = [];
|
|
247
|
+
memory.currentMode = this.defaultStartAgentMode;
|
|
248
|
+
memory.haveShownConfigurationWidget = false;
|
|
249
|
+
memory.haveShownLoginWidget = false;
|
|
250
|
+
memory.currentAgentStatus = undefined;
|
|
251
|
+
if (!req.user && memory.ownerUserId === null) {
|
|
252
|
+
memory.currentUser = undefined;
|
|
253
|
+
}
|
|
254
|
+
await req.redisClient.set(memory.redisKey, JSON.stringify(memory));
|
|
255
|
+
if (req.user) {
|
|
256
|
+
//TODO: REMOVE THIS when we have multi workflows
|
|
257
|
+
await YpSubscription.destroy({
|
|
258
|
+
where: {
|
|
259
|
+
user_id: req.user?.id,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
285
262
|
}
|
|
286
263
|
else {
|
|
287
|
-
console.warn(
|
|
288
|
-
res.sendStatus(200);
|
|
264
|
+
console.warn("No user found to clear runs for");
|
|
289
265
|
}
|
|
266
|
+
res.sendStatus(200);
|
|
290
267
|
}
|
|
291
268
|
catch (error) {
|
|
292
269
|
console.error("Error clearing chat log:", error);
|
|
@@ -294,47 +271,11 @@ export class AssistantController {
|
|
|
294
271
|
}
|
|
295
272
|
};
|
|
296
273
|
this.getMemory = async (req, res) => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
try {
|
|
300
|
-
memoryId = this.getMemoryUserId(req);
|
|
301
|
-
console.log(`Getting memory for memoryId: ${memoryId}`);
|
|
302
|
-
if (memoryId) {
|
|
303
|
-
memory = (await YpAgentAssistant.loadMemoryFromRedis(memoryId));
|
|
304
|
-
if (!memory) {
|
|
305
|
-
console.log(`memory not found for id ${memoryId}`);
|
|
306
|
-
memory = {
|
|
307
|
-
redisKey: YpAgentAssistant.getRedisKey(memoryId),
|
|
308
|
-
chatLog: [],
|
|
309
|
-
currentMode: this.defaultStartAgentMode,
|
|
310
|
-
modeHistory: [],
|
|
311
|
-
modeData: undefined,
|
|
312
|
-
};
|
|
313
|
-
await req.redisClient.set(memory.redisKey, JSON.stringify(memory));
|
|
314
|
-
}
|
|
315
|
-
else {
|
|
316
|
-
if (req.user && !memory.currentUser) {
|
|
317
|
-
memory.currentUser = req.user;
|
|
318
|
-
}
|
|
319
|
-
else if (!req.user && memory.currentUser) {
|
|
320
|
-
memory.currentUser = undefined;
|
|
321
|
-
}
|
|
322
|
-
await req.redisClient.set(memory.redisKey, JSON.stringify(memory));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
catch (error) {
|
|
327
|
-
console.log(error);
|
|
328
|
-
res.sendStatus(500);
|
|
274
|
+
const memory = await this.loadMemoryWithOwnership(req, res);
|
|
275
|
+
if (!memory)
|
|
329
276
|
return;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
res.send(memory);
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
console.error(`No memory found for memoryId: ${memoryId}`);
|
|
336
|
-
res.send({});
|
|
337
|
-
}
|
|
277
|
+
console.log(`Getting memory at key: ${memory.redisKey}`);
|
|
278
|
+
return res.json(memory);
|
|
338
279
|
};
|
|
339
280
|
// New API endpoints for workflow management
|
|
340
281
|
this.getRunningWorkflowConversations = async (req, res) => {
|
|
@@ -418,10 +359,102 @@ export class AssistantController {
|
|
|
418
359
|
const status = await this.agentQueueManager.getAgentStatus(agentId);
|
|
419
360
|
return status ? status.messages[status.messages.length - 1] : null;
|
|
420
361
|
}
|
|
362
|
+
getMemoryRedisKey(req) {
|
|
363
|
+
const userIdentifier = req.body.clientMemoryUuid || req.query.clientMemoryUuid;
|
|
364
|
+
return `assistant:${ /*req.params.domainId*/1}:${userIdentifier}`;
|
|
365
|
+
}
|
|
366
|
+
async loadMemoryWithOwnership(req, res) {
|
|
367
|
+
// Get the calling function name from the stack trace
|
|
368
|
+
const stackTrace = new Error().stack;
|
|
369
|
+
const callerLine = stackTrace?.split("\n")[2]; // First line is Error, second is current function, third is caller
|
|
370
|
+
const callerMatch = callerLine?.match(/at\s+(.*)\s+\(/);
|
|
371
|
+
const caller = callerMatch ? callerMatch[1] : "unknown";
|
|
372
|
+
console.debug(`loadMemoryWithOwnership called by: ${caller}`);
|
|
373
|
+
console.debug(`loadMemoryWithOwnership: ${JSON.stringify(req.body, null, 2)}`);
|
|
374
|
+
const redisKey = this.getMemoryRedisKey(req);
|
|
375
|
+
console.debug(`loadMemoryWithOwnership: redisKey: ${redisKey}`);
|
|
376
|
+
try {
|
|
377
|
+
const rawMemory = await req.redisClient.get(redisKey);
|
|
378
|
+
let memory = rawMemory
|
|
379
|
+
? JSON.parse(rawMemory)
|
|
380
|
+
: null;
|
|
381
|
+
// If no memory, create new
|
|
382
|
+
if (!memory) {
|
|
383
|
+
console.debug(`loadMemoryWithOwnership: creating new memory`);
|
|
384
|
+
memory = {
|
|
385
|
+
redisKey,
|
|
386
|
+
chatLog: [],
|
|
387
|
+
completeChatLog: [],
|
|
388
|
+
currentMode: this.defaultStartAgentMode,
|
|
389
|
+
modeHistory: [],
|
|
390
|
+
modeData: undefined,
|
|
391
|
+
ownerUserId: null,
|
|
392
|
+
};
|
|
393
|
+
if (req.user) {
|
|
394
|
+
console.debug(`loadMemoryWithOwnership: setting ownerUserId to ${req.user.id}`);
|
|
395
|
+
memory.ownerUserId = req.user.id;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
console.debug(`loadMemoryWithOwnership: no user in request`);
|
|
399
|
+
}
|
|
400
|
+
await req.redisClient.set(redisKey, JSON.stringify(memory));
|
|
401
|
+
const rawAfterSet = await req.redisClient.get(redisKey);
|
|
402
|
+
console.log("loadMemoryWithOwnership: After set, raw in Redis is:", rawAfterSet);
|
|
403
|
+
console.debug(`loadMemoryWithOwnership: returning new memory`);
|
|
404
|
+
return memory;
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
console.debug(`loadMemoryWithOwnership: memory already exists`);
|
|
408
|
+
console.debug(`loadMemoryWithOwnership: memory: ${JSON.stringify(memory, null, 2)}`);
|
|
409
|
+
}
|
|
410
|
+
// If memory is owned by someone
|
|
411
|
+
if (memory.ownerUserId !== null) {
|
|
412
|
+
console.debug(`loadMemoryWithOwnership: memory is owned by ${memory.ownerUserId}`);
|
|
413
|
+
if (!req.user) {
|
|
414
|
+
console.debug(`loadMemoryWithOwnership: no user in request`);
|
|
415
|
+
res.status(401).json({ error: "Unauthorized" });
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
console.debug(`loadMemoryWithOwnership: user in request`);
|
|
420
|
+
}
|
|
421
|
+
if (memory.ownerUserId !== req.user.id) {
|
|
422
|
+
console.debug(`loadMemoryWithOwnership: ownerUserId does not match ${memory.ownerUserId} !== ${req.user.id}`);
|
|
423
|
+
res.status(403).json({ error: "Forbidden" });
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
console.debug(`loadMemoryWithOwnership: ownerUserId matches ${memory.ownerUserId} === ${req.user.id}`);
|
|
428
|
+
}
|
|
429
|
+
// Same user => fine
|
|
430
|
+
return memory;
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
// memory is guest
|
|
434
|
+
if (req.user) {
|
|
435
|
+
// optionally upgrade
|
|
436
|
+
memory.ownerUserId = req.user.id;
|
|
437
|
+
await req.redisClient.set(redisKey, JSON.stringify(memory));
|
|
438
|
+
console.debug(`loadMemoryWithOwnership: returning memory with ownerUserId ${memory.ownerUserId}`);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
console.debug(`loadMemoryWithOwnership: returning memory with ownerUserId null`);
|
|
442
|
+
}
|
|
443
|
+
return memory;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (error) {
|
|
447
|
+
console.error("Error loading memory:", error);
|
|
448
|
+
res.status(500).json({ error: "Internal server error" });
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
421
452
|
async startVoiceSession(req, res) {
|
|
422
453
|
try {
|
|
423
454
|
const { wsClientId, currentMode } = req.body;
|
|
424
|
-
const
|
|
455
|
+
const memory = await this.loadMemoryWithOwnership(req, res);
|
|
456
|
+
if (!memory)
|
|
457
|
+
return;
|
|
425
458
|
console.log(`Starting chat session for client: ${wsClientId}`);
|
|
426
459
|
let oldVoiceAssistant = this.voiceAssistantInstances.get("voiceAssistant");
|
|
427
460
|
if (oldVoiceAssistant) {
|
|
@@ -433,7 +466,7 @@ export class AssistantController {
|
|
|
433
466
|
oldChatAssistant.destroy();
|
|
434
467
|
this.chatAssistantInstances.delete("mainAssistant");
|
|
435
468
|
}
|
|
436
|
-
const assistant = new YpAgentAssistant(wsClientId, this.wsClients, req.redisClient, true, parseInt(req.params.domainId)
|
|
469
|
+
const assistant = new YpAgentAssistant(wsClientId, this.wsClients, req.redisClient, true, memory.redisKey, parseInt(req.params.domainId));
|
|
437
470
|
this.voiceAssistantInstances.set("voiceAssistant", assistant);
|
|
438
471
|
await assistant.initialize();
|
|
439
472
|
res.status(200).json({
|
|
@@ -448,9 +481,10 @@ export class AssistantController {
|
|
|
448
481
|
}
|
|
449
482
|
async sendChatMessage(req, res) {
|
|
450
483
|
try {
|
|
484
|
+
const memory = await this.loadMemoryWithOwnership(req, res);
|
|
485
|
+
if (!memory)
|
|
486
|
+
return; // ends early if 401/403
|
|
451
487
|
const { wsClientId, chatLog, currentMode } = req.body;
|
|
452
|
-
const memoryId = this.getMemoryUserId(req);
|
|
453
|
-
console.log(`Starting chat session for client: ${wsClientId} with currentMode: ${currentMode}`);
|
|
454
488
|
const oldVoiceAssistant = this.voiceAssistantInstances.get("voiceAssistant");
|
|
455
489
|
if (oldVoiceAssistant) {
|
|
456
490
|
oldVoiceAssistant.destroy();
|
|
@@ -461,7 +495,7 @@ export class AssistantController {
|
|
|
461
495
|
oldAssistant.destroy();
|
|
462
496
|
this.chatAssistantInstances.delete("mainAssistant");
|
|
463
497
|
}
|
|
464
|
-
const assistant = new YpAgentAssistant(wsClientId, this.wsClients, req.redisClient, false, parseInt(req.params.domainId)
|
|
498
|
+
const assistant = new YpAgentAssistant(wsClientId, this.wsClients, req.redisClient, false, memory.redisKey, parseInt(req.params.domainId));
|
|
465
499
|
this.chatAssistantInstances.set("mainAssistant", assistant);
|
|
466
500
|
assistant.conversation(chatLog);
|
|
467
501
|
res.status(200).json({
|