@nocobase/plugin-ai 2.0.0-alpha.59 → 2.0.0-alpha.60

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 (31) hide show
  1. package/dist/client/{1e2575d69d11cc58.js → a7f0550514128d80.js} +1 -1
  2. package/dist/client/ai-employees/chatbox/hooks/useChatMessageActions.d.ts +10 -0
  3. package/dist/client/ai-employees/suggestions/tools/index.d.ts +10 -0
  4. package/dist/client/ai-employees/suggestions/ui/SuggestionsOptions.d.ts +16 -0
  5. package/dist/client/ai-employees/types.d.ts +1 -0
  6. package/dist/client/index.js +3 -3
  7. package/dist/externalVersion.js +11 -11
  8. package/dist/node_modules/@langchain/anthropic/package.json +1 -1
  9. package/dist/node_modules/@langchain/core/package.json +1 -1
  10. package/dist/node_modules/@langchain/deepseek/package.json +1 -1
  11. package/dist/node_modules/@langchain/google-genai/package.json +1 -1
  12. package/dist/node_modules/@langchain/ollama/package.json +1 -1
  13. package/dist/node_modules/@langchain/openai/package.json +1 -1
  14. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  15. package/dist/node_modules/zod/package.json +1 -1
  16. package/dist/node_modules/zod-to-json-schema/package.json +1 -1
  17. package/dist/server/ai-employees/built-in/data-modeling/index.js +4 -0
  18. package/dist/server/ai-employees/built-in/data-modeling/prompt.js +19 -8
  19. package/dist/server/llm-providers/google-genai.d.ts +5 -0
  20. package/dist/server/llm-providers/google-genai.js +26 -0
  21. package/dist/server/llm-providers/provider.d.ts +1 -12
  22. package/dist/server/manager/ai-chat-conversation.js +1 -1
  23. package/dist/server/plugin.js +5 -0
  24. package/dist/server/tools/data-modeling.js +56 -13
  25. package/dist/server/tools/datasource-query.js +17 -7
  26. package/dist/server/tools/suggestions.d.ts +10 -0
  27. package/dist/server/tools/suggestions.js +74 -0
  28. package/dist/server/tools/utils.d.ts +40 -0
  29. package/dist/server/tools/utils.js +87 -0
  30. package/dist/server/tools/workflow-caller.js +2 -1
  31. package/package.json +2 -2
@@ -47,17 +47,22 @@ var import_lodash = __toESM(require("lodash"));
47
47
  var import_zod = require("zod");
48
48
  const idField = {
49
49
  name: "id",
50
- type: "bigInt",
51
- autoIncrement: true,
50
+ type: "snowflakeId",
51
+ autoIncrement: false,
52
52
  primaryKey: true,
53
53
  allowNull: false,
54
54
  uiSchema: {
55
55
  type: "number",
56
56
  title: '{{t("ID")}}',
57
57
  "x-component": "InputNumber",
58
- "x-read-pretty": true
58
+ "x-component-props": {
59
+ stringMode: true,
60
+ separator: "0.00",
61
+ step: "1"
62
+ },
63
+ "x-validator": "integer"
59
64
  },
60
- interface: "id"
65
+ interface: "snowflakeId"
61
66
  };
