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

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 (101) hide show
  1. package/client-v2.d.ts +1 -0
  2. package/client-v2.js +1 -0
  3. package/dist/ai/docs/nocobase/ai/index.md +1 -1
  4. package/dist/ai/docs/nocobase/ai-employees/built-in/atlas.md +24 -0
  5. package/dist/ai/docs/nocobase/ai-employees/built-in/dara.md +22 -0
  6. package/dist/ai/docs/nocobase/ai-employees/built-in/dex.md +32 -0
  7. package/dist/ai/docs/nocobase/ai-employees/built-in/ellis.md +22 -0
  8. package/dist/ai/docs/nocobase/ai-employees/built-in/index.md +25 -0
  9. package/dist/ai/docs/nocobase/ai-employees/built-in/lexi.md +26 -0
  10. package/dist/ai/docs/nocobase/ai-employees/built-in/lina.md +142 -0
  11. package/dist/ai/docs/nocobase/ai-employees/built-in/nathan.md +36 -0
  12. package/dist/ai/docs/nocobase/ai-employees/built-in/vera.md +22 -0
  13. package/dist/ai/docs/nocobase/ai-employees/built-in/viz.md +25 -0
  14. package/dist/ai/docs/nocobase/ai-employees/features/built-in-employee.md +1 -29
  15. package/dist/ai/docs/nocobase/ai-employees/features/collaborate.md +17 -7
  16. package/dist/ai/docs/nocobase/ai-employees/features/enable-ai-employee.md +4 -4
  17. package/dist/ai/docs/nocobase/ai-employees/features/model-settings.md +87 -0
  18. package/dist/ai/docs/nocobase/ai-employees/index.md +1 -1
  19. package/dist/ai/docs/nocobase/ai-employees/scenarios/localization-hy-mt.md +241 -0
  20. package/dist/ai/docs/nocobase/ai-employees/workflow/nodes/employee/configuration.md +1 -1
  21. package/dist/ai/docs/nocobase/cluster-mode/index.md +5 -1
  22. package/dist/ai/docs/nocobase/cluster-mode/preparations.md +58 -3
  23. package/dist/ai/docs/nocobase/interface-builder/actions/types/js-action.md +1 -1
  24. package/dist/ai/docs/nocobase/interface-builder/actions/types/js-item.md +1 -1
  25. package/dist/ai/docs/nocobase/interface-builder/blocks/other-blocks/js-block.md +1 -1
  26. package/dist/ai/docs/nocobase/interface-builder/fields/specific/js-column.md +1 -1
  27. package/dist/ai/docs/nocobase/interface-builder/fields/specific/js-field.md +1 -1
  28. package/dist/ai/docs/nocobase/interface-builder/fields/specific/js-item.md +1 -1
  29. package/dist/ai/docs/nocobase/security/guide.md +13 -1
  30. package/dist/ai/docs/nocobase/system-management/localization/index.md +25 -1
  31. package/dist/client/462.1708385b148779cd.js +10 -0
  32. package/dist/client/{559.39872901b9053629.js → 559.585f80c3bcea0bed.js} +1 -1
  33. package/dist/client/646.b0ed728921b007d4.js +10 -0
  34. package/dist/client/{927.ac9ee9a8c1cb4f1d.js → 927.d95c74ebb8fd51c9.js} +1 -1
  35. package/dist/client/ai-employees/admin/ModelSettings.d.ts +10 -0
  36. package/dist/client/ai-employees/admin/hooks.d.ts +1 -1
  37. package/dist/client/ai-employees/avatars.d.ts +9 -783
  38. package/dist/client/ai-employees/chatbox/model.d.ts +6 -3
  39. package/dist/client/ai-employees/types.d.ts +23 -0
  40. package/dist/client/features/vector-database-provider.d.ts +1 -1
  41. package/dist/client/index.js +4 -4
  42. package/dist/client/llm-services/component/EnabledModelsSelect.d.ts +1 -14
  43. package/dist/client-v2/ai-employees/AIEmployeeShortcut.d.ts +21 -0
  44. package/dist/client-v2/ai-employees/ProfileCard.d.ts +17 -0
  45. package/dist/client-v2/ai-employees/avatars.d.ts +783 -0
  46. package/dist/client-v2/ai-employees/types.d.ts +20 -0
  47. package/dist/client-v2/index.d.ts +17 -0
  48. package/dist/client-v2/index.js +10 -0
  49. package/dist/client-v2/llm-services/model-label.d.ts +22 -0
  50. package/dist/collections/ai-employees.d.ts +2 -1
  51. package/dist/collections/ai-employees.js +1 -1
  52. package/dist/externalVersion.js +17 -17
  53. package/dist/locale/en-US.json +15 -1
  54. package/dist/locale/zh-CN.json +15 -1
  55. package/dist/node_modules/@langchain/xai/package.json +1 -1
  56. package/dist/node_modules/fs-extra/package.json +1 -1
  57. package/dist/node_modules/jsonrepair/package.json +1 -1
  58. package/dist/node_modules/just-bash/package.json +1 -1
  59. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  60. package/dist/node_modules/openai/package.json +1 -1
  61. package/dist/node_modules/zod/package.json +1 -1
  62. package/dist/server/ai-employees/ai-conversations.d.ts +1 -0
  63. package/dist/server/ai-employees/ai-conversations.js +4 -1
  64. package/dist/server/ai-employees/ai-employee.d.ts +15 -1
  65. package/dist/server/ai-employees/ai-employee.js +140 -9
  66. package/dist/server/ai-employees/ai-employees-manager.d.ts +4 -0
  67. package/dist/server/ai-employees/ai-employees-manager.js +41 -0
  68. package/dist/server/ai-employees/ai-knowledge-base.js +7 -7
  69. package/dist/server/ai-employees/middleware/conversation.d.ts +1 -0
  70. package/dist/server/ai-employees/middleware/conversation.js +4 -2
  71. package/dist/server/ai-employees/sub-agents/dispatcher.js +2 -4
  72. package/dist/server/ai-employees/utils.d.ts +6 -3
  73. package/dist/server/ai-employees/utils.js +7 -1
  74. package/dist/server/features/knowledge-base.d.ts +3 -2
  75. package/dist/server/index.d.ts +2 -0
  76. package/dist/server/index.js +3 -0
  77. package/dist/server/llm-providers/common/reasoning.d.ts +2 -0
  78. package/dist/server/llm-providers/common/reasoning.js +15 -2
  79. package/dist/server/llm-providers/dashscope.d.ts +2 -1
  80. package/dist/server/llm-providers/dashscope.js +39 -0
  81. package/dist/server/llm-providers/deepseek.js +2 -0
  82. package/dist/server/llm-providers/provider.d.ts +15 -1
  83. package/dist/server/llm-providers/provider.js +21 -2
  84. package/dist/server/manager/ai-chat-conversation.js +3 -4
  85. package/dist/server/manager/ai-manager.d.ts +17 -0
  86. package/dist/server/manager/ai-manager.js +65 -0
  87. package/dist/server/manager/llm-stream-manager.d.ts +16 -10
  88. package/dist/server/manager/llm-stream-manager.js +121 -27
  89. package/dist/server/migrations/20260407170416-ai-employee-knowledge-base-add-key.d.ts +14 -0
  90. package/dist/server/migrations/20260407170416-ai-employee-knowledge-base-add-key.js +61 -0
  91. package/dist/server/resource/ai.js +1 -41
  92. package/dist/server/resource/aiConversations.d.ts +12 -13
  93. package/dist/server/resource/aiConversations.js +129 -121
  94. package/dist/server/resource/aiEmployees.js +32 -1
  95. package/dist/server/types/knowledge-base.type.d.ts +3 -2
  96. package/dist/server/utils.d.ts +4 -0
  97. package/dist/server/utils.js +9 -0
  98. package/dist/server/workflow/nodes/employee/index.js +4 -2
  99. package/package.json +2 -2
  100. package/dist/client/343.6f36d97dd122c5b6.js +0 -10
  101. package/dist/client/646.5860101cb28c8272.js +0 -10
