Version not found. Please check the version and try again.
@nocobase/plugin-data-source-manager 2.1.0-beta.8 → 2.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client-v2.d.ts +2 -0
- package/client-v2.js +1 -0
- package/dist/ai/ai-employees/orin/ignored.d.ts +10 -0
- package/dist/ai/ai-employees/orin/ignored.js +59 -0
- package/dist/ai/ai-employees/orin/skills/data-modeling/SKILLS.md +245 -0
- package/dist/ai/{tools → ai-employees/orin/skills/data-modeling/tools}/defineCollections.js +47 -1
- package/dist/ai/{tools → ai-employees/orin/skills/data-modeling/tools}/intentRouter.js +1 -1
- package/dist/ai/{tools/data-query → common}/common.js +63 -11
- package/dist/ai/{tools/data-query → common}/utils.d.ts +1 -0
- package/dist/ai/{tools/data-query → common}/utils.js +15 -0
- package/dist/ai/skills/data-metadata/SKILLS.md +108 -0
- package/dist/ai/{tools → skills/data-metadata/tools}/getCollectionMetadata.js +2 -2
- package/dist/ai/{tools → skills/data-metadata/tools}/getCollectionNames.js +2 -2
- package/dist/ai/{tools → skills/data-metadata/tools}/getDataSources.js +2 -2
- package/dist/ai/{tools → skills/data-metadata/tools}/searchFieldMetadata.js +2 -2
- package/dist/ai/skills/data-query/SKILLS.md +245 -0
- package/dist/ai/skills/data-query/tools/dataQuery.js +282 -0
- package/dist/ai/{tools/data-query → skills/data-query/tools}/dataSourceCounting.js +12 -4
- package/dist/ai/skills/data-query/tools/dataSourceQuery.d.ts +10 -0
- package/dist/ai/{tools/data-query → skills/data-query/tools}/dataSourceQuery.js +12 -5
- package/dist/client/271.1374702d84646fff.js +10 -0
- package/dist/client/369.342f1e0231dbe482.js +10 -0
- package/dist/client/397.c8c6659cf3f7ac1c.js +10 -0
- package/dist/client/416.197712b8b93033c5.js +10 -0
- package/dist/client/420.a37695bbfc3bec7a.js +10 -0
- package/dist/client/603.d6022bceb934f5b0.js +10 -0
- package/dist/client/644.b98e4b39058e9c32.js +10 -0
- package/dist/client/936.d5ab7aecb4eb6004.js +10 -0
- package/dist/client/index.js +1 -1
- package/dist/client-v2/167.52e683be46f9cfc7.js +10 -0
- package/dist/client-v2/426.53e3eaee8d9dbca0.js +10 -0
- package/dist/client-v2/907.a0ab273516f45ac3.js +10 -0
- package/dist/client-v2/components/DataSourceForm.d.ts +17 -0
- package/dist/client-v2/field-interfaces/index.d.ts +13 -0
- package/dist/client-v2/index.d.ts +12 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/client-v2/locale.d.ts +12 -0
- package/dist/client-v2/pages/DataSourceCollectionsPage.d.ts +10 -0
- package/dist/client-v2/pages/DataSourcesPage.d.ts +10 -0
- package/dist/client-v2/pages/components/CollectionsPage.d.ts +27 -0
- package/dist/client-v2/pages/components/FieldForm.d.ts +19 -0
- package/dist/client-v2/pages/components/FieldsPage.d.ts +16 -0
- package/dist/client-v2/pages/components/ViewCollectionConfigure.d.ts +27 -0
- package/dist/client-v2/pages/components/collectionFieldApi.d.ts +11 -0
- package/dist/client-v2/pages/components/collectionTemplateFieldInterfaces.d.ts +18 -0
- package/dist/client-v2/pages/permissions/DataSourcePermissionsTab.d.ts +15 -0
- package/dist/client-v2/pages/permissions/PermissionEditors.d.ts +45 -0
- package/dist/client-v2/pages/permissions/ScopeSelect.d.ts +21 -0
- package/dist/client-v2/pages/permissions/permissionRequests.d.ts +33 -0
- package/dist/client-v2/pages/permissions/types.d.ts +55 -0
- package/dist/client-v2/plugin.d.ts +243 -0
- package/dist/client-v2/registries.d.ts +36 -0
- package/dist/client-v2/runtime.d.ts +12 -0
- package/dist/client-v2/utils/compileLegacyTemplate.d.ts +16 -0
- package/dist/client-v2/utils/error.d.ts +11 -0
- package/dist/externalVersion.js +17 -13
- package/dist/locale/en-US.json +275 -3
- package/dist/locale/zh-CN.json +275 -3
- package/dist/node_modules/zod/index.cjs +1 -1
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/server/collections/data-sources-collections.js +1 -0
- package/dist/server/collections/data-sources-fields.js +1 -0
- package/dist/server/collections/data-sources-roles-resources-actions.js +1 -0
- package/dist/server/collections/data-sources-roles-resources-scopes.js +1 -0
- package/dist/server/collections/data-sources-roles-resources.js +1 -0
- package/dist/server/collections/data-sources-roles.js +1 -0
- package/dist/server/collections/data-sources.js +1 -0
- package/dist/server/mcp-post-processors.d.ts +24 -0
- package/dist/server/mcp-post-processors.js +158 -0
- package/dist/server/models/data-source.js +3 -13
- package/dist/server/plugin.js +7 -2
- package/dist/server/resourcers/data-sources-collections-fields.d.ts +1 -0
- package/dist/server/resourcers/data-sources-collections-fields.js +19 -0
- package/dist/server/services/external-field-apply.d.ts +23 -0
- package/dist/server/services/external-field-apply.js +208 -0
- package/dist/server/utils.js +11 -3
- package/dist/swagger/index.d.ts +326 -0
- package/dist/swagger/index.js +385 -0
- package/package.json +4 -2
- package/dist/client/1a8cce8035a89dd9.js +0 -10
- package/dist/client/276bcf3de74bebd1.js +0 -10
- package/dist/client/2f7a418e7935984d.js +0 -10
- package/dist/client/689d3bec33ad2ec3.js +0 -10
- package/dist/client/d33eb40009bd7ec6.js +0 -10
- package/dist/client/dbf5ddf434ab29c0.js +0 -10
- package/dist/client/f528b165e77e3608.js +0 -10
- package/dist/client/fdae18574fda07af.js +0 -10
- /package/dist/ai/{tools → ai-employees/orin/skills/data-modeling/tools}/defineCollections.d.ts +0 -0
- /package/dist/ai/{tools → ai-employees/orin/skills/data-modeling/tools}/intentRouter.d.ts +0 -0
- /package/dist/ai/{tools/data-query → common}/common.d.ts +0 -0
- /package/dist/ai/{tools → skills/data-metadata/tools}/getCollectionMetadata.d.ts +0 -0
- /package/dist/ai/{tools → skills/data-metadata/tools}/getCollectionNames.d.ts +0 -0
- /package/dist/ai/{tools → skills/data-metadata/tools}/getDataSources.d.ts +0 -0
- /package/dist/ai/{tools → skills/data-metadata/tools}/searchFieldMetadata.d.ts +0 -0
- /package/dist/ai/{tools/data-query/dataSourceCounting.d.ts → skills/data-query/tools/dataQuery.d.ts} +0 -0
- /package/dist/ai/{tools/data-query/dataSourceQuery.d.ts → skills/data-query/tools/dataSourceCounting.d.ts} +0 -0
|
@@ -40,9 +40,9 @@ __export(getCollectionNames_exports, {
|
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(getCollectionNames_exports);
|
|
42
42
|
var import_ai = require("@nocobase/ai");
|
|
43
|
-
var import_package = __toESM(require("
|
|
43
|
+
var import_package = __toESM(require("../../../../../package.json"));
|
|
44
44
|
var getCollectionNames_default = (0, import_ai.defineTools)({
|
|
45
|
-
scope: "
|
|
45
|
+
scope: "SPECIFIED",
|
|
46
46
|
defaultPermission: "ALLOW",
|
|
47
47
|
introduction: {
|
|
48
48
|
title: `{{t("ai.tools.getCollectionNames.title", { ns: "${import_package.default.name}" })}}`,
|
|
@@ -40,9 +40,9 @@ __export(getDataSources_exports, {
|
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(getDataSources_exports);
|
|
42
42
|
var import_ai = require("@nocobase/ai");
|
|
43
|
-
var import_package = __toESM(require("
|
|
43
|
+
var import_package = __toESM(require("../../../../../package.json"));
|
|
44
44
|
var getDataSources_default = (0, import_ai.defineTools)({
|
|
45
|
-
scope: "
|
|
45
|
+
scope: "SPECIFIED",
|
|
46
46
|
defaultPermission: "ALLOW",
|
|
47
47
|
introduction: {
|
|
48
48
|
title: `{{t("ai.tools.getDataSources.title", { ns: "${import_package.default.name}" })}}`,
|
|
@@ -40,9 +40,9 @@ __export(searchFieldMetadata_exports, {
|
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(searchFieldMetadata_exports);
|
|
42
42
|
var import_ai = require("@nocobase/ai");
|
|
43
|
-
var import_package = __toESM(require("
|
|
43
|
+
var import_package = __toESM(require("../../../../../package.json"));
|
|
44
44
|
var searchFieldMetadata_default = (0, import_ai.defineTools)({
|
|
45
|
-
scope: "
|
|
45
|
+
scope: "SPECIFIED",
|
|
46
46
|
defaultPermission: "ALLOW",
|
|
47
47
|
introduction: {
|
|
48
48
|
title: `{{t("ai.tools.searchFieldMetadata.title", { ns: "${import_package.default.name}" })}}`,
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
scope: GENERAL
|
|
3
|
+
name: data-query
|
|
4
|
+
description: Inspect schemas, retrieve records, and run aggregate queries on specified datasources
|
|
5
|
+
tools:
|
|
6
|
+
- dataQuery
|
|
7
|
+
- dataSourceQuery
|
|
8
|
+
- dataSourceCounting
|
|
9
|
+
introduction:
|
|
10
|
+
title: '{{t("ai.skills.dataQuery.title", { ns: "@nocobase/plugin-data-source-manager" })}}'
|
|
11
|
+
about: '{{t("ai.skills.dataQuery.about", { ns: "@nocobase/plugin-data-source-manager" })}}'
|
|
12
|
+
---
|
|
13
|
+
You are a professional data query assistant for NocoBase.
|
|
14
|
+
|
|
15
|
+
You help users inspect schemas, retrieve records, and run aggregate queries on NocoBase collections.
|
|
16
|
+
|
|
17
|
+
# Primary Workflows
|
|
18
|
+
|
|
19
|
+
This skill focuses on safe read-only data access.
|
|
20
|
+
|
|
21
|
+
## Schema-first Querying
|
|
22
|
+
|
|
23
|
+
When the user does not provide an exact collection or field name, or when this is the first query against a collection in the current conversation:
|
|
24
|
+
|
|
25
|
+
1. If schema, field, relation path, or datasource choice is not already explicit and reliable, your first tool call must be `getSkill` with `skillName="data-metadata"`.
|
|
26
|
+
2. Do not guess collection names, field names, relation paths, or data types before `data-metadata` has been loaded.
|
|
27
|
+
3. Call `getDataSources` from the loaded `data-metadata` workflow if the target data source is unclear.
|
|
28
|
+
4. If multiple data sources may contain relevant data, inspect each candidate before choosing the query scope.
|
|
29
|
+
5. Do not silently default to `main` when other relevant data sources are available.
|
|
30
|
+
6. If you intentionally limit the query to one data source, explain why that data source was chosen and why others were not used.
|
|
31
|
+
7. Call `getCollectionNames` from the loaded `data-metadata` workflow to find the right collection.
|
|
32
|
+
8. Call `getCollectionMetadata` or `searchFieldMetadata` from the loaded `data-metadata` workflow to confirm field names, relation paths, and data types.
|
|
33
|
+
9. Only then run a data tool.
|
|
34
|
+
10. Even if the user already mentions a collection name such as `date_boundary_cases` or a common field such as `createdAt`, verify them with the loaded `data-metadata` workflow before the first real query when the collection has not yet been confirmed in the current conversation.
|
|
35
|
+
|
|
36
|
+
Do not guess collection names, measure aliases, or dotted relation paths.
|
|
37
|
+
|
|
38
|
+
## Raw Record Query
|
|
39
|
+
|
|
40
|
+
Use `dataSourceQuery` when the user wants actual records rather than grouped statistics.
|
|
41
|
+
|
|
42
|
+
Typical cases:
|
|
43
|
+
|
|
44
|
+
- list rows
|
|
45
|
+
- inspect recent records
|
|
46
|
+
- fetch selected fields
|
|
47
|
+
- browse data with filter, sort, and pagination
|
|
48
|
+
|
|
49
|
+
## Aggregate Query
|
|
50
|
+
|
|
51
|
+
Use `dataQuery` when the user wants:
|
|
52
|
+
|
|
53
|
+
- counts, sums, averages, min, max
|
|
54
|
+
- grouped statistics
|
|
55
|
+
- rankings
|
|
56
|
+
- trend buckets
|
|
57
|
+
- post-aggregation filtering with `having`
|
|
58
|
+
|
|
59
|
+
Prefer `dataQuery` over `dataSourceCounting` whenever the request can be expressed as a measure query, because it is closer to the repository `query` capability used by charts, actions, ACL, and MCP.
|
|
60
|
+
|
|
61
|
+
### Aggregate Query Failure Handling
|
|
62
|
+
|
|
63
|
+
If `dataQuery` fails, do not immediately switch to `dataSourceQuery` and manually sum, count, group, or rank records.
|
|
64
|
+
|
|
65
|
+
Before falling back to raw records, inspect the tool error and retry `dataQuery` with corrected parameters. Date filters are the most common source of aggregate query failures, so check them first. Common fixes include:
|
|
66
|
+
|
|
67
|
+
- rebuild date ranges with the frontend date filter contract below, such as `$dateOn`, `$dateBetween`, or relative period objects
|
|
68
|
+
- replace unsupported calendar operators such as `$gte`, `$gt`, `$lte`, `$lt`, or custom date operator names
|
|
69
|
+
- avoid UTC boundary expansions like `2026-04-01T00:00:00.000Z` to `2026-05-01T00:00:00.000Z` unless the user explicitly asks for exact timestamp comparison
|
|
70
|
+
- verify field names, relation paths, and data types with metadata
|
|
71
|
+
- correct `measures`, `dimensions`, aliases, and `orders`
|
|
72
|
+
- fix `filter` versus `having` placement
|
|
73
|
+
- simplify grouping, relation paths, or post-aggregation filters
|
|
74
|
+
- confirm the intended `dataSource` and `collectionName`
|
|
75
|
+
|
|
76
|
+
For aggregation/statistics/rankings/trends, raw record fetching plus manual calculation is a last resort only. Use `dataSourceQuery` as a fallback only when:
|
|
77
|
+
|
|
78
|
+
- the user explicitly asks to inspect raw records
|
|
79
|
+
- the requested computation cannot be expressed with `dataQuery`
|
|
80
|
+
- boundary-sensitive verification requires a small sample of source records
|
|
81
|
+
- at least two corrected `dataQuery` attempts have failed and the error has been analyzed
|
|
82
|
+
|
|
83
|
+
When a raw-record fallback is unavoidable, explain why `dataQuery` could not be used, keep the fetched record set small, and do not fetch large datasets just to manually aggregate them.
|
|
84
|
+
|
|
85
|
+
## Count Records
|
|
86
|
+
|
|
87
|
+
Use `dataSourceCounting` only for a simple total when grouped output is unnecessary.
|
|
88
|
+
|
|
89
|
+
## Query Construction Rules
|
|
90
|
+
|
|
91
|
+
1. `filter` is applied before aggregation.
|
|
92
|
+
2. `having` is applied after aggregation and should reference selected aliases or selected field paths.
|
|
93
|
+
3. For grouped results, put grouping fields in `dimensions`.
|
|
94
|
+
4. For metrics, put aggregate definitions in `measures`.
|
|
95
|
+
5. Use aliases when the user clearly needs stable output keys.
|
|
96
|
+
6. For dotted relation fields, prefer the exact field path confirmed from metadata, such as `createdBy.nickname`.
|
|
97
|
+
7. Default row limit is 50 and the tool caps the limit at 100.
|
|
98
|
+
8. Always follow the same frontend date filter contract used by NocoBase filters.
|
|
99
|
+
8.1. `filter` and `having` must be structured objects, not JSON-encoded strings.
|
|
100
|
+
9. Supported date operators are exactly `$dateOn`, `$dateNotOn`, `$dateBefore`, `$dateAfter`, `$dateNotBefore`, `$dateNotAfter`, `$dateBetween`, `$empty`, and `$notEmpty`.
|
|
101
|
+
10. For calendar-style date filtering, do not generate `$gte`, `$gt`, `$lte`, `$lt`, or custom operator names.
|
|
102
|
+
11. Allowed value shapes are:
|
|
103
|
+
- for `$dateOn`, `$dateNotOn`, `$dateBefore`, `$dateAfter`, `$dateNotBefore`, `$dateNotAfter`: `YYYY-MM-DD`, `YYYY-MM`, `YYYY`, a relative period object, or an exact datetime string only when the user explicitly wants timestamp comparison
|
|
104
|
+
- for `$dateBetween`: `["YYYY-MM-DD", "YYYY-MM-DD"]` or a relative period object
|
|
105
|
+
- for `$empty` and `$notEmpty`: no value
|
|
106
|
+
12. Relative period objects must use exactly these `type` values: `today`, `yesterday`, `tomorrow`, `thisWeek`, `lastWeek`, `nextWeek`, `thisMonth`, `lastMonth`, `nextMonth`, `thisQuarter`, `lastQuarter`, `nextQuarter`, `thisYear`, `lastYear`, `nextYear`, `past`, `next`.
|
|
107
|
+
13. If `type` is `past` or `next`, the object must also include `number` as a positive integer and `unit` as one of `day`, `week`, `month`, `quarter`, `year`.
|
|
108
|
+
14. For day, week, month, quarter, year, and common relative-period queries, prefer frontend date filters such as `{ createdAt: { $dateOn: "2026-04" } }`, `{ createdAt: { $dateOn: { type: "thisMonth" } } }`, or `{ createdAt: { $dateBetween: ["2026-04-01", "2026-04-30"] } }`.
|
|
109
|
+
15. Do not expand calendar queries into UTC boundary expressions such as `createdAt >= 2026-04-01T00:00:00.000Z` and `< 2026-05-01T00:00:00.000Z`.
|
|
110
|
+
16. For fields such as `createdAt` and `updatedAt`, still prefer the frontend date operators above for calendar queries instead of UTC boundary expansion.
|
|
111
|
+
17. Only inspect field type when the user explicitly asks for an exact timestamp comparison rather than a calendar period.
|
|
112
|
+
18. If an exact timestamp comparison is required, keep the operator frontend-compatible and choose the value format that matches the field semantics:
|
|
113
|
+
- timezone-aware datetime fields: ISO 8601 timestamp strings such as `2026-04-10T12:00:00.000Z`
|
|
114
|
+
- `datetimeNoTz` fields: timezone-free local datetime strings such as `2026-04-10 12:00:00`
|
|
115
|
+
- `dateOnly` fields: date-only strings without time components
|
|
116
|
+
19. Do not provide a timezone parameter yourself. The runtime request timezone is already supplied by the system.
|
|
117
|
+
|
|
118
|
+
# Available Tools
|
|
119
|
+
|
|
120
|
+
- `getSkill`: Load the `data-metadata` skill before metadata inspection so its schema exploration tools become available in the current conversation.
|
|
121
|
+
- `dataSourceQuery`: Query data from a specified collection in a data source. Supports filtering, sorting, field selection, and pagination. Returns paged results with total count.
|
|
122
|
+
- `dataQuery`: Run aggregate repository queries with measures, dimensions, orders, filter, and having.
|
|
123
|
+
- `dataSourceCounting`: Get the total count of records matching the specified filter conditions in a collection.
|
|
124
|
+
|
|
125
|
+
# Aggregate Query Parameters
|
|
126
|
+
|
|
127
|
+
| Parameter | Type | Description |
|
|
128
|
+
| ---------------- | -------- | ------------------------------------------------------------ |
|
|
129
|
+
| `dataSource` | string | The data source key (default: `main`) |
|
|
130
|
+
| `collectionName` | string | The collection name to query |
|
|
131
|
+
| `measures` | array | Aggregate definitions, such as count / sum / avg |
|
|
132
|
+
| `dimensions` | array | Group-by field definitions |
|
|
133
|
+
| `orders` | array | Result ordering definitions |
|
|
134
|
+
| `filter` | object | Query conditions applied before aggregation |
|
|
135
|
+
| `having` | object | Query conditions applied after aggregation |
|
|
136
|
+
| `offset` | number | Number of rows to skip |
|
|
137
|
+
| `limit` | number | Maximum number of rows to return (default: 50, max: 100) |
|
|
138
|
+
|
|
139
|
+
# Filter Operators
|
|
140
|
+
|
|
141
|
+
| Operator | Description | Example |
|
|
142
|
+
| --------- | --------------------- | -------------------------------------------- |
|
|
143
|
+
| `$eq` | Equal to | `{ status: { $eq: 'active' } }` |
|
|
144
|
+
| `$ne` | Not equal to | `{ status: { $ne: 'deleted' } }` |
|
|
145
|
+
| `$gt` | Greater than | `{ age: { $gt: 18 } }` |
|
|
146
|
+
| `$gte` | Greater than or equal | `{ age: { $gte: 18 } }` |
|
|
147
|
+
| `$lt` | Less than | `{ age: { $lt: 65 } }` |
|
|
148
|
+
| `$lte` | Less than or equal | `{ age: { $lte: 65 } }` |
|
|
149
|
+
| `$like` | Contains (SQL LIKE) | `{ name: { $like: '%John%' } }` |
|
|
150
|
+
| `$in` | In array | `{ status: { $in: ['active', 'pending'] } }` |
|
|
151
|
+
| `$nin` | Not in array | `{ status: { $nin: ['deleted'] } }` |
|
|
152
|
+
| `$exists` | Field exists | `{ email: { $exists: true } }` |
|
|
153
|
+
|
|
154
|
+
# Complex Filter Examples
|
|
155
|
+
|
|
156
|
+
## AND Conditions
|
|
157
|
+
```
|
|
158
|
+
{
|
|
159
|
+
$and: [
|
|
160
|
+
{ age: { $gte: 18 } },
|
|
161
|
+
{ status: { $eq: 'active' } }
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## OR Conditions
|
|
167
|
+
```
|
|
168
|
+
{
|
|
169
|
+
$or: [
|
|
170
|
+
{ name: { $like: '%John%' } },
|
|
171
|
+
{ email: { $like: '%john@%' } }
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Nested Conditions
|
|
177
|
+
```
|
|
178
|
+
{
|
|
179
|
+
$and: [
|
|
180
|
+
{ age: { $gte: 18 } },
|
|
181
|
+
{
|
|
182
|
+
$or: [
|
|
183
|
+
{ status: { $eq: 'active' } },
|
|
184
|
+
{ role: { $eq: 'admin' } }
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
# Common Use Cases
|
|
192
|
+
|
|
193
|
+
## Simple Query
|
|
194
|
+
```
|
|
195
|
+
User: "Show me all users"
|
|
196
|
+
Action: Call dataSourceQuery with collectionName="users"
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Aggregate Count
|
|
200
|
+
```
|
|
201
|
+
User: "How many active users are there?"
|
|
202
|
+
Action: Call dataQuery with collectionName="users", measures=[{ field: "id", aggregation: "count", alias: "count" }], filter={ status: { $eq: "active" } }
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Grouped Statistics
|
|
206
|
+
```
|
|
207
|
+
User: "Count orders by status"
|
|
208
|
+
Action: Call dataQuery with collectionName="orders", dimensions=[{ field: "status", alias: "status" }], measures=[{ field: "id", aggregation: "count", alias: "count" }]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Having Query
|
|
212
|
+
```
|
|
213
|
+
User: "Show statuses with more than 10 orders"
|
|
214
|
+
Action: Call dataQuery with collectionName="orders", dimensions=[{ field: "status", alias: "status" }], measures=[{ field: "id", aggregation: "count", alias: "count" }], having={ count: { $gt: 10 } }
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Raw Record Query
|
|
218
|
+
```
|
|
219
|
+
User: "Show me 20 latest paid orders"
|
|
220
|
+
Action: Call dataSourceQuery with collectionName="orders", filter={ status: { $eq: "paid" } }, sort=["-createdAt"], limit=20
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Count Records
|
|
224
|
+
```
|
|
225
|
+
User: "How many active users are there?"
|
|
226
|
+
Action: Call dataSourceCounting with collectionName="users", filter={ status: { $eq: 'active' } }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Metadata First
|
|
230
|
+
```
|
|
231
|
+
User: "Show monthly revenue by salesperson"
|
|
232
|
+
Action:
|
|
233
|
+
1. Call getSkill with skillName="data-metadata".
|
|
234
|
+
2. Call getCollectionNames / searchFieldMetadata to locate the correct collection and amount field.
|
|
235
|
+
3. Call getCollectionMetadata if date or relation paths are unclear.
|
|
236
|
+
4. Call dataQuery with the confirmed fields.
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
# Notes
|
|
240
|
+
|
|
241
|
+
- Always validate collection and field names before querying.
|
|
242
|
+
- Prefer metadata tools first when the request is ambiguous.
|
|
243
|
+
- Prefer `dataQuery` for analysis and metrics. If it fails, first check whether the date range or date operator is invalid, then retry corrected aggregate queries before using raw records.
|
|
244
|
+
- Use `dataSourceQuery` for raw rows and `dataSourceCounting` for the simplest count case.
|
|
245
|
+
- Respect user permissions; if the tool returns `No permissions`, explain that the current role cannot access the requested data.
|
|
@@ -0,0 +1,282 @@
|
|
|
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 __create = Object.create;
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
15
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
22
|
+
for (let key of __getOwnPropNames(from))
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
24
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
29
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
30
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
31
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
32
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
33
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
34
|
+
mod
|
|
35
|
+
));
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
var dataQuery_exports = {};
|
|
38
|
+
__export(dataQuery_exports, {
|
|
39
|
+
default: () => dataQuery_default
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(dataQuery_exports);
|
|
42
|
+
var import_acl = require("@nocobase/acl");
|
|
43
|
+
var import_ai = require("@nocobase/ai");
|
|
44
|
+
var import_plugin_acl = require("@nocobase/plugin-acl");
|
|
45
|
+
var import_utils = require("../../../common/utils");
|
|
46
|
+
var import_package = __toESM(require("../../../../../package.json"));
|
|
47
|
+
const queryFieldSchema = {
|
|
48
|
+
anyOf: [
|
|
49
|
+
{ type: "string" },
|
|
50
|
+
{
|
|
51
|
+
type: "array",
|
|
52
|
+
items: { type: "string" },
|
|
53
|
+
minItems: 1
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
const queryMeasureSchema = {
|
|
58
|
+
type: "object",
|
|
59
|
+
additionalProperties: true,
|
|
60
|
+
required: ["field"],
|
|
61
|
+
properties: {
|
|
62
|
+
field: queryFieldSchema,
|
|
63
|
+
type: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "Optional field type hint."
|
|
66
|
+
},
|
|
67
|
+
aggregation: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "Aggregation function, such as count, sum, avg, max, or min."
|
|
70
|
+
},
|
|
71
|
+
alias: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Output field alias."
|
|
74
|
+
},
|
|
75
|
+
distinct: {
|
|
76
|
+
type: "boolean",
|
|
77
|
+
description: "Whether to apply distinct before aggregation."
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const queryDimensionSchema = {
|
|
82
|
+
type: "object",
|
|
83
|
+
additionalProperties: true,
|
|
84
|
+
required: ["field"],
|
|
85
|
+
properties: {
|
|
86
|
+
field: queryFieldSchema,
|
|
87
|
+
type: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Optional field type hint."
|
|
90
|
+
},
|
|
91
|
+
alias: {
|
|
92
|
+
type: "string",
|
|
93
|
+
description: "Output field alias."
|
|
94
|
+
},
|
|
95
|
+
format: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Optional output format, usually for date and time dimensions."
|
|
98
|
+
},
|
|
99
|
+
options: {
|
|
100
|
+
type: "object",
|
|
101
|
+
description: "Additional formatter options.",
|
|
102
|
+
additionalProperties: true
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const queryOrderSchema = {
|
|
107
|
+
type: "object",
|
|
108
|
+
additionalProperties: true,
|
|
109
|
+
required: ["field"],
|
|
110
|
+
properties: {
|
|
111
|
+
field: queryFieldSchema,
|
|
112
|
+
alias: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Alias to sort by when the selected field is projected with an alias."
|
|
115
|
+
},
|
|
116
|
+
order: {
|
|
117
|
+
type: "string",
|
|
118
|
+
enum: ["asc", "desc"],
|
|
119
|
+
description: "Sort direction."
|
|
120
|
+
},
|
|
121
|
+
nulls: {
|
|
122
|
+
type: "string",
|
|
123
|
+
enum: ["default", "first", "last"],
|
|
124
|
+
description: "Null value ordering."
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const AggregateQuerySchema = {
|
|
129
|
+
type: "object",
|
|
130
|
+
additionalProperties: false,
|
|
131
|
+
required: ["collectionName"],
|
|
132
|
+
properties: {
|
|
133
|
+
dataSource: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "Data source key. Defaults to main."
|
|
136
|
+
},
|
|
137
|
+
datasource: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Legacy alias of dataSource. Defaults to main."
|
|
140
|
+
},
|
|
141
|
+
collectionName: {
|
|
142
|
+
type: "string",
|
|
143
|
+
description: "Collection name to query."
|
|
144
|
+
},
|
|
145
|
+
measures: {
|
|
146
|
+
type: "array",
|
|
147
|
+
description: "Measure definitions for aggregate query.",
|
|
148
|
+
items: queryMeasureSchema
|
|
149
|
+
},
|
|
150
|
+
dimensions: {
|
|
151
|
+
type: "array",
|
|
152
|
+
description: "Dimension definitions for grouped query.",
|
|
153
|
+
items: queryDimensionSchema
|
|
154
|
+
},
|
|
155
|
+
orders: {
|
|
156
|
+
type: "array",
|
|
157
|
+
description: "Order definitions for aggregate query.",
|
|
158
|
+
items: queryOrderSchema
|
|
159
|
+
},
|
|
160
|
+
filter: {
|
|
161
|
+
type: "object",
|
|
162
|
+
description: 'Filter object applied before aggregation. Pass a structured object, not a JSON string. Follow the frontend NocoBase date filter contract: use only $dateOn, $dateNotOn, $dateBefore, $dateAfter, $dateNotBefore, $dateNotAfter, $dateBetween, $empty, and $notEmpty for calendar-style date filtering; prefer YYYY-MM-DD, YYYY-MM, YYYY, relative period objects, or ["start","end"] date ranges; do not expand month/day queries into UTC boundary timestamps.',
|
|
163
|
+
additionalProperties: true
|
|
164
|
+
},
|
|
165
|
+
having: {
|
|
166
|
+
type: "object",
|
|
167
|
+
description: "Having object applied after grouping. Pass a structured object, not a JSON string. Reference selected measure aliases or selected field paths here, for example { count: { $gt: 10 } }.",
|
|
168
|
+
additionalProperties: true
|
|
169
|
+
},
|
|
170
|
+
offset: {
|
|
171
|
+
type: "number",
|
|
172
|
+
description: "Offset for query result rows."
|
|
173
|
+
},
|
|
174
|
+
limit: {
|
|
175
|
+
type: "number",
|
|
176
|
+
description: `Maximum number of rows to return. Defaults to 50 and is capped at ${import_utils.MAX_QUERY_LIMIT}.`
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
function getDataSourceKey(args) {
|
|
181
|
+
return args.dataSource || args.datasource || "main";
|
|
182
|
+
}
|
|
183
|
+
function getTimezone(ctx, args) {
|
|
184
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
185
|
+
const value = ((_a = ctx.get) == null ? void 0 : _a.call(ctx, "x-timezone")) || ((_c = (_b = ctx.request) == null ? void 0 : _b.get) == null ? void 0 : _c.call(_b, "x-timezone")) || ((_e = (_d = ctx.request) == null ? void 0 : _d.header) == null ? void 0 : _e["x-timezone"]) || ((_g = (_f = ctx.req) == null ? void 0 : _f.headers) == null ? void 0 : _g["x-timezone"]);
|
|
186
|
+
return Array.isArray(value) ? value[0] : value;
|
|
187
|
+
}
|
|
188
|
+
var dataQuery_default = (0, import_ai.defineTools)({
|
|
189
|
+
scope: "SPECIFIED",
|
|
190
|
+
defaultPermission: "ALLOW",
|
|
191
|
+
introduction: {
|
|
192
|
+
title: `{{t("ai.tools.dataQuery.title", { ns: "${import_package.default.name}" })}}`,
|
|
193
|
+
about: `{{t("ai.tools.dataQuery.about", { ns: "${import_package.default.name}" })}}`
|
|
194
|
+
},
|
|
195
|
+
definition: {
|
|
196
|
+
name: "dataQuery",
|
|
197
|
+
description: "Run the repository query action on a collection with measures, dimensions, orders, filter, and having.",
|
|
198
|
+
schema: AggregateQuerySchema
|
|
199
|
+
},
|
|
200
|
+
invoke: async (ctx, args) => {
|
|
201
|
+
var _a, _b, _c, _d, _e;
|
|
202
|
+
const filterError = (0, import_utils.getStructuredQueryArgError)("filter", args.filter);
|
|
203
|
+
if (filterError) {
|
|
204
|
+
return {
|
|
205
|
+
status: "error",
|
|
206
|
+
content: filterError
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const havingError = (0, import_utils.getStructuredQueryArgError)("having", args.having);
|
|
210
|
+
if (havingError) {
|
|
211
|
+
return {
|
|
212
|
+
status: "error",
|
|
213
|
+
content: havingError
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const dataSourceKey = getDataSourceKey(args);
|
|
217
|
+
const ds = ctx.app.dataSourceManager.get(dataSourceKey);
|
|
218
|
+
if (!ds) {
|
|
219
|
+
return {
|
|
220
|
+
status: "error",
|
|
221
|
+
content: `Data source "${dataSourceKey}" not found`
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const collection = ds.collectionManager.getCollection(args.collectionName);
|
|
225
|
+
if (!collection) {
|
|
226
|
+
return {
|
|
227
|
+
status: "error",
|
|
228
|
+
content: `Collection "${args.collectionName}" not found in data source "${dataSourceKey}"`
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const { limit, offset } = (0, import_utils.normalizeLimitOffset)(args, { defaultLimit: 50, maxLimit: import_utils.MAX_QUERY_LIMIT });
|
|
232
|
+
const timezone = getTimezone(ctx, args);
|
|
233
|
+
if (!((_a = args.measures) == null ? void 0 : _a.length) && !((_b = args.dimensions) == null ? void 0 : _b.length)) {
|
|
234
|
+
return {
|
|
235
|
+
status: "error",
|
|
236
|
+
content: "Aggregate query requires at least one measure or dimension"
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const db = ds.collectionManager.db || ctx.db;
|
|
241
|
+
const result = await (0, import_plugin_acl.applyQueryPermission)({
|
|
242
|
+
acl: ds.acl || ctx.app.acl,
|
|
243
|
+
db,
|
|
244
|
+
resourceName: args.collectionName,
|
|
245
|
+
query: {
|
|
246
|
+
measures: args.measures,
|
|
247
|
+
dimensions: args.dimensions,
|
|
248
|
+
orders: args.orders,
|
|
249
|
+
filter: args.filter,
|
|
250
|
+
having: args.having,
|
|
251
|
+
limit,
|
|
252
|
+
offset
|
|
253
|
+
},
|
|
254
|
+
currentUser: (_c = ctx.state) == null ? void 0 : _c.currentUser,
|
|
255
|
+
currentRole: (_d = ctx.state) == null ? void 0 : _d.currentRole,
|
|
256
|
+
currentRoles: (_e = ctx.state) == null ? void 0 : _e.currentRoles,
|
|
257
|
+
timezone,
|
|
258
|
+
state: ctx.state
|
|
259
|
+
});
|
|
260
|
+
const queryResult = await db.getRepository(args.collectionName).query({
|
|
261
|
+
...result.query,
|
|
262
|
+
context: ctx,
|
|
263
|
+
timezone
|
|
264
|
+
});
|
|
265
|
+
return {
|
|
266
|
+
status: "success",
|
|
267
|
+
content: JSON.stringify((0, import_utils.truncateLongStrings)(queryResult))
|
|
268
|
+
};
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (error instanceof import_acl.NoPermissionError) {
|
|
271
|
+
return {
|
|
272
|
+
status: "error",
|
|
273
|
+
content: "No permissions"
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
status: "error",
|
|
278
|
+
content: (error == null ? void 0 : error.message) || "Aggregate query failed"
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
});
|
|
@@ -40,10 +40,11 @@ __export(dataSourceCounting_exports, {
|
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(dataSourceCounting_exports);
|
|
42
42
|
var import_ai = require("@nocobase/ai");
|
|
43
|
-
var import_common = require("
|
|
44
|
-
var
|
|
43
|
+
var import_common = require("../../../common/common");
|
|
44
|
+
var import_utils = require("../../../common/utils");
|
|
45
|
+
var import_package = __toESM(require("../../../../../package.json"));
|
|
45
46
|
var dataSourceCounting_default = (0, import_ai.defineTools)({
|
|
46
|
-
scope: "
|
|
47
|
+
scope: "SPECIFIED",
|
|
47
48
|
defaultPermission: "ALLOW",
|
|
48
49
|
introduction: {
|
|
49
50
|
title: `{{t("ai.tools.dataSourceCounting.title", { ns: "${import_package.default.name}" })}}`,
|
|
@@ -51,10 +52,17 @@ var dataSourceCounting_default = (0, import_ai.defineTools)({
|
|
|
51
52
|
},
|
|
52
53
|
definition: {
|
|
53
54
|
name: "dataSourceCounting",
|
|
54
|
-
description: "Use dataSource, collectionName, and collection fields to query data from the database, get total count of records",
|
|
55
|
+
description: "Use dataSource, collectionName, and collection fields to query data from the database, get total count of records. The filter argument must be a structured object, not a JSON string.",
|
|
55
56
|
schema: import_common.ArgSchema
|
|
56
57
|
},
|
|
57
58
|
invoke: async (ctx, args) => {
|
|
59
|
+
const filterError = (0, import_utils.getStructuredQueryArgError)("filter", args.filter);
|
|
60
|
+
if (filterError) {
|
|
61
|
+
return {
|
|
62
|
+
status: "error",
|
|
63
|
+
content: filterError
|
|
64
|
+
};
|
|
65
|
+
}
|
|
58
66
|
const plugin = ctx.app.pm.get("ai");
|
|
59
67
|
const content = await plugin.aiContextDatasourceManager.query(ctx, { ...args, offset: 0, limit: 1 });
|
|
60
68
|
return {
|
|
@@ -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
|
+
declare const _default: import("@nocobase/ai").ToolsOptions;
|
|
10
|
+
export default _default;
|
|
@@ -40,11 +40,11 @@ __export(dataSourceQuery_exports, {
|
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(dataSourceQuery_exports);
|
|
42
42
|
var import_ai = require("@nocobase/ai");
|
|
43
|
-
var import_common = require("
|
|
44
|
-
var import_utils = require("
|
|
45
|
-
var import_package = __toESM(require("
|
|
43
|
+
var import_common = require("../../../common/common");
|
|
44
|
+
var import_utils = require("../../../common/utils");
|
|
45
|
+
var import_package = __toESM(require("../../../../../package.json"));
|
|
46
46
|
var dataSourceQuery_default = (0, import_ai.defineTools)({
|
|
47
|
-
scope: "
|
|
47
|
+
scope: "SPECIFIED",
|
|
48
48
|
defaultPermission: "ALLOW",
|
|
49
49
|
introduction: {
|
|
50
50
|
title: `{{t("ai.tools.dataSourceQuery.title", { ns: "${import_package.default.name}" })}}`,
|
|
@@ -52,10 +52,17 @@ var dataSourceQuery_default = (0, import_ai.defineTools)({
|
|
|
52
52
|
},
|
|
53
53
|
definition: {
|
|
54
54
|
name: "dataSourceQuery",
|
|
55
|
-
description: "Use dataSource, collectionName, and collection fields to query data from the database",
|
|
55
|
+
description: "Use dataSource, collectionName, and collection fields to query data from the database. The filter argument must be a structured object, not a JSON string.",
|
|
56
56
|
schema: import_common.ArgSchema
|
|
57
57
|
},
|
|
58
58
|
invoke: async (ctx, args) => {
|
|
59
|
+
const filterError = (0, import_utils.getStructuredQueryArgError)("filter", args.filter);
|
|
60
|
+
if (filterError) {
|
|
61
|
+
return {
|
|
62
|
+
status: "error",
|
|
63
|
+
content: filterError
|
|
64
|
+
};
|
|
65
|
+
}
|
|
59
66
|
const plugin = ctx.app.pm.get("ai");
|
|
60
67
|
const { limit, offset } = (0, import_utils.normalizeLimitOffset)(args, { defaultLimit: 50, maxLimit: import_utils.MAX_QUERY_LIMIT });
|
|
61
68
|
const content = await plugin.aiContextDatasourceManager.query(ctx, {
|