62
67
  const createdAtField = {
63
68
  name: "createdAt",
@@ -95,20 +100,20 @@ const createPrompt = `You are now entering the **New Schema Creation Flow**. Fol
95
100
 
96
101
  3. **Output and Confirmation**
97
102
  - Present the full schema in **formatted natural language** (not plain JSON).
98
- - Wait for user confirmation, then call the \`defineCollections\` tool with the **complete schema definition**.
103
+ - Wait for user confirmation, then call the \`dataModeling-defineCollections\` tool with the **complete schema definition**.
99
104
  - Until the tool responds, **assume the schema is not saved** \u2014 user may continue editing.
100
105
  - **Do not say or imply the schema has been created without tool response.**
101
106
 
102
- Only the \`defineCollections\` tool may be used.`;
107
+ Only the \`dataModeling-defineCollections\` tool may be used.`;
103
108
  const editPrompt = `## Existing Schema Editing Flow
104
109
 
105
110
  1. **Clarify What Needs to Be Changed**
106
111
  - Identify which tables are affected by the requested changes.
107
- - If needed, call \`getCollectionNames\` to retrieve the list of all tables (ID and title).
112
+ - If needed, call \`dataModeling-getCollectionNames\` to retrieve the list of all tables (ID and title).
108
113
 
109
114
  2. **Fetch Table Metadata**
110
115
  - Analyze the current structure and identify what needs to be added, removed, or updated.
111
- - If needed, use the \`getCollectionMetadata\` tool to retrieve schema details of the target table(s).
116
+ - If needed, use the \`dataModeling-getCollectionMetadata\` tool to retrieve schema details of the target table(s).
112
117
 
113
118
  3. **Propose Changes**
114
119
  - Output your change suggestions in clear **natural language**.
@@ -116,9 +121,14 @@ const editPrompt = `## Existing Schema Editing Flow
116
121
  - Wait for user confirmation before applying any changes.
117
122
 
118
123
  4. **Apply Changes**
119
- - Once confirmed, call the \`defineCollections\` tool with **only the modified parts** of the schema.
124
+ - Once confirmed, call the \`dataModeling-defineCollections\` tool with **only the modified parts** of the schema.
120
125
  - Until the tool responds successfully, assume changes have not been saved \u2014 the user may continue editing.
121
126
  - **Do not say or imply the schema is being or has been updated until a tool response is received.**`;
127
+ class IntentError extends Error {
128
+ constructor(message) {
129
+ super(message);
130
+ }
131
+ }
122
132
  const dataModelingIntentRouter = {
123
133
  name: "intentRouter",
124
134
  title: '{{t("Intent Router")}}',
@@ -305,14 +315,26 @@ const defineCollections = {
305
315
  title: '{{t("Define collections")}}',
306
316
  description: '{{t("Create or edit collections")}}',
307
317
  schema: import_zod.z.object({
318
+ intent: import_zod.z.enum(["create", "edit"]).describe(
319
+ `Pass the intent of the current tool invocation as an enum value. The value must be either 'create' or 'edit':
320
+ - create: create a brand-new data table definition
321
+ - edit: modify an existing data table definition`
322
+ ),
308
323
  collections: import_zod.z.array(
309
324
  import_zod.z.object({}).catchall(import_zod.z.any()).describe("Valid collection object which defined in collection_type_definition")
310
325
  ).describe("An array of collections to be defined or edited.")
311
326
  }),
312
- invoke: async (ctx) => {
313
- const {
314
- args: { collections }
315
- } = ctx.action.params.values || {};
327
+ invoke: async (ctx, args) => {
328
+ var _a, _b, _c;
329
+ const { intent, collections: originalCollections } = ((_c = (_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) == null ? void 0 : _c.args) ?? args ?? {};
330
+ if (!intent || !["create", "edit"].includes(intent)) {
331
+ return {
332
+ status: "error",
333
+ content: `Please explicitly specify your intent. The value of the intent parameter must be either 'create' or 'edit'.`
334
+ };
335
+ }
336
+ const collectionsType = typeof originalCollections;
337
+ const collections = collectionsType === "string" ? JSON.parse(originalCollections) : originalCollections;
316
338
  if (!collections || !Array.isArray(collections)) {
317
339
  return {
318
340
  status: "error",
@@ -352,12 +374,22 @@ const defineCollections = {
352
374
  const baseFields = [...systemFields, ...normalFields];
353
375
  const collection = await repo.findOne({ filter: { name: options.name }, transaction });
354
376
  if (!collection) {
377
+ if (intent === "edit") {
378
+ throw new IntentError(
379
+ `You want to edit a collection definition, but there is no existing data table definition named '${options.name}'.`
380
+ );
381
+ }
355
382
  await repo.create({
356
383
  values: { ...options, fields: baseFields },
357
384
  transaction,
358
385
  context: ctx
359
386
  });
360
387
  } else {
388
+ if (intent === "create") {
389
+ throw new IntentError(
390
+ `You want to create a collection definition, but a collection definition named '${options.name}' already exists. Please change the name of your collection definition and then invoke this tool again.`
391
+ );
392
+ }
361
393
  await repo.update({
362
394
  filterByTk: options.name,
363
395
  values: import_lodash.default.omit(options, ["fields"]),
@@ -429,6 +461,17 @@ const defineCollections = {
429
461
  stack: e.stack,
430
462
  cause: e.cause
431
463
  });
464
+ if (e instanceof IntentError) {
465
+ return {
466
+ status: "error",
467
+ content: e.message
468
+ };
469
+ }
470
+ if (intent === "create") {
471
+ for (const options of sorted) {
472
+ ctx.db.removeCollection(options.name);
473
+ }
474
+ }
432
475
  return {
433
476
  status: "error",
434
477
  content: `Failed to define collections: ${e.message}`
@@ -31,6 +31,7 @@ __export(datasource_query_exports, {
31
31
  });
32
32
  module.exports = __toCommonJS(datasource_query_exports);
33
33
  var import_zod = require("zod");
34
+ var import_utils = require("./utils");
34
35
  const ArgSchema = import_zod.z.object({
35
36
  datasource: import_zod.z.string().describe('{{t("Data source key")}}'),
36
37
  collectionName: import_zod.z.string().describe('{{t("Collection name")}}'),
@@ -93,8 +94,8 @@ const example3: QueryObject = { age: { $lt: 50 } };
93
94
  sort: import_zod.z.array(import_zod.z.string()).describe(
94
95
  '{{t("Sort field names. By default, they are in ascending order. A minus sign before the field name indicates descending order")}}'
95
96
  ),
96
- offset: import_zod.z.number().describe('{{t("Offset of records to be queried")}}'),
97
- limit: import_zod.z.number().describe('{{t("Maximum number of records to be queried")}}')
97
+ offset: import_zod.z.number().optional().describe('{{t("Offset of records to be queried")}}'),
98
+ limit: import_zod.z.number().optional().describe('{{t("Maximum number of records to be queried")}}')
98
99
  });
99
100
  const dataSourceQuery = {
100
101
  name: "dataSourceQuery",
@@ -103,10 +104,21 @@ const dataSourceQuery = {
103
104
  schema: ArgSchema,
104
105
  invoke: async (ctx, args) => {
105
106
  const plugin = ctx.app.pm.get("ai");
106
- const content = await plugin.aiContextDatasourceManager.query(ctx, args);
107
+ const { limit, offset } = (0, import_utils.normalizeLimitOffset)(args, { defaultLimit: 50, maxLimit: import_utils.MAX_QUERY_LIMIT });
108
+ const content = await plugin.aiContextDatasourceManager.query(ctx, {
109
+ ...args,
110
+ limit,
111
+ offset
112
+ });
113
+ const records = (0, import_utils.truncateLongStrings)((content == null ? void 0 : content.records) || []);
114
+ const total = (content == null ? void 0 : content.total) || 0;
115
+ const result = (0, import_utils.buildPagedToolResult)({ total, offset, limit, records });
116
+ if (result.hasMore) {
117
+ result.note = `Showing ${result.returned} of ${result.total} records. To fetch more, call this tool again with offset:${result.nextOffset}`;
118
+ }
107
119
  return {
108
120
  status: "success",
109
- content: JSON.stringify(content)
121
+ content: JSON.stringify(result)
110
122
  };
111
123
  }
112
124
  };
@@ -117,9 +129,7 @@ const dataSourceCounting = {
117
129
  schema: ArgSchema,
118
130
  invoke: async (ctx, args) => {
119
131
  const plugin = ctx.app.pm.get("ai");
120
- args.offset = 0;
121
- args.limit = 1;
122
- const content = await plugin.aiContextDatasourceManager.query(ctx, args);
132
+ const content = await plugin.aiContextDatasourceManager.query(ctx, { ...args, offset: 0, limit: 1 });
123
133
  return {
124
134
  status: "success",
125
135
  content: String((content == null ? void 0 : content.total) ?? 0)
@@ -0,0 +1,10 @@
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 { ToolOptions } from '../manager/tool-manager';
10
+ export declare const suggestions: ToolOptions;
@@ -0,0 +1,74 @@
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 suggestions_exports = {};
28
+ __export(suggestions_exports, {
29
+ suggestions: () => suggestions
30
+ });
31
+ module.exports = __toCommonJS(suggestions_exports);
32
+ var import_zod = require("zod");
33
+ const suggestions = {
34
+ name: "suggestions",
35
+ title: '{{t("Suggestions")}}',
36
+ description: '{{t("Provide a list of suggested prompts for the user to choose from.")}}',
37
+ schema: import_zod.z.object({
38
+ options: import_zod.z.array(import_zod.z.string()).describe(
39
+ "A list of suggested prompts that can be presented to the user as selectable options.Each option represents a possible next user message."
40
+ )
41
+ }),
42
+ invoke: async (ctx, _args, id) => {
43
+ var _a, _b;
44
+ const { messageId, args } = ((_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) || {};
45
+ if (messageId) {
46
+ const messageRepo = ctx.app.db.getRepository("aiMessages");
47
+ const message = await messageRepo.findOne({
48
+ filterByTk: messageId
49
+ });
50
+ const toolCalls = message.toolCalls || [];
51
+ const index = toolCalls.findIndex((toolCall) => toolCall.id === id);
52
+ if (index !== -1) {
53
+ toolCalls[index] = {
54
+ ...toolCalls[index],
55
+ selectedSuggestion: args == null ? void 0 : args.option
56
+ };
57
+ await messageRepo.update({
58
+ filter: { messageId },
59
+ values: {
60
+ toolCalls
61
+ }
62
+ });
63
+ }
64
+ }
65
+ return {
66
+ status: "success",
67
+ content: args == null ? void 0 : args.option
68
+ };
69
+ }
70
+ };
71
+ // Annotate the CommonJS export names for ESM import in node:
72
+ 0 && (module.exports = {
73
+ suggestions
74
+ });
@@ -0,0 +1,40 @@
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
+ export declare const MAX_STRING_LENGTH = 500;
11
+ /** 单次查询最大记录数 */
12
+ export declare const MAX_QUERY_LIMIT = 100;
13
+ export declare function normalizeLimitOffset(args: {
14
+ limit?: unknown;
15
+ offset?: unknown;
16
+ }, options?: {
17
+ defaultLimit?: number;
18
+ maxLimit?: number;
19
+ }): {
20
+ limit: number;
21
+ offset: number;
22
+ };
23
+ export declare function buildPagedToolResult<T>(params: {
24
+ total: number;
25
+ offset: number;
26
+ limit: number;
27
+ records: T[];
28
+ }): {
29
+ total: number;
30
+ offset: number;
31
+ limit: number;
32
+ returned: number;
33
+ hasMore: boolean;
34
+ nextOffset: number;
35
+ records: T[];
36
+ };
37
+ /**
38
+ * 递归截断对象中的长字符串,保持 JSON 结构完整
39
+ */
40
+ export declare function truncateLongStrings(obj: any, maxLen?: number): any;
@@ -0,0 +1,87 @@
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 utils_exports = {};
28
+ __export(utils_exports, {
29
+ MAX_QUERY_LIMIT: () => MAX_QUERY_LIMIT,
30
+ MAX_STRING_LENGTH: () => MAX_STRING_LENGTH,
31
+ buildPagedToolResult: () => buildPagedToolResult,
32
+ normalizeLimitOffset: () => normalizeLimitOffset,
33
+ truncateLongStrings: () => truncateLongStrings
34
+ });
35
+ module.exports = __toCommonJS(utils_exports);
36
+ const MAX_STRING_LENGTH = 500;
37
+ const MAX_QUERY_LIMIT = 100;
38
+ function normalizeLimitOffset(args, options) {
39
+ const defaultLimit = (options == null ? void 0 : options.defaultLimit) ?? 50;
40
+ const maxLimit = (options == null ? void 0 : options.maxLimit) ?? MAX_QUERY_LIMIT;
41
+ const rawLimit = typeof args.limit === "number" && Number.isFinite(args.limit) ? args.limit : void 0;
42
+ const rawOffset = typeof args.offset === "number" && Number.isFinite(args.offset) ? args.offset : void 0;
43
+ const limit = Math.min(Math.max(rawLimit ?? defaultLimit, 1), maxLimit);
44
+ const offset = Math.max(rawOffset ?? 0, 0);
45
+ return { limit, offset };
46
+ }
47
+ function buildPagedToolResult(params) {
48
+ const returned = params.records.length;
49
+ const nextOffset = params.offset + returned;
50
+ const hasMore = params.total > nextOffset;
51
+ return {
52
+ total: params.total,
53
+ offset: params.offset,
54
+ limit: params.limit,
55
+ returned,
56
+ hasMore,
57
+ nextOffset,
58
+ records: params.records
59
+ };
60
+ }
61
+ function truncateLongStrings(obj, maxLen = MAX_STRING_LENGTH) {
62
+ if (obj === null || obj === void 0) {
63
+ return obj;
64
+ }
65
+ if (typeof obj === "string") {
66
+ return obj.length > maxLen ? obj.slice(0, maxLen) + "...[truncated]" : obj;
67
+ }
68
+ if (Array.isArray(obj)) {
69
+ return obj.map((item) => truncateLongStrings(item, maxLen));
70
+ }
71
+ if (typeof obj === "object") {
72
+ const result = {};
73
+ for (const key in obj) {
74
+ result[key] = truncateLongStrings(obj[key], maxLen);
75
+ }
76
+ return result;
77
+ }
78
+ return obj;
79
+ }
80
+ // Annotate the CommonJS export names for ESM import in node:
81
+ 0 && (module.exports = {
82
+ MAX_QUERY_LIMIT,
83
+ MAX_STRING_LENGTH,
84
+ buildPagedToolResult,
85
+ normalizeLimitOffset,
86
+ truncateLongStrings
87
+ });
@@ -31,6 +31,7 @@ __export(workflow_caller_exports, {
31
31
  module.exports = __toCommonJS(workflow_caller_exports);
32
32
  var import_zod = require("zod");
33
33
  var import_plugin_workflow = require("@nocobase/plugin-workflow");
34
+ var import_utils = require("./utils");
34
35
  const buildSchema = (config) => {
35
36
  var _a;
36
37
  const schemaProperties = {};
@@ -86,7 +87,7 @@ const invoke = async (ctx, workflow, args) => {
86
87
  if (processor.execution.status !== import_plugin_workflow.EXECUTION_STATUS.RESOLVED) {
87
88
  return { status: "error", content: "Workflow execution exceptions" };
88
89
  }
89
- const lastJobResult = processor.lastSavedJob.result;
90
+ const lastJobResult = (0, import_utils.truncateLongStrings)(processor.lastSavedJob.result);
90
91
  return {
91
92
  status: "success",
92
93
  content: JSON.stringify(lastJobResult)
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Create AI employees with diverse skills to collaborate with humans, build systems, and handle business operations.",
7
7
  "description.ru-RU": "Поддержка интеграции с AI-сервисами: предоставляются AI-узлы для рабочих процессов, расширяя возможности бизнес-обработки.",
8
8
  "description.zh-CN": "创建各种技能的 AI 员工,与人类协同,搭建系统,处理业务。",
9
- "version": "2.0.0-alpha.59",
9
+ "version": "2.0.0-alpha.60",
10
10
  "main": "dist/server/index.js",
11
11
  "homepage": "https://docs.nocobase.com/handbook/action-ai",
12
12
  "homepage.ru-RU": "https://docs-ru.nocobase.com/handbook/action-ai",
@@ -47,5 +47,5 @@
47
47
  "keywords": [
48
48
  "AI"
49
49
  ],
50
- "gitHead": "ca9fca2a826b17735c6287bebf877b2da9a00e24"
50
+ "gitHead": "9113d61ce85b60b7ba3d0e5ca64182d92a15ece4"
51
51
  }