@@ -11,6 +11,8 @@ export { LLMProvider, LLMProviderOptions } from './llm-providers/provider';
11
11
  export { LLMProviderMeta } from './manager/ai-manager';
12
12
  export { ToolOptions } from './manager/tool-manager';
13
13
  export { DocumentLoader } from './document-loader';
14
+ export { AIEmployee } from './ai-employees/ai-employee';
15
+ export type { AIEmployeeOptions, ModelRef } from './ai-employees/ai-employee';
14
16
  export type { ParsedDocumentResult, ParseableFile, DocumentParseMeta } from './document-loader';
15
17
  export type * from './features';
16
18
  export type * from './types';
@@ -36,6 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var server_exports = {};
38
38
  __export(server_exports, {
39
+ AIEmployee: () => import_ai_employee.AIEmployee,
39
40
  DocumentLoader: () => import_document_loader.DocumentLoader,
40
41
  LLMProvider: () => import_provider.LLMProvider,
41
42
  LLMProviderMeta: () => import_ai_manager.LLMProviderMeta,
@@ -49,8 +50,10 @@ var import_provider = require("./llm-providers/provider");
49
50
  var import_ai_manager = require("./manager/ai-manager");
50
51
  var import_tool_manager = require("./manager/tool-manager");
51
52
  var import_document_loader = require("./document-loader");
53
+ var import_ai_employee = require("./ai-employees/ai-employee");
52
54
  // Annotate the CommonJS export names for ESM import in node:
53
55
  0 && (module.exports = {
56
+ AIEmployee,
54
57
  DocumentLoader,
55
58
  LLMProvider,
56
59
  LLMProviderMeta,
@@ -10,8 +10,10 @@ import { BaseMessage } from '@langchain/core/messages';
10
10
  import { ChatOpenAICompletions } from '@langchain/openai';
11
11
  import type OpenAI from 'openai';
12
12
  export declare const REASONING_MAP_KEY = "__nb_reasoning_map";
13
+ export declare const MODEL_KWARGS_KEY = "__nb_model_kwargs";
13
14
  export declare const collectReasoningMap: (messages: BaseMessage[]) => Map<string, string>;
14
15
  export declare const patchRequestMessagesReasoning: (request: any, reasoningMap?: Map<string, string>) => void;
16
+ export declare const patchRequestModelKwargs: (request: any, modelKwargs?: Record<string, any>) => void;
15
17
  export declare class ReasoningChatOpenAI extends ChatOpenAICompletions {
16
18
  _generate(messages: BaseMessage[], options: any, runManager?: any): Promise<import("@langchain/core/dist/outputs").ChatResult>;
17
19
  _streamResponseChunks(messages: BaseMessage[], options: any, runManager?: any): AsyncGenerator<import("@langchain/core/dist/outputs").ChatGenerationChunk, void, unknown>;
@@ -26,15 +26,18 @@ var __copyProps = (to, from, except, desc) => {
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
  var reasoning_exports = {};
28
28
  __export(reasoning_exports, {
29
+ MODEL_KWARGS_KEY: () => MODEL_KWARGS_KEY,
29
30
  REASONING_MAP_KEY: () => REASONING_MAP_KEY,
30
31
  ReasoningChatOpenAI: () => ReasoningChatOpenAI,
31
32
  collectReasoningMap: () => collectReasoningMap,
32
- patchRequestMessagesReasoning: () => patchRequestMessagesReasoning
33
+ patchRequestMessagesReasoning: () => patchRequestMessagesReasoning,
34
+ patchRequestModelKwargs: () => patchRequestModelKwargs
33
35
  });
34
36
  module.exports = __toCommonJS(reasoning_exports);
35
37
  var import_messages = require("@langchain/core/messages");
36
38
  var import_openai = require("@langchain/openai");
37
39
  const REASONING_MAP_KEY = "__nb_reasoning_map";
40
+ const MODEL_KWARGS_KEY = "__nb_model_kwargs";
38
41
  const collectReasoningMap = (messages) => {
39
42
  var _a;
40
43
  const reasoningMap = /* @__PURE__ */ new Map();
@@ -69,6 +72,12 @@ const patchRequestMessagesReasoning = (request, reasoningMap) => {
69
72
  }
70
73
  }
71
74
  };
75
+ const patchRequestModelKwargs = (request, modelKwargs) => {
76
+ if (!modelKwargs || typeof modelKwargs !== "object") {
77
+ return;
78
+ }
79
+ Object.assign(request, modelKwargs);
80
+ };
72
81
  class ReasoningChatOpenAI extends import_openai.ChatOpenAICompletions {
73
82
  async _generate(messages, options, runManager) {
74
83
  const reasoningMap = collectReasoningMap(messages);
@@ -108,7 +117,9 @@ class ReasoningChatOpenAI extends import_openai.ChatOpenAICompletions {
108
117
  }
109
118
  async completionWithRetry(request, requestOptions) {
110
119
  const reasoningMap = requestOptions == null ? void 0 : requestOptions[REASONING_MAP_KEY];
120
+ const modelKwargs = requestOptions == null ? void 0 : requestOptions[MODEL_KWARGS_KEY];
111
121
  patchRequestMessagesReasoning(request, reasoningMap);
122
+ patchRequestModelKwargs(request, modelKwargs);
112
123
  if (request.stream) {
113
124
  return super.completionWithRetry(request, requestOptions);
114
125
  }
@@ -117,8 +128,10 @@ class ReasoningChatOpenAI extends import_openai.ChatOpenAICompletions {
117
128
  }
118
129
  // Annotate the CommonJS export names for ESM import in node:
119
130
  0 && (module.exports = {
131
+ MODEL_KWARGS_KEY,
120
132
  REASONING_MAP_KEY,
121
133
  ReasoningChatOpenAI,
122
134
  collectReasoningMap,
123
- patchRequestMessagesReasoning
135
+ patchRequestMessagesReasoning,
136
+ patchRequestModelKwargs
124
137
  });
@@ -7,7 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { AIMessageChunk } from '@langchain/core/messages';
10
- import { EmbeddingProvider, LLMProvider } from './provider';
10
+ import { EmbeddingProvider, LLMModelRequestBuilder, LLMProvider } from './provider';
11
11
  import { EmbeddingsInterface } from '@langchain/core/embeddings';
12
12
  import { SupportedModel } from '../manager/ai-manager';
13
13
  import { Model } from '@nocobase/database';
@@ -19,6 +19,7 @@ export declare class DashscopeProvider extends LLMProvider {
19
19
  createModel(): ReasoningChatOpenAI;
20
20
  isToolConflict(): boolean;
21
21
  resolveTools(toolDefinitions: any[]): any[];
22
+ protected getModelRequestBuilder(model?: string): LLMModelRequestBuilder | null;
22
23
  parseResponseMessage(message: Model): {
23
24
  key: any;
24
25
  createdAt: any;
@@ -41,6 +41,7 @@ __export(dashscope_exports, {
41
41
  dashscopeProviderOptions: () => dashscopeProviderOptions
42
42
  });
43
43
  module.exports = __toCommonJS(dashscope_exports);
44
+ var import_messages = require("@langchain/core/messages");
44
45
  var import_openai = require("@langchain/openai");
45
46
  var import_provider = require("./provider");
46
47
  var import_ai_manager = require("../manager/ai-manager");
@@ -94,6 +95,44 @@ class DashscopeProvider extends import_provider.LLMProvider {
94
95
  return toolDefinitions;
95
96
  }
96
97
  }
98
+ getModelRequestBuilder(model) {
99
+ if (!/^qwen-mt-/i.test(model || "")) {
100
+ return null;
101
+ }
102
+ return ({ context, options }) => {
103
+ var _a, _b;
104
+ const { sourceText, sourceLang, targetLang, terms } = (options == null ? void 0 : options.modelRequestParams) || {};
105
+ if (!sourceText || !sourceLang || !targetLang) {
106
+ return {
107
+ context,
108
+ options
109
+ };
110
+ }
111
+ (_b = this.app.logger) == null ? void 0 : _b.debug("Dashscope Qwen-MT model request builder matched", {
112
+ model: (_a = this.modelOptions) == null ? void 0 : _a.model,
113
+ sourceTextLength: (sourceText == null ? void 0 : sourceText.length) ?? 0,
114
+ sourceLang,
115
+ targetLang,
116
+ terms: Array.isArray(terms) ? terms.length : 0
117
+ });
118
+ return {
119
+ context: {
120
+ messages: [new import_messages.HumanMessage(sourceText)]
121
+ },
122
+ options: {
123
+ ...options,
124
+ modelKwargs: {
125
+ ...(options == null ? void 0 : options.modelKwargs) || {},
126
+ translation_options: {
127
+ source_lang: sourceLang,
128
+ target_lang: targetLang,
129
+ ...Array.isArray(terms) && terms.length ? { terms } : {}
130
+ }
131
+ }
132
+ }
133
+ };
134
+ };
135
+ }
97
136
  parseResponseMessage(message) {
98
137
  var _a;
99
138
  const result = super.parseResponseMessage(message);
@@ -64,7 +64,9 @@ class ReasoningDeepSeek extends import_deepseek.ChatDeepSeek {
64
64
  }
65
65
  async completionWithRetry(request, requestOptions) {
66
66
  const reasoningMap = requestOptions == null ? void 0 : requestOptions[import_reasoning.REASONING_MAP_KEY];
67
+ const modelKwargs = requestOptions == null ? void 0 : requestOptions[import_reasoning.MODEL_KWARGS_KEY];
67
68
  (0, import_reasoning.patchRequestMessagesReasoning)(request, reasoningMap);
69
+ (0, import_reasoning.patchRequestModelKwargs)(request, modelKwargs);
68
70
  if (request.stream) {
69
71
  return super.completionWithRetry(request, requestOptions);
70
72
  }
@@ -22,6 +22,19 @@ export type ParsedAttachmentResult = {
22
22
  placement: string;
23
23
  content: any;
24
24
  };
25
+ export type LLMProviderInvokeOptions = {
26
+ modelKwargs?: Record<string, any>;
27
+ modelRequestParams?: Record<string, any>;
28
+ [key: string]: any;
29
+ };
30
+ export type LLMModelRequestBuilderResult = {
31
+ context: AIChatContext;
32
+ options?: LLMProviderInvokeOptions;
33
+ };
34
+ export type LLMModelRequestBuilder = (input: {
35
+ context: AIChatContext;
36
+ options?: LLMProviderInvokeOptions;
37
+ }) => LLMModelRequestBuilderResult;
25
38
  export interface LLMProviderOptions {
26
39
  app: Application;
27
40
  serviceOptions?: Record<string, any>;
@@ -35,8 +48,9 @@ export declare abstract class LLMProvider {
35
48
  abstract createModel(): BaseChatModel | any;
36
49
  get baseURL(): string | null;
37
50
  constructor(opts: LLMProviderOptions);
51
+ protected getModelRequestBuilder(_model?: string): LLMModelRequestBuilder | null;
38
52
  prepareChain(context: AIChatContext): any;
39
- invoke(context: AIChatContext, options?: any): Promise<any>;
53
+ invoke(context: AIChatContext, options?: LLMProviderInvokeOptions): Promise<any>;
40
54
  stream(context: AIChatContext, options?: any): Promise<any>;
41
55
  listModels(): Promise<{
42
56
  models?: {
@@ -45,6 +45,7 @@ var import_utils = require("../utils");
45
45
  var import_stream = require("@langchain/core/utils/stream");
46
46
  var import_document_loader = require("../document-loader");
47
47
  var import_node_path = __toESM(require("node:path"));
48
+ var import_reasoning = require("./common/reasoning");
48
49
  class LLMProvider {
49
50
  app;
50
51
  serviceOptions;
@@ -62,6 +63,9 @@ class LLMProvider {
62
63
  this.chatModel = this.createModel();
63
64
  }
64
65
  }
66
+ getModelRequestBuilder(_model) {
67
+ return null;
68
+ }
65
69
  prepareChain(context) {
66
70
  var _a, _b, _c, _d;
67
71
  let chain = this.chatModel;
@@ -84,8 +88,23 @@ class LLMProvider {
84
88
  return chain;
85
89
  }
86
90
  async invoke(context, options) {
87
- const chain = this.prepareChain(context);
88
- return chain.invoke(context.messages, options);
91
+ var _a;
92
+ const builder = this.getModelRequestBuilder((_a = this.modelOptions) == null ? void 0 : _a.model);
93
+ const request = (builder == null ? void 0 : builder({ context, options })) || { context, options };
94
+ const chain = this.prepareChain(request.context);
95
+ const { modelKwargs, modelRequestParams, options: requestOptions, ...restOptions } = request.options || {};
96
+ const invokeOptions = modelKwargs ? {
97
+ ...restOptions,
98
+ [import_reasoning.MODEL_KWARGS_KEY]: modelKwargs,
99
+ options: {
100
+ ...requestOptions || {},
101
+ [import_reasoning.MODEL_KWARGS_KEY]: modelKwargs
102
+ }
103
+ } : {
104
+ ...restOptions,
105
+ ...requestOptions ? { options: requestOptions } : {}
106
+ };
107
+ return chain.invoke(request.context.messages, invokeOptions);
89
108
  }
90
109
  async stream(context, options) {
91
110
  const chain = this.prepareChain(context);
@@ -138,11 +138,10 @@ class AIChatConversationImpl {
138
138
  formatMessages
139
139
  } = options ?? {};
140
140
  let messages = userMessages ? await (formatMessages == null ? void 0 : formatMessages(userMessages)) ?? [] : void 0;
141
- const additionSystemPrompt = messages == null ? void 0 : messages.filter((it) => it.role === "system").map((it) => it.content).join("\n");
141
+ const additionSystemPrompt = messages == null ? void 0 : messages.filter((it) => it.role === "system").map((it) => it.content).filter(Boolean).join("\n");
142
142
  messages = messages == null ? void 0 : messages.filter((it) => it.role !== "system");
143
- const systemPrompt = `${await (getSystemPrompt == null ? void 0 : getSystemPrompt(userMessages ?? [])) ?? ""}
144
-
145
- ${additionSystemPrompt}`;
143
+ const baseSystemPrompt = await (getSystemPrompt == null ? void 0 : getSystemPrompt(userMessages ?? []));
144
+ const systemPrompt = [baseSystemPrompt, additionSystemPrompt].filter(Boolean).join("\n\n") || void 0;
146
145
  const chatContext = {
147
146
  systemPrompt,
148
147
  messages,
@@ -26,6 +26,19 @@ export type LLMModelOptions = {
26
26
  model: string;
27
27
  webSearch?: boolean;
28
28
  };
29
+ export type EnabledLLMModel = {
30
+ label: string;
31
+ value: string;
32
+ };
33
+ export type EnabledLLMService = {
34
+ llmService: string;
35
+ llmServiceTitle: string;
36
+ provider: string;
37
+ providerTitle?: string;
38
+ enabledModels: EnabledLLMModel[];
39
+ supportWebSearch: boolean;
40
+ isToolConflict: boolean;
41
+ };
29
42
  export declare class AIManager {
30
43
  protected plugin: PluginAIServer;
31
44
  llmProviders: Map<string, LLMProviderMeta>;
@@ -39,6 +52,10 @@ export declare class AIManager {
39
52
  supportWebSearch: boolean;
40
53
  }[];
41
54
  getSupportedProvider(model: SupportedModel): string[];
55
+ listAllEnabledModels(): Promise<EnabledLLMService[]>;
56
+ resolveModel(model?: LLMModelOptions | null): Promise<LLMModelOptions>;
57
+ private toEnabledLLMService;
58
+ private getEnabledModels;
42
59
  getLLMService(options: LLMModelOptions): Promise<{
43
60
  provider: LLMProvider;
44
61
  model: string;
@@ -31,6 +31,7 @@ __export(ai_manager_exports, {
31
31
  });
32
32
  module.exports = __toCommonJS(ai_manager_exports);
33
33
  var import_tool_manager = require("./tool-manager");
34
+ var import_recommended_models = require("../../common/recommended-models");
34
35
  var SupportedModel = /* @__PURE__ */ ((SupportedModel2) => {
35
36
  SupportedModel2["LLM"] = "LLM";
36
37
  SupportedModel2["EMBEDDING"] = "EMBEDDING";
@@ -57,6 +58,70 @@ class AIManager {
57
58
  getSupportedProvider(model) {
58
59
  return Array.from(this.llmProviders.entries()).filter(([_2, { supportedModel }]) => supportedModel && supportedModel.includes(model)).map(([name]) => name);
59
60
  }
61
+ async listAllEnabledModels() {
62
+ const services = await this.plugin.db.getRepository("llmServices").find({ sort: "sort" });
63
+ return services.filter((service) => service.enabled !== false).map((service) => this.toEnabledLLMService(service)).filter(Boolean);
64
+ }
65
+ async resolveModel(model) {
66
+ var _a;
67
+ if ((model == null ? void 0 : model.llmService) && (model == null ? void 0 : model.model)) {
68
+ return model;
69
+ }
70
+ const services = await this.listAllEnabledModels();
71
+ const service = services.find((service2) => service2.enabledModels.length);
72
+ const firstModel = (_a = service == null ? void 0 : service.enabledModels[0]) == null ? void 0 : _a.value;
73
+ if ((service == null ? void 0 : service.llmService) && firstModel) {
74
+ return {
75
+ llmService: service.llmService,
76
+ model: firstModel
77
+ };
78
+ }
79
+ throw new Error("LLM service not configured");
80
+ }
81
+ toEnabledLLMService(service) {
82
+ var _a, _b, _c;
83
+ const provider = ((_a = service.get) == null ? void 0 : _a.call(service, "provider")) || service.provider;
84
+ const providerMeta = this.llmProviders.get(provider);
85
+ if (!providerMeta) {
86
+ return null;
87
+ }
88
+ const enabledModels = this.getEnabledModels(service);
89
+ if (!enabledModels.length) {
90
+ return null;
91
+ }
92
+ const Provider = providerMeta.provider;
93
+ const providerClient = new Provider({ app: this.plugin.app });
94
+ return {
95
+ llmService: ((_b = service.get) == null ? void 0 : _b.call(service, "name")) || service.name,
96
+ llmServiceTitle: ((_c = service.get) == null ? void 0 : _c.call(service, "title")) || service.title,
97
+ provider,
98
+ providerTitle: providerMeta.title,
99
+ enabledModels,
100
+ supportWebSearch: providerMeta.supportWebSearch ?? false,
101
+ isToolConflict: providerClient.isToolConflict()
102
+ };
103
+ }
104
+ getEnabledModels(service) {
105
+ var _a, _b;
106
+ const provider = ((_a = service.get) == null ? void 0 : _a.call(service, "provider")) || service.provider;
107
+ const raw = ((_b = service.get) == null ? void 0 : _b.call(service, "enabledModels")) || service.enabledModels;
108
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && raw.mode) {
109
+ if (raw.mode === "recommended") {
110
+ return (0, import_recommended_models.getRecommendedModels)(provider);
111
+ }
112
+ return (raw.models || []).filter((model) => model.value).map((model) => ({
113
+ label: model.label || model.value,
114
+ value: model.value
115
+ }));
116
+ }
117
+ if (Array.isArray(raw)) {
118
+ if (!raw.length) {
119
+ return (0, import_recommended_models.getRecommendedModels)(provider);
120
+ }
121
+ return raw.map((id) => ({ label: id, value: id }));
122
+ }
123
+ return (0, import_recommended_models.getRecommendedModels)(provider);
124
+ }
60
125
  async getLLMService(options) {
61
126
  const { llmService, model, webSearch } = options ?? {};
62
127
  if (!llmService || !model) {
@@ -7,6 +7,11 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import PluginAIServer from '../plugin';
10
+ type LLMStreamOptions = {
11
+ pollInterval?: number;
12
+ initialWaitTimeout?: number;
13
+ signal?: AbortSignal;
14
+ };
10
15
  export declare class LLMStreamCachedManager {
11
16
  private readonly plugin;
12
17
  private cachePromise?;
@@ -14,14 +19,10 @@ export declare class LLMStreamCachedManager {
14
19
  getCached(sessionId: string): LLMStreamCached;
15
20
  clear(sessionId: string): Promise<void>;
16
21
  append(sessionId: string, chunk: string): Promise<void>;
17
- stream(sessionId: string, options?: {
18
- pollInterval?: number;
19
- initialWaitTimeout?: number;
20
- }): AsyncGenerator<string, void, void>;
22
+ stream(sessionId: string, options?: LLMStreamOptions): AsyncGenerator<string, void, void>;
21
23
  private getChunks;
22
24
  private getCache;
23
- private withLock;
24
- private getLockKey;
25
+ private get store();
25
26
  }
26
27
  export declare class LLMStreamCached {
27
28
  private readonly sessionId;
@@ -30,8 +31,13 @@ export declare class LLMStreamCached {
30
31
  clear(): Promise<void>;
31
32
  append(chunk: string): Promise<void>;
32
33
  skipped(): Promise<void>;
33
- stream(options?: {
34
- pollInterval?: number;
35
- initialWaitTimeout?: number;
36
- }): AsyncGenerator<string, void, void>;
34
+ stream(options?: LLMStreamOptions): AsyncGenerator<string, void, void>;
37
35
  }
36
+ export declare class BufferedLLMStreamCached extends LLMStreamCached {
37
+ private buffer;
38
+ private interval;
39
+ clear(): Promise<void>;
40
+ append(chunk: string): Promise<void>;
41
+ private flush;
42
+ }
43
+ export {};
@@ -26,54 +26,59 @@ var __copyProps = (to, from, except, desc) => {
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
  var llm_stream_manager_exports = {};
28
28
  __export(llm_stream_manager_exports, {
29
+ BufferedLLMStreamCached: () => BufferedLLMStreamCached,
29
30
  LLMStreamCached: () => LLMStreamCached,
30
31
  LLMStreamCachedManager: () => LLMStreamCachedManager
31
32
  });
32
33
  module.exports = __toCommonJS(llm_stream_manager_exports);
33
- var import_utils = require("@nocobase/utils");
34
34
  const CACHE_NAME = "ai-llm-stream-cache";
35
- const LOCK_KEY_PREFIX = "ai-llm-stream-lock";
36
- const WRITE_LOCK_TTL = 3e3;
37
35
  const CACHE_TTL = 10 * 60 * 1e3;
38
36
  const STREAM_END_MARK = '"type":"stream_end"';
39
37
  const SKIPPED_MARK = "__skipped__";
40
- const DEFAULT_POLL_INTERVAL = 50;
41
- const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e3;
38
+ const DEFAULT_INITIAL_WAIT_TIMEOUT = 1e4;
42
39
  class LLMStreamCachedManager {
43
40
  constructor(plugin) {
44
41
  this.plugin = plugin;
45
42
  }
46
43
  cachePromise;
47
44
  getCached(sessionId) {
45
+ if (this.store !== "memory") {
46
+ return new BufferedLLMStreamCached(sessionId, this);
47
+ }
48
48
  return new LLMStreamCached(sessionId, this);
49
49
  }
50
50
  async clear(sessionId) {
51
- await this.withLock(sessionId, async () => {
52
- const cache = await this.getCache();
53
- await cache.del(sessionId);
54
- });
51
+ const cache = await this.getCache();
52
+ await cache.del(sessionId);
55
53
  }
56
54
  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
- });
55
+ const cache = await this.getCache();
56
+ const chunks = await cache.get(sessionId) ?? [];
57
+ chunks.push(chunk);
58
+ await cache.set(sessionId, chunks, CACHE_TTL);
63
59
  }
64
60
  async *stream(sessionId, options) {
65
- const pollInterval = (options == null ? void 0 : options.pollInterval) ?? DEFAULT_POLL_INTERVAL;
61
+ const pollInterval = (options == null ? void 0 : options.pollInterval) ?? (this.store !== "memory" ? 1e3 : 100);
66
62
  const initialWaitTimeout = (options == null ? void 0 : options.initialWaitTimeout) ?? DEFAULT_INITIAL_WAIT_TIMEOUT;
63
+ const signal = options == null ? void 0 : options.signal;
67
64
  let offset = 0;
68
65
  let waited = 0;
69
66
  let completed = false;
70
67
  while (!completed) {
68
+ if (signal == null ? void 0 : signal.aborted) {
69
+ return;
70
+ }
71
71
  const chunks = await this.getChunks(sessionId);
72
72
  const lastSkippedIndex = chunks.lastIndexOf(SKIPPED_MARK);
73
+ let hasNewChunks = false;
73
74
  if (lastSkippedIndex >= offset) {
74
75
  offset = lastSkippedIndex + 1;
75
76
  }
76
77
  while (offset < chunks.length) {
78
+ if (signal == null ? void 0 : signal.aborted) {
79
+ return;
80
+ }
81
+ hasNewChunks = true;
77
82
  const chunk = chunks[offset++];
78
83
  yield chunk;
79
84
  waited = 0;
@@ -85,16 +90,15 @@ class LLMStreamCachedManager {
85
90
  if (completed) {
86
91
  return;
87
92
  }
88
- if (!chunks.length) {
89
- if (offset > 0) {
90
- return;
91
- }
93
+ if (!hasNewChunks) {
92
94
  if (waited >= initialWaitTimeout) {
93
95
  return;
94
96
  }
95
97
  waited += pollInterval;
96
98
  }
97
- await (0, import_utils.sleep)(pollInterval);
99
+ if (await abortableSleep(pollInterval, signal)) {
100
+ return;
101
+ }
98
102
  }
99
103
  }
100
104
  async getChunks(sessionId) {
@@ -104,15 +108,13 @@ class LLMStreamCachedManager {
104
108
  async getCache() {
105
109
  this.cachePromise ??= this.plugin.app.cacheManager.createCache({
106
110
  name: CACHE_NAME,
107
- store: this.plugin.app.cacheManager.defaultStore
111
+ store: this.store
108
112
  });
109
113
  return this.cachePromise;
110
114
  }
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}`;
115
+ get store() {
116
+ var _a;
117
+ return ((_a = this.plugin.app.options.cacheManager) == null ? void 0 : _a.defaultStore) ?? "memory";
116
118
  }
117
119
  }
118
120
  class LLMStreamCached {
@@ -135,8 +137,100 @@ class LLMStreamCached {
135
137
  }
136
138
  }
137
139
  }
140
+ class BufferedLLMStreamCached extends LLMStreamCached {
141
+ buffer = new ChunksBuffer();
142
+ interval;
143
+ async clear() {
144
+ clearInterval(this.interval);
145
+ delete this.interval;
146
+ await this.flush();
147
+ await super.clear();
148
+ }
149
+ async append(chunk) {
150
+ if (!this.interval) {
151
+ this.interval = setInterval(() => {
152
+ void this.flush();
153
+ }, 1e3);
154
+ }
155
+ this.buffer.append(chunk);
156
+ }
157
+ async flush() {
158
+ if (this.buffer.isEmpty()) {
159
+ return;
160
+ }
161
+ const chunks = this.buffer.compact();
162
+ this.buffer.clear();
163
+ const appendChunks = async () => {
164
+ for (const chunk of chunks) {
165
+ await super.append(chunk);
166
+ }
167
+ };
168
+ await appendChunks();
169
+ }
170
+ }
171
+ class ChunksBuffer {
172
+ _size = 0;
173
+ _chunks = [];
174
+ append(chunk) {
175
+ this._size += chunk.length;
176
+ this._chunks.push(chunk);
177
+ }
178
+ clear() {
179
+ this._size = 0;
180
+ this._chunks = [];
181
+ }
182
+ isEmpty() {
183
+ return this._size === 0;
184
+ }
185
+ get size() {
186
+ return this._size;
187
+ }
188
+ get chunks() {
189
+ return this._chunks;
190
+ }
191
+ compact() {
192
+ const chunks = [];
193
+ let buffer = "";
194
+ for (const chunk of this._chunks) {
195
+ if (chunk === SKIPPED_MARK) {
196
+ if (buffer) {
197
+ chunks.push(buffer);
198
+ buffer = "";
199
+ }
200
+ chunks.push(SKIPPED_MARK);
201
+ } else {
202
+ buffer += chunk;
203
+ }
204
+ }
205
+ if (buffer) {
206
+ chunks.push(buffer);
207
+ }
208
+ return chunks;
209
+ }
210
+ }
211
+ function abortableSleep(ms, signal) {
212
+ if (signal == null ? void 0 : signal.aborted) {
213
+ return Promise.resolve(true);
214
+ }
215
+ return new Promise((resolve) => {
216
+ const timeout = setTimeout(() => {
217
+ cleanup();
218
+ resolve(false);
219
+ }, ms);
220
+ const cleanup = () => {
221
+ clearTimeout(timeout);
222
+ signal == null ? void 0 : signal.removeEventListener("abort", abort);
223
+ };
224
+ const abort = () => {
225
+ cleanup();
226
+ resolve(true);
227
+ };
228
+ signal == null ? void 0 : signal.addEventListener("abort", abort, { once: true });
229
+ });
230
+ }
138
231
  // Annotate the CommonJS export names for ESM import in node:
139
232
  0 && (module.exports = {
233
+ BufferedLLMStreamCached,
140
234
  LLMStreamCached,
141
235
  LLMStreamCachedManager
142
236
  });
@@ -0,0 +1,14 @@
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 { Migration } from '@nocobase/server';
10
+ export default class extends Migration {
11
+ on: string;
12
+ appVersion: string;
13
+ up(): Promise<void>;
14
+ }