@yrpri/api 9.0.100 → 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.
@@ -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
- super(wsClientId, wsClients, "undefined");
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
- resolve(this.getEmptyMemory());
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(this.getEmptyMemory());
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, memoryId) {
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
- //@ts-ignore
69
- this.redis = new ioredis.default(process.env.REDIS_MEMORY_URL ||
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, domainId, memoryId) {
8
- super(wsClientId, wsClients, redis, voiceEnabled, domainId, memoryId);
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, domainId, memoryId) {
17
- super(wsClientId, wsClients, memoryId);
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.handleClientSystemMessage.bind(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("setupMemoryAsync: loading memory");
406
+ console.log("loadMemoryWithOwnership: loading memory");
421
407
  this.memory = (await this.loadMemory());
422
408
  }
423
409
  if (!this.memory) {
424
- console.log("setupMemoryAsync: creating new memory");
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, domainId, memoryId) {
5
- super(wsClientId, wsClients, redis, domainId, memoryId);
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.memoryId, true, 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, memoryId, voiceEnabled = false, parentAssistant) {
6
- super(wsClientId, wsClients, memoryId);
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.getMemoryUserId(req);
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 && req.params.domainId) {
217
- try {
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
- else {
240
- res.sendStatus(401);
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
- this.defaultStartAgentMode = "agent_selection_mode";
244
- this.getMemoryUserId = (req) => {
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 memoryId = this.getMemoryUserId(req);
254
- console.log(`Clearing chat log for memoryId: ${memoryId}`);
255
- const redisKey = YpAgentAssistant.getRedisKey(memoryId);
256
- const memory = (await YpAgentAssistant.loadMemoryFromRedis(memoryId));
257
- if (memory) {
258
- if (!memory.allChatLogs) {
259
- memory.allChatLogs = [];
260
- }
261
- if (memory.chatLog) {
262
- memory.allChatLogs = [...memory.allChatLogs, ...memory.chatLog];
263
- }
264
- memory.chatLog = [];
265
- memory.currentMode = this.defaultStartAgentMode;
266
- memory.haveShownConfigurationWidget = false;
267
- memory.haveShownLoginWidget = false;
268
- memory.currentAgentStatus = undefined;
269
- if (!req.user) {
270
- memory.currentUser = undefined;
271
- }
272
- await req.redisClient.set(redisKey, JSON.stringify(memory));
273
- if (req.user) {
274
- //TODO: REMOVE THIS !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
275
- await YpSubscription.destroy({
276
- where: {
277
- user_id: req.user?.id,
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(`No memory found to clear for id ${memoryId}`);
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
- let memory;
298
- let memoryId;
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
- if (memory) {
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 memoryId = this.getMemoryUserId(req);
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), memoryId);
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), memoryId);
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yrpri/api",
3
- "version": "9.0.100",
3
+ "version": "9.0.102",
4
4
  "license": "MIT",
5
5
  "author": "Robert Bjarnason & Citizens Foundation",
6
6
  "repository": {