@nocobase/plugin-ai 2.1.0-beta.27 → 2.1.0-beta.30

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.
Files changed (87) hide show
  1. package/dist/ai/docs/nocobase/api/cli/api/dynamic.md +7 -0
  2. package/dist/ai/docs/nocobase/api/cli/api/resource/index.md +3 -0
  3. package/dist/ai/docs/nocobase/api/cli/app/down.md +7 -3
  4. package/dist/ai/docs/nocobase/api/cli/app/index.md +1 -1
  5. package/dist/ai/docs/nocobase/api/cli/app/logs.md +3 -0
  6. package/dist/ai/docs/nocobase/api/cli/app/restart.md +4 -0
  7. package/dist/ai/docs/nocobase/api/cli/app/start.md +4 -0
  8. package/dist/ai/docs/nocobase/api/cli/app/stop.md +3 -0
  9. package/dist/ai/docs/nocobase/api/cli/app/upgrade.md +5 -0
  10. package/dist/ai/docs/nocobase/api/cli/env/add.md +11 -3
  11. package/dist/ai/docs/nocobase/api/cli/env/auth.md +1 -1
  12. package/dist/ai/docs/nocobase/api/cli/env/current.md +29 -0
  13. package/dist/ai/docs/nocobase/api/cli/env/index.md +22 -4
  14. package/dist/ai/docs/nocobase/api/cli/env/info.md +1 -5
  15. package/dist/ai/docs/nocobase/api/cli/env/list.md +11 -6
  16. package/dist/ai/docs/nocobase/api/cli/env/remove.md +4 -1
  17. package/dist/ai/docs/nocobase/api/cli/env/status.md +52 -0
  18. package/dist/ai/docs/nocobase/api/cli/env/update.md +1 -1
  19. package/dist/ai/docs/nocobase/api/cli/env/use.md +11 -1
  20. package/dist/ai/docs/nocobase/api/cli/index.md +13 -1
  21. package/dist/ai/docs/nocobase/api/cli/license/activate.md +4 -1
  22. package/dist/ai/docs/nocobase/api/cli/license/id.md +4 -0
  23. package/dist/ai/docs/nocobase/api/cli/license/plugins/clean.md +5 -1
  24. package/dist/ai/docs/nocobase/api/cli/license/plugins/list.md +4 -0
  25. package/dist/ai/docs/nocobase/api/cli/license/plugins/sync.md +5 -1
  26. package/dist/ai/docs/nocobase/api/cli/license/status.md +4 -0
  27. package/dist/ai/docs/nocobase/api/cli/plugin/disable.md +4 -0
  28. package/dist/ai/docs/nocobase/api/cli/plugin/enable.md +4 -0
  29. package/dist/ai/docs/nocobase/api/cli/plugin/list.md +4 -0
  30. package/dist/ai/docs/nocobase/api/cli/session/id.md +28 -0
  31. package/dist/ai/docs/nocobase/api/cli/session/index.md +41 -0
  32. package/dist/ai/docs/nocobase/api/cli/session/remove.md +35 -0
  33. package/dist/ai/docs/nocobase/api/cli/session/setup.md +47 -0
  34. package/dist/client/119.c6bf8c6433167d81.js +10 -0
  35. package/dist/client/228.b4b709f93b86b6b9.js +10 -0
  36. package/dist/client/{486.afbed6b132b3c0dd.js → 486.dcac8f3fcec19c33.js} +1 -1
  37. package/dist/client/597.b0d64948d74cf6cb.js +10 -0
  38. package/dist/client/646.5860101cb28c8272.js +10 -0
  39. package/dist/client/711.92cd94681fde7e05.js +10 -0
  40. package/dist/client/768.5177bff46ae71a5b.js +10 -0
  41. package/dist/client/792.abb57765453bcbcc.js +10 -0
  42. package/dist/client/820.f72ef2462b61d812.js +10 -0
  43. package/dist/client/927.ac9ee9a8c1cb4f1d.js +10 -0
  44. package/dist/client/ai-employees/chatbox/conversations/ConversationsList.d.ts +1 -15
  45. package/dist/client/ai-employees/chatbox/conversations/WorkflowTasksList.d.ts +1 -21
  46. package/dist/client/ai-employees/chatbox/hooks/useChat.d.ts +125 -0
  47. package/dist/client/ai-employees/chatbox/hooks/useChatBoxActions.d.ts +1 -1
  48. package/dist/client/ai-employees/chatbox/hooks/useChatConversationActions.d.ts +13 -1
  49. package/dist/client/ai-employees/chatbox/hooks/useChatMessageActions.d.ts +8 -8
  50. package/dist/client/ai-employees/chatbox/stores/chat-conversations.d.ts +4 -0
  51. package/dist/client/ai-employees/chatbox/stores/chat-messages.d.ts +77 -50
  52. package/dist/client/ai-employees/chatbox/stores/chat-tool-call.d.ts +24 -16
  53. package/dist/client/ai-employees/types.d.ts +1 -0
  54. package/dist/client/index.d.ts +2 -0
  55. package/dist/client/index.js +4 -4
  56. package/dist/externalVersion.js +16 -15
  57. package/dist/locale/en-US.json +1 -0
  58. package/dist/locale/zh-CN.json +1 -0
  59. package/dist/node_modules/@langchain/xai/package.json +1 -1
  60. package/dist/node_modules/fs-extra/package.json +1 -1
  61. package/dist/node_modules/jsonrepair/package.json +1 -1
  62. package/dist/node_modules/just-bash/package.json +1 -1
  63. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  64. package/dist/node_modules/openai/package.json +1 -1
  65. package/dist/node_modules/zod/package.json +1 -1
  66. package/dist/server/ai-employees/ai-conversations.d.ts +3 -1
  67. package/dist/server/ai-employees/ai-conversations.js +40 -3
  68. package/dist/server/ai-employees/ai-employee.d.ts +16 -14
  69. package/dist/server/ai-employees/ai-employee.js +65 -43
  70. package/dist/server/ai-employees/middleware/conversation.js +11 -9
  71. package/dist/server/collections/ai-conversations.js +6 -0
  72. package/dist/server/manager/llm-stream-manager.d.ts +37 -0
  73. package/dist/server/manager/llm-stream-manager.js +142 -0
  74. package/dist/server/plugin.d.ts +2 -0
  75. package/dist/server/plugin.js +3 -0
  76. package/dist/server/resource/aiConversations.d.ts +8 -0
  77. package/dist/server/resource/aiConversations.js +129 -2
  78. package/package.json +2 -2
  79. package/dist/client/119.78774f3ad953af49.js +0 -10
  80. package/dist/client/228.a3df2921c8beb766.js +0 -10
  81. package/dist/client/597.aa363881a325b5c0.js +0 -10
  82. package/dist/client/646.217a40387efbd163.js +0 -10
  83. package/dist/client/711.266b8f1c520d467a.js +0 -10
  84. package/dist/client/768.973ce32e15099a48.js +0 -10
  85. package/dist/client/792.2e48eab4767d662a.js +0 -10
  86. package/dist/client/820.6a26239ea96c075a.js +0 -10
  87. package/dist/client/927.ff5cd05b14901ae6.js +0 -10
@@ -70,6 +70,7 @@ class AIEmployee {
70
70
  legacy;
71
71
  tools;
72
72
  inWorkflow;
73
+ streamCached;
73
74
  constructor({
74
75
  ctx,
75
76
  employee,
@@ -94,10 +95,17 @@ class AIEmployee {
94
95
  this.legacy = legacy;
95
96
  this.from = from;
96
97
  this.tools = tools;
98
+ this.streamCached = this.plugin.llmStreamCachedManager.getCached(sessionId);
97
99
  const builtInManager = this.plugin.builtInManager;
98
100
  builtInManager.setupBuiltInInfo(ctx, this.employee);
99
101
  this.webSearch = webSearch;
100
- this.protocol = ChatStreamProtocol.fromContext(ctx);
102
+ this.protocol = ChatStreamProtocol.fromContext(ctx, async (chunk) => {
103
+ try {
104
+ await this.streamCached.append(chunk);
105
+ } catch (error) {
106
+ this.logger.warn("Failed to append LLM stream cache", { sessionId: this.sessionId, error });
107
+ }
108
+ });
101
109
  }
102
110
  async getFormatMessages(userMessages) {
103
111
  const { provider } = await this.plugin.aiManager.getLLMService({
@@ -194,6 +202,7 @@ class AIEmployee {
194
202
  userMessages = [],
195
203
  userDecisions
196
204
  }) {
205
+ await this.streamCached.clear();
197
206
  await this.aiConversationsRepo.update({
198
207
  values: { llmActiveState: "streaming" },
199
208
  filter: {
@@ -228,11 +237,12 @@ class AIEmployee {
228
237
  return false;
229
238
  } finally {
230
239
  await this.aiConversationsRepo.update({
231
- values: { llmActiveState: "idle" },
240
+ values: { llmActiveState: "idle", read: false },
232
241
  filter: {
233
242
  sessionId: this.sessionId
234
243
  }
235
244
  });
245
+ await this.streamCached.clear();
236
246
  }
237
247
  }
238
248
  async invoke({
@@ -386,6 +396,14 @@ class AIEmployee {
386
396
  }
387
397
  } catch (e) {
388
398
  this.logger.error("Fail to save message after conversation abort", gathered);
399
+ } finally {
400
+ await this.aiConversationsRepo.update({
401
+ values: { llmActiveState: "idle", read: true },
402
+ filter: {
403
+ sessionId: this.sessionId
404
+ }
405
+ });
406
+ await this.streamCached.clear();
389
407
  }
390
408
  });
391
409
  try {
@@ -394,7 +412,7 @@ class AIEmployee {
394
412
  from: this.from,
395
413
  username: this.employee.username
396
414
  };
397
- this.protocol.with(aiEmployeeConversation).startStream();
415
+ await this.protocol.with(aiEmployeeConversation).startStream();
398
416
  for await (const [mode, chunks] of stream) {
399
417
  if (mode === "messages") {
400
418
  const [chunk, metadata] = chunks;
@@ -404,24 +422,24 @@ class AIEmployee {
404
422
  if (chunk.content) {
405
423
  if (isReasoning) {
406
424
  isReasoning = false;
407
- this.protocol.with(currentConversation).stopReasoning();
425
+ await this.protocol.with(currentConversation).stopReasoning();
408
426
  }
409
427
  const parsedContent = provider.parseResponseChunk(chunk.content);
410
428
  if (parsedContent) {
411
- this.protocol.with(currentConversation).content(parsedContent);
429
+ await this.protocol.with(currentConversation).content(parsedContent);
412
430
  }
413
431
  }
414
432
  if ((_a = chunk.tool_call_chunks) == null ? void 0 : _a.length) {
415
- this.protocol.with(currentConversation).toolCallChunks(chunk.tool_call_chunks);
433
+ await this.protocol.with(currentConversation).toolCallChunks(chunk.tool_call_chunks);
416
434
  }
417
435
  const webSearch = provider.parseWebSearchAction(chunk);
418
436
  if (webSearch == null ? void 0 : webSearch.length) {
419
- this.protocol.with(currentConversation).webSearch(webSearch);
437
+ await this.protocol.with(currentConversation).webSearch(webSearch);
420
438
  }
421
439
  const reasoningContent = provider.parseReasoningContent(chunk);
422
440
  if (reasoningContent) {
423
441
  isReasoning = true;
424
- this.protocol.with(currentConversation).reasoning(reasoningContent);
442
+ await this.protocol.with(currentConversation).reasoning(reasoningContent);
425
443
  }
426
444
  }
427
445
  } else if (mode === "updates") {
@@ -431,8 +449,8 @@ class AIEmployee {
431
449
  await this.handleInterruptedToolCalls(
432
450
  interrupt,
433
451
  (sessionId) => aiMessageIdMap.get(sessionId),
434
- ({ messageId, interruptAction, toolCall, currentConversation }) => {
435
- this.protocol.with(currentConversation).toolCallStatus({
452
+ async ({ messageId, interruptAction, toolCall, currentConversation }) => {
453
+ await this.protocol.with(currentConversation).toolCallStatus({
436
454
  toolCall: {
437
455
  messageId,
438
456
  id: toolCall.id,
@@ -448,6 +466,7 @@ class AIEmployee {
448
466
  } else if (mode === "custom") {
449
467
  const { currentConversation } = chunks;
450
468
  if (chunks.action === "AfterAIMessageSaved") {
469
+ await this.streamCached.skipped();
451
470
  aiMessageIdMap.set(currentConversation.sessionId, chunks.body.messageId);
452
471
  const data = responseMetadata.get(chunks.body.id);
453
472
  if (data) {
@@ -476,11 +495,11 @@ class AIEmployee {
476
495
  }
477
496
  }
478
497
  } else if (chunks.action === "initToolCalls") {
479
- this.protocol.with(currentConversation).toolCalls(chunks.body);
498
+ await this.protocol.with(currentConversation).toolCalls(chunks.body);
480
499
  } else if (chunks.action === "beforeToolCall") {
481
500
  const toolsMap = await this.getToolsMap();
482
501
  const willInterrupt = this.shouldInterruptToolCall(toolsMap.get((_d = (_c = chunks.body) == null ? void 0 : _c.toolCall) == null ? void 0 : _d.name));
483
- this.protocol.with(currentConversation).toolCallStatus({
502
+ await this.protocol.with(currentConversation).toolCallStatus({
484
503
  toolCall: {
485
504
  messageId: (_f = (_e = chunks.body) == null ? void 0 : _e.toolCall) == null ? void 0 : _f.messageId,
486
505
  id: (_h = (_g = chunks.body) == null ? void 0 : _g.toolCall) == null ? void 0 : _h.id,
@@ -492,7 +511,7 @@ class AIEmployee {
492
511
  } else if (chunks.action === "afterToolCall") {
493
512
  const toolsMap = await this.getToolsMap();
494
513
  const willInterrupt = this.shouldInterruptToolCall(toolsMap.get((_l = (_k = chunks.body) == null ? void 0 : _k.toolCall) == null ? void 0 : _l.name));
495
- this.protocol.with(currentConversation).toolCallStatus({
514
+ await this.protocol.with(currentConversation).toolCallStatus({
496
515
  toolCall: {
497
516
  messageId: (_n = (_m = chunks.body) == null ? void 0 : _m.toolCall) == null ? void 0 : _n.messageId,
498
517
  id: (_p = (_o = chunks.body) == null ? void 0 : _o.toolCall) == null ? void 0 : _p.id,
@@ -516,7 +535,7 @@ class AIEmployee {
516
535
  for (const { metadata } of messages) {
517
536
  const tools = toolsMap.get(metadata.toolName);
518
537
  const toolCallResult = toolCallResultMap.get(metadata.toolCallId);
519
- this.protocol.with(currentConversation).toolCallStatus({
538
+ await this.protocol.with(currentConversation).toolCallStatus({
520
539
  toolCall: {
521
540
  messageId,
522
541
  id: metadata.toolCallId,
@@ -531,9 +550,9 @@ class AIEmployee {
531
550
  });
532
551
  }
533
552
  }
534
- this.protocol.with(currentConversation).newMessage();
553
+ await this.protocol.with(currentConversation).newMessage();
535
554
  } else if (chunks.action === "afterSubAgentInvoke") {
536
- this.protocol.with(currentConversation).subAgentCompleted();
555
+ await this.protocol.with(currentConversation).subAgentCompleted();
537
556
  }
538
557
  }
539
558
  }
@@ -541,7 +560,7 @@ class AIEmployee {
541
560
  this.sendErrorResponse("Empty message");
542
561
  return;
543
562
  }
544
- this.protocol.with(aiEmployeeConversation).endStream();
563
+ await this.protocol.with(aiEmployeeConversation).endStream();
545
564
  } catch (err) {
546
565
  this.ctx.log.error(err);
547
566
  if (err.name === "GraphRecursionError") {
@@ -1393,8 +1412,9 @@ class AgentThread {
1393
1412
  }
1394
1413
  }
1395
1414
  class ChatStreamProtocol {
1396
- constructor(streamConsumer) {
1415
+ constructor(streamConsumer, onWrite) {
1397
1416
  this.streamConsumer = streamConsumer;
1417
+ this.onWrite = onWrite;
1398
1418
  }
1399
1419
  _statistics = {
1400
1420
  sent: 0,
@@ -1405,43 +1425,45 @@ class ChatStreamProtocol {
1405
1425
  this._statistics.sent = 0;
1406
1426
  }
1407
1427
  };
1408
- static fromContext(ctx) {
1409
- return new ChatStreamProtocol(ctx.res);
1428
+ static fromContext(ctx, onWrite) {
1429
+ return new ChatStreamProtocol(ctx.res, onWrite);
1410
1430
  }
1411
1431
  with(conversation) {
1412
- const write = ({ type, body }) => {
1432
+ const write = async ({ type, body }) => {
1433
+ var _a;
1413
1434
  const { sessionId, from, username } = conversation;
1414
1435
  const data = `data: ${JSON.stringify({ sessionId, from, username, type, body })}
1415
1436
 
1416
1437
  `;
1438
+ await ((_a = this.onWrite) == null ? void 0 : _a.call(this, data));
1417
1439
  this.streamConsumer.write(data);
1418
1440
  this._statistics.addSent(data.length);
1419
1441
  };
1420
1442
  return {
1421
- startStream: () => {
1443
+ startStream: async () => {
1422
1444
  this._statistics.reset();
1423
- write({ type: "stream_start" });
1445
+ await write({ type: "stream_start" });
1424
1446
  },
1425
- endStream: () => {
1426
- write({ type: "stream_end" });
1447
+ endStream: async () => {
1448
+ await write({ type: "stream_end" });
1427
1449
  },
1428
- subAgentCompleted: () => {
1429
- write({ type: "sub_agent_completed" });
1450
+ subAgentCompleted: async () => {
1451
+ await write({ type: "sub_agent_completed" });
1430
1452
  },
1431
- newMessage: (content) => {
1432
- write({ type: "new_message", body: content });
1453
+ newMessage: async (content) => {
1454
+ await write({ type: "new_message", body: content });
1433
1455
  },
1434
- content: (content) => {
1435
- write({ type: "content", body: content });
1456
+ content: async (content) => {
1457
+ await write({ type: "content", body: content });
1436
1458
  },
1437
- webSearch: (content) => {
1438
- write({ type: "web_search", body: content });
1459
+ webSearch: async (content) => {
1460
+ await write({ type: "web_search", body: content });
1439
1461
  },
1440
- reasoning: (content) => {
1441
- write({ type: "reasoning", body: content });
1462
+ reasoning: async (content) => {
1463
+ await write({ type: "reasoning", body: content });
1442
1464
  },
1443
- stopReasoning: () => {
1444
- write({
1465
+ stopReasoning: async () => {
1466
+ await write({
1445
1467
  type: "reasoning",
1446
1468
  body: {
1447
1469
  status: "stop",
@@ -1449,13 +1471,13 @@ class ChatStreamProtocol {
1449
1471
  }
1450
1472
  });
1451
1473
  },
1452
- toolCallChunks: (content) => {
1453
- write({ type: "tool_call_chunks", body: content });
1474
+ toolCallChunks: async (content) => {
1475
+ await write({ type: "tool_call_chunks", body: content });
1454
1476
  },
1455
- toolCalls: (content) => {
1456
- write({ type: "tool_calls", body: content });
1477
+ toolCalls: async (content) => {
1478
+ await write({ type: "tool_calls", body: content });
1457
1479
  },
1458
- toolCallStatus: ({
1480
+ toolCallStatus: async ({
1459
1481
  toolCall,
1460
1482
  invokeStatus,
1461
1483
  status,
@@ -1464,7 +1486,7 @@ class ChatStreamProtocol {
1464
1486
  content,
1465
1487
  interruptAction
1466
1488
  }) => {
1467
- write({
1489
+ await write({
1468
1490
  type: "tool_call_status",
1469
1491
  body: {
1470
1492
  toolCall,
@@ -109,6 +109,9 @@ const conversationMiddleware = (aiEmployee, options) => {
109
109
  }
110
110
  });
111
111
  },
112
+ afterAgent: async () => {
113
+ aiEmployee.removeAbortController();
114
+ },
112
115
  beforeModel: async (state, runtime) => {
113
116
  var _a;
114
117
  const { messageId: messageId2 } = state;
@@ -159,7 +162,6 @@ const conversationMiddleware = (aiEmployee, options) => {
159
162
  if ((lastMessage == null ? void 0 : lastMessage.type) !== "ai") {
160
163
  return newState;
161
164
  }
162
- aiEmployee.removeAbortController();
163
165
  if ((_a = runtime.signal) == null ? void 0 : _a.aborted) {
164
166
  return newState;
165
167
  }
@@ -180,19 +182,19 @@ const conversationMiddleware = (aiEmployee, options) => {
180
182
  fillToolCall(result, toolsMap, initializedToolCalls, toolCalls);
181
183
  }
182
184
  });
183
- (_b = runtime.writer) == null ? void 0 : _b.call(runtime, {
185
+ if (toolCalls == null ? void 0 : toolCalls.length) {
186
+ (_b = runtime.writer) == null ? void 0 : _b.call(runtime, {
187
+ action: "initToolCalls",
188
+ body: { toolCalls },
189
+ currentConversation
190
+ });
191
+ }
192
+ (_c = runtime.writer) == null ? void 0 : _c.call(runtime, {
184
193
  action: "AfterAIMessageSaved",
185
194
  body: { id: aiMessage.id, messageId: newState.messageId },
186
195
  currentConversation
187
196
  });
188
197
  }
189
- if (toolCalls == null ? void 0 : toolCalls.length) {
190
- (_c = runtime.writer) == null ? void 0 : _c.call(runtime, {
191
- action: "initToolCalls",
192
- body: { toolCalls },
193
- currentConversation
194
- });
195
- }
196
198
  return newState;
197
199
  } catch (e) {
198
200
  (_f = (_e = (_d = runtime.context) == null ? void 0 : _d.ctx) == null ? void 0 : _e.logger) == null ? void 0 : _f.error(e);
@@ -97,6 +97,12 @@ var ai_conversations_default = (0, import_database.defineCollection)({
97
97
  type: "string",
98
98
  // chat, task
99
99
  defaultValue: "chat"
100
+ },
101
+ {
102
+ name: "read",
103
+ type: "boolean",
104
+ allowNull: false,
105
+ defaultValue: true
100
106
  }
101
107
  ]
102
108
  });
@@ -0,0 +1,37 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import PluginAIServer from '../plugin';
10
+ export declare class LLMStreamCachedManager {
11
+ private readonly plugin;
12
+ private cachePromise?;
13
+ constructor(plugin: PluginAIServer);
14
+ getCached(sessionId: string): LLMStreamCached;
15
+ clear(sessionId: string): Promise<void>;
16
+ append(sessionId: string, chunk: string): Promise<void>;
17
+ stream(sessionId: string, options?: {
18
+ pollInterval?: number;
19
+ initialWaitTimeout?: number;
20
+ }): AsyncGenerator<string, void, void>;
21
+ private getChunks;
22
+ private getCache;
23
+ private withLock;
24
+ private getLockKey;
25
+ }
26
+ export declare class LLMStreamCached {
27
+ private readonly sessionId;
28
+ private readonly manager;
29
+ constructor(sessionId: string, manager: LLMStreamCachedManager);
30
+ clear(): Promise<void>;
31
+ append(chunk: string): Promise<void>;
32
+ skipped(): Promise<void>;
33
+ stream(options?: {
34
+ pollInterval?: number;
35
+ initialWaitTimeout?: number;
36
+ }): AsyncGenerator<string, void, void>;
37
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var llm_stream_manager_exports = {};
28
+ __export(llm_stream_manager_exports, {
29
+ LLMStreamCached: () => LLMStreamCached,
30
+ LLMStreamCachedManager: () => LLMStreamCachedManager
31
+ });
32
+ module.exports = __toCommonJS(llm_stream_manager_exports);
33
+ var import_utils = require("@nocobase/utils");
34
+ const CACHE_NAME = "ai-llm-stream-cache";
35
+ const LOCK_KEY_PREFIX = "ai-llm-stream-lock";
36
+ const WRITE_LOCK_TTL = 3e3;
37
+ const CACHE_TTL = 10 * 60 * 1e3;
38
+ const STREAM_END_MARK = '"type":"stream_end"';
39
+ const SKIPPED_MARK = "__skipped__";
40
+ const DEFAULT_POLL_INTERVAL = 50;
41
+ const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e3;
42
+ class LLMStreamCachedManager {
43
+ constructor(plugin) {
44
+ this.plugin = plugin;
45
+ }
46
+ cachePromise;
47
+ getCached(sessionId) {
48
+ return new LLMStreamCached(sessionId, this);
49
+ }
50
+ async clear(sessionId) {
51
+ await this.withLock(sessionId, async () => {
52
+ const cache = await this.getCache();
53
+ await cache.del(sessionId);
54
+ });
55
+ }
56
+ async append(sessionId, chunk) {
57
+ await this.withLock(sessionId, async () => {
58
+ const cache = await this.getCache();
59
+ const chunks = await cache.get(sessionId) ?? [];
60
+ chunks.push(chunk);
61
+ await cache.set(sessionId, chunks, CACHE_TTL);
62
+ });
63
+ }
64
+ async *stream(sessionId, options) {
65
+ const pollInterval = (options == null ? void 0 : options.pollInterval) ?? DEFAULT_POLL_INTERVAL;
66
+ const initialWaitTimeout = (options == null ? void 0 : options.initialWaitTimeout) ?? DEFAULT_INITIAL_WAIT_TIMEOUT;
67
+ let offset = 0;
68
+ let waited = 0;
69
+ let completed = false;
70
+ while (!completed) {
71
+ const chunks = await this.getChunks(sessionId);
72
+ const lastSkippedIndex = chunks.lastIndexOf(SKIPPED_MARK);
73
+ if (lastSkippedIndex >= offset) {
74
+ offset = lastSkippedIndex + 1;
75
+ }
76
+ while (offset < chunks.length) {
77
+ const chunk = chunks[offset++];
78
+ yield chunk;
79
+ waited = 0;
80
+ if (chunk.includes(STREAM_END_MARK)) {
81
+ completed = true;
82
+ break;
83
+ }
84
+ }
85
+ if (completed) {
86
+ return;
87
+ }
88
+ if (!chunks.length) {
89
+ if (offset > 0) {
90
+ return;
91
+ }
92
+ if (waited >= initialWaitTimeout) {
93
+ return;
94
+ }
95
+ waited += pollInterval;
96
+ }
97
+ await (0, import_utils.sleep)(pollInterval);
98
+ }
99
+ }
100
+ async getChunks(sessionId) {
101
+ const cache = await this.getCache();
102
+ return await cache.get(sessionId) ?? [];
103
+ }
104
+ async getCache() {
105
+ this.cachePromise ??= this.plugin.app.cacheManager.createCache({
106
+ name: CACHE_NAME,
107
+ store: this.plugin.app.cacheManager.defaultStore
108
+ });
109
+ return this.cachePromise;
110
+ }
111
+ async withLock(sessionId, fn) {
112
+ return await this.plugin.app.lockManager.runExclusive(this.getLockKey(sessionId), fn, WRITE_LOCK_TTL);
113
+ }
114
+ getLockKey(sessionId) {
115
+ return `${LOCK_KEY_PREFIX}:${sessionId}`;
116
+ }
117
+ }
118
+ class LLMStreamCached {
119
+ constructor(sessionId, manager) {
120
+ this.sessionId = sessionId;
121
+ this.manager = manager;
122
+ }
123
+ async clear() {
124
+ await this.manager.clear(this.sessionId);
125
+ }
126
+ async append(chunk) {
127
+ await this.manager.append(this.sessionId, chunk);
128
+ }
129
+ async skipped() {
130
+ await this.append(SKIPPED_MARK);
131
+ }
132
+ async *stream(options) {
133
+ for await (const chunk of this.manager.stream(this.sessionId, options)) {
134
+ yield chunk;
135
+ }
136
+ }
137
+ }
138
+ // Annotate the CommonJS export names for ESM import in node:
139
+ 0 && (module.exports = {
140
+ LLMStreamCached,
141
+ LLMStreamCachedManager
142
+ });
@@ -20,11 +20,13 @@ import { DocumentLoaders } from './document-loader';
20
20
  import type PluginFileManagerServer from '@nocobase/plugin-file-manager';
21
21
  import { SubAgentsDispatcher } from './ai-employees/sub-agents';
22
22
  import { KnowledgeBaseManager } from './ai-employees/ai-knowledge-base';
23
+ import { LLMStreamCachedManager } from './manager/llm-stream-manager';
23
24
  export declare class PluginAIServer extends Plugin {
24
25
  features: AIPluginFeatureManagerImpl;
25
26
  aiManager: AIManager;
26
27
  aiEmployeesManager: AIEmployeesManager;
27
28
  aiConversationsManager: AIConversationsManager;
29
+ llmStreamCachedManager: LLMStreamCachedManager;
28
30
  builtInManager: BuiltInManager;
29
31
  aiContextDatasourceManager: AIContextDatasourceManager;
30
32
  aiCodingManager: AICodingManager;
@@ -77,11 +77,13 @@ var import_mimo = require("./llm-providers/mimo");
77
77
  var import_sub_agents = require("./ai-employees/sub-agents");
78
78
  var import_employee = require("./workflow/nodes/employee");
79
79
  var import_ai_knowledge_base = require("./ai-employees/ai-knowledge-base");
80
+ var import_llm_stream_manager = require("./manager/llm-stream-manager");
80
81
  class PluginAIServer extends import_server.Plugin {
81
82
  features = new import_ai_feature_manager.AIPluginFeatureManagerImpl();
82
83
  aiManager = new import_ai_manager.AIManager(this);
83
84
  aiEmployeesManager = new import_ai_employees_manager.AIEmployeesManager(this);
84
85
  aiConversationsManager = new import_ai_conversations.AIConversationsManager(this);
86
+ llmStreamCachedManager = new import_llm_stream_manager.LLMStreamCachedManager(this);
85
87
  builtInManager = new import_built_in_manager.BuiltInManager(this);
86
88
  aiContextDatasourceManager = new import_ai_context_datasource_manager.AIContextDatasourceManager(this);
87
89
  aiCodingManager = new import_ai_coding_manager.AICodingManager(this);
@@ -135,6 +137,7 @@ class PluginAIServer extends import_server.Plugin {
135
137
  this.registerWorkflow();
136
138
  this.registerWorkContextResolveStrategy();
137
139
  (0, import_employee.registerAIEmployeeTaskNotification)(this);
140
+ (0, import_ai_conversations.registerAIConversationReadNotification)(this);
138
141
  (0, import_employee.registerOnJobAbortedHandler)(this);
139
142
  }
140
143
  registerLLMProviders() {
@@ -7,10 +7,17 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { Context, Next } from '@nocobase/actions';
10
+ declare function parallelConversationsLimit(ctx: Context, next: Next): Promise<never>;
10
11
  declare const _default: {
11
12
  name: string;
13
+ middlewares: {
14
+ only: string[];
15
+ handler: typeof parallelConversationsLimit;
16
+ }[];
12
17
  actions: {
13
18
  list(ctx: Context, next: Next): Promise<void>;
19
+ unreadCount(ctx: Context, next: Next): Promise<never>;
20
+ unreadCounts(ctx: Context, next: Next): Promise<never>;
14
21
  create(ctx: Context, next: Next): Promise<never>;
15
22
  update(ctx: Context, next: Next): Promise<never>;
16
23
  updateOptions(ctx: Context, next: Next): Promise<never>;
@@ -19,6 +26,7 @@ declare const _default: {
19
26
  updateToolArgs(ctx: Context, next: Next): Promise<any>;
20
27
  sendMessages(ctx: Context, next: Next): Promise<any>;
21
28
  abort(ctx: Context, next: Next): Promise<never>;
29
+ resumeStream(ctx: Context, next: Next): Promise<never>;
22
30
  resendMessages(ctx: Context, next: Next): Promise<any>;
23
31
  updateUserDecision(ctx: Context, next: Next): Promise<never>;
24
32
  resumeToolCall(ctx: Context, next: Next): Promise<any>;