@objectstack/service-ai 4.0.1 → 4.0.3
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/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +17 -0
- package/dist/index.cjs +1632 -355
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +330 -87
- package/dist/index.d.ts +330 -87
- package/dist/index.js +1623 -352
- package/dist/index.js.map +1 -1
- package/package.json +27 -5
- package/src/__tests__/ai-service.test.ts +260 -27
- package/src/__tests__/auth-and-toolcalling.test.ts +81 -29
- package/src/__tests__/chatbot-features.test.ts +397 -102
- package/src/__tests__/metadata-tools.test.ts +970 -0
- package/src/__tests__/objectql-conversation-service.test.ts +34 -16
- package/src/__tests__/tool-routes.test.ts +191 -0
- package/src/__tests__/vercel-stream-encoder.test.ts +310 -0
- package/src/adapters/index.ts +2 -0
- package/src/adapters/memory-adapter.ts +17 -9
- package/src/adapters/vercel-adapter.ts +148 -0
- package/src/agent-runtime.ts +27 -3
- package/src/agents/index.ts +1 -0
- package/src/agents/metadata-assistant-agent.ts +87 -0
- package/src/ai-service.ts +75 -36
- package/src/conversation/in-memory-conversation-service.ts +2 -2
- package/src/conversation/objectql-conversation-service.ts +67 -18
- package/src/index.ts +22 -2
- package/src/plugin.ts +237 -30
- package/src/routes/agent-routes.ts +68 -12
- package/src/routes/ai-routes.ts +93 -14
- package/src/routes/index.ts +1 -0
- package/src/routes/message-utils.ts +90 -0
- package/src/routes/tool-routes.ts +142 -0
- package/src/stream/index.ts +3 -0
- package/src/stream/vercel-stream-encoder.ts +153 -0
- package/src/tools/add-field.tool.ts +70 -0
- package/src/tools/create-object.tool.ts +66 -0
- package/src/tools/data-tools.ts +4 -101
- package/src/tools/delete-field.tool.ts +38 -0
- package/src/tools/describe-object.tool.ts +31 -0
- package/src/tools/index.ts +12 -1
- package/src/tools/list-objects.tool.ts +34 -0
- package/src/tools/metadata-tools.ts +430 -0
- package/src/tools/modify-field.tool.ts +44 -0
- package/src/tools/tool-registry.ts +32 -9
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { defineTool } from '@objectstack/spec/ai';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* create_object — AI Tool Metadata
|
|
7
|
+
*
|
|
8
|
+
* Creates a new data object (table) with schema validation.
|
|
9
|
+
* Validates snake_case naming for object and initial fields,
|
|
10
|
+
* checks for duplicates, and registers the object definition.
|
|
11
|
+
*/
|
|
12
|
+
export const createObjectTool = defineTool({
|
|
13
|
+
name: 'create_object',
|
|
14
|
+
label: 'Create Object',
|
|
15
|
+
description:
|
|
16
|
+
'Creates a new data object (table) with the specified name, label, and optional field definitions. ' +
|
|
17
|
+
'Use this when the user wants to create a new entity, table, or data model.',
|
|
18
|
+
category: 'data',
|
|
19
|
+
builtIn: true,
|
|
20
|
+
// NOTE: requiresConfirmation is intentionally false (default) because the
|
|
21
|
+
// server-side tool-call loop in AIService.chatWithTools/streamChatWithTools
|
|
22
|
+
// executes tool calls immediately without checking this flag. The flag
|
|
23
|
+
// should only be set once server-side approval gating is implemented to
|
|
24
|
+
// avoid giving users a false sense of safety.
|
|
25
|
+
parameters: {
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: {
|
|
28
|
+
name: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Machine name for the object (snake_case, e.g. project_task)',
|
|
31
|
+
},
|
|
32
|
+
label: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Human-readable display name (e.g. Project Task)',
|
|
35
|
+
},
|
|
36
|
+
fields: {
|
|
37
|
+
type: 'array',
|
|
38
|
+
description: 'Initial fields to create with the object',
|
|
39
|
+
items: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
name: { type: 'string', description: 'Field machine name (snake_case)' },
|
|
43
|
+
label: { type: 'string', description: 'Field display name' },
|
|
44
|
+
type: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Field data type',
|
|
47
|
+
enum: ['text', 'textarea', 'number', 'boolean', 'date', 'datetime', 'select', 'lookup', 'formula', 'autonumber'],
|
|
48
|
+
},
|
|
49
|
+
required: { type: 'boolean', description: 'Whether the field is required' },
|
|
50
|
+
},
|
|
51
|
+
required: ['name', 'type'],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
enableFeatures: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
description: 'Object capability flags',
|
|
57
|
+
properties: {
|
|
58
|
+
trackHistory: { type: 'boolean' },
|
|
59
|
+
apiEnabled: { type: 'boolean' },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ['name', 'label'],
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
});
|
package/src/tools/data-tools.ts
CHANGED
|
@@ -1,30 +1,9 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
|
|
3
|
-
import type { AIToolDefinition, IDataEngine
|
|
3
|
+
import type { AIToolDefinition, IDataEngine } from '@objectstack/spec/contracts';
|
|
4
4
|
import type { ToolHandler } from './tool-registry.js';
|
|
5
5
|
import type { ToolRegistry } from './tool-registry.js';
|
|
6
6
|
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
// Internal type aliases for metadata payloads (returned as `unknown` from
|
|
9
|
-
// IMetadataService — we cast to these lightweight shapes for field access).
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
|
|
12
|
-
/** Minimal shape of an object definition as returned by IMetadataService. */
|
|
13
|
-
interface ObjectDef {
|
|
14
|
-
name: string;
|
|
15
|
-
label?: string;
|
|
16
|
-
fields?: Record<string, FieldDef>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Minimal shape of a field definition inside an object. */
|
|
20
|
-
interface FieldDef {
|
|
21
|
-
type?: string;
|
|
22
|
-
label?: string;
|
|
23
|
-
required?: boolean;
|
|
24
|
-
reference?: string;
|
|
25
|
-
options?: unknown;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
7
|
// ---------------------------------------------------------------------------
|
|
29
8
|
// Data context — injected once at registration time
|
|
30
9
|
// ---------------------------------------------------------------------------
|
|
@@ -38,8 +17,6 @@ interface FieldDef {
|
|
|
38
17
|
export interface DataToolContext {
|
|
39
18
|
/** ObjectQL data engine for record-level operations. */
|
|
40
19
|
dataEngine: IDataEngine;
|
|
41
|
-
/** Metadata service for schema/object introspection. */
|
|
42
|
-
metadataService: IMetadataService;
|
|
43
20
|
}
|
|
44
21
|
|
|
45
22
|
// ---------------------------------------------------------------------------
|
|
@@ -52,34 +29,6 @@ const MAX_QUERY_LIMIT = 200;
|
|
|
52
29
|
/** Default record limit when not specified. */
|
|
53
30
|
const DEFAULT_QUERY_LIMIT = 20;
|
|
54
31
|
|
|
55
|
-
export const LIST_OBJECTS_TOOL: AIToolDefinition = {
|
|
56
|
-
name: 'list_objects',
|
|
57
|
-
description: 'List all available data objects (tables) in the system. Returns object names and labels.',
|
|
58
|
-
parameters: {
|
|
59
|
-
type: 'object',
|
|
60
|
-
properties: {},
|
|
61
|
-
additionalProperties: false,
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export const DESCRIBE_OBJECT_TOOL: AIToolDefinition = {
|
|
66
|
-
name: 'describe_object',
|
|
67
|
-
description:
|
|
68
|
-
'Get the schema (fields, types, labels) of a specific data object. ' +
|
|
69
|
-
'Use this to understand the structure of a table before querying it.',
|
|
70
|
-
parameters: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
objectName: {
|
|
74
|
-
type: 'string',
|
|
75
|
-
description: 'The snake_case name of the object to describe',
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
required: ['objectName'],
|
|
79
|
-
additionalProperties: false,
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
32
|
export const QUERY_RECORDS_TOOL: AIToolDefinition = {
|
|
84
33
|
name: 'query_records',
|
|
85
34
|
description:
|
|
@@ -203,10 +152,8 @@ export const AGGREGATE_DATA_TOOL: AIToolDefinition = {
|
|
|
203
152
|
},
|
|
204
153
|
};
|
|
205
154
|
|
|
206
|
-
/** All built-in data
|
|
155
|
+
/** All built-in data tools definitions. */
|
|
207
156
|
export const DATA_TOOL_DEFINITIONS: AIToolDefinition[] = [
|
|
208
|
-
LIST_OBJECTS_TOOL,
|
|
209
|
-
DESCRIBE_OBJECT_TOOL,
|
|
210
157
|
QUERY_RECORDS_TOOL,
|
|
211
158
|
GET_RECORD_TOOL,
|
|
212
159
|
AGGREGATE_DATA_TOOL,
|
|
@@ -216,46 +163,6 @@ export const DATA_TOOL_DEFINITIONS: AIToolDefinition[] = [
|
|
|
216
163
|
// Handler Factories
|
|
217
164
|
// ---------------------------------------------------------------------------
|
|
218
165
|
|
|
219
|
-
function createListObjectsHandler(ctx: DataToolContext): ToolHandler {
|
|
220
|
-
return async () => {
|
|
221
|
-
const objects = await ctx.metadataService.listObjects();
|
|
222
|
-
const summary = (objects as ObjectDef[]).map(o => ({
|
|
223
|
-
name: o.name,
|
|
224
|
-
label: o.label ?? o.name,
|
|
225
|
-
}));
|
|
226
|
-
return JSON.stringify(summary);
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function createDescribeObjectHandler(ctx: DataToolContext): ToolHandler {
|
|
231
|
-
return async (args) => {
|
|
232
|
-
const { objectName } = args as { objectName: string };
|
|
233
|
-
const objectDef = await ctx.metadataService.getObject(objectName);
|
|
234
|
-
if (!objectDef) {
|
|
235
|
-
return JSON.stringify({ error: `Object "${objectName}" not found` });
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const def = objectDef as ObjectDef;
|
|
239
|
-
const fields = def.fields ?? {};
|
|
240
|
-
const fieldSummary: Record<string, Record<string, unknown>> = {};
|
|
241
|
-
for (const [key, f] of Object.entries(fields)) {
|
|
242
|
-
fieldSummary[key] = {
|
|
243
|
-
type: f.type,
|
|
244
|
-
label: f.label ?? key,
|
|
245
|
-
required: f.required ?? false,
|
|
246
|
-
...(f.reference ? { reference: f.reference } : {}),
|
|
247
|
-
...(f.options ? { options: f.options } : {}),
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return JSON.stringify({
|
|
252
|
-
name: def.name,
|
|
253
|
-
label: def.label ?? def.name,
|
|
254
|
-
fields: fieldSummary,
|
|
255
|
-
});
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
166
|
function createQueryRecordsHandler(ctx: DataToolContext): ToolHandler {
|
|
260
167
|
return async (args) => {
|
|
261
168
|
const {
|
|
@@ -366,15 +273,13 @@ function createAggregateDataHandler(ctx: DataToolContext): ToolHandler {
|
|
|
366
273
|
/**
|
|
367
274
|
* Register all built-in data tools on the given {@link ToolRegistry}.
|
|
368
275
|
*
|
|
369
|
-
* Typically called from the `ai:ready` hook after
|
|
370
|
-
* and metadata service are available.
|
|
276
|
+
* Typically called from the `ai:ready` hook after the data engine is available.
|
|
371
277
|
*
|
|
372
278
|
* @example
|
|
373
279
|
* ```ts
|
|
374
280
|
* ctx.hook('ai:ready', async (aiService) => {
|
|
375
281
|
* const dataEngine = ctx.getService<IDataEngine>('data');
|
|
376
|
-
*
|
|
377
|
-
* registerDataTools(aiService.toolRegistry, { dataEngine, metadataService });
|
|
282
|
+
* registerDataTools(aiService.toolRegistry, { dataEngine });
|
|
378
283
|
* });
|
|
379
284
|
* ```
|
|
380
285
|
*/
|
|
@@ -382,8 +287,6 @@ export function registerDataTools(
|
|
|
382
287
|
registry: ToolRegistry,
|
|
383
288
|
context: DataToolContext,
|
|
384
289
|
): void {
|
|
385
|
-
registry.register(LIST_OBJECTS_TOOL, createListObjectsHandler(context));
|
|
386
|
-
registry.register(DESCRIBE_OBJECT_TOOL, createDescribeObjectHandler(context));
|
|
387
290
|
registry.register(QUERY_RECORDS_TOOL, createQueryRecordsHandler(context));
|
|
388
291
|
registry.register(GET_RECORD_TOOL, createGetRecordHandler(context));
|
|
389
292
|
registry.register(AGGREGATE_DATA_TOOL, createAggregateDataHandler(context));
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { defineTool } from '@objectstack/spec/ai';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* delete_field — AI Tool Metadata
|
|
7
|
+
*
|
|
8
|
+
* Removes a field (column) from an existing data object.
|
|
9
|
+
* This is a destructive operation.
|
|
10
|
+
*/
|
|
11
|
+
export const deleteFieldTool = defineTool({
|
|
12
|
+
name: 'delete_field',
|
|
13
|
+
label: 'Delete Field',
|
|
14
|
+
description:
|
|
15
|
+
'Removes a field (column) from an existing data object. This is a destructive operation. ' +
|
|
16
|
+
'Use this when the user explicitly wants to remove an attribute or column from a table.',
|
|
17
|
+
category: 'data',
|
|
18
|
+
builtIn: true,
|
|
19
|
+
// NOTE: requiresConfirmation is intentionally false (default) because the
|
|
20
|
+
// server-side tool-call loop in AIService.chatWithTools/streamChatWithTools
|
|
21
|
+
// executes tool calls immediately without checking this flag. The flag
|
|
22
|
+
// should only be set once server-side approval gating is implemented.
|
|
23
|
+
parameters: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
objectName: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Target object machine name (snake_case)',
|
|
29
|
+
},
|
|
30
|
+
fieldName: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Field machine name to delete (snake_case)',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['objectName', 'fieldName'],
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { defineTool } from '@objectstack/spec/ai';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* describe_object — AI Tool Metadata
|
|
7
|
+
*
|
|
8
|
+
* Returns the full schema of a data object including all fields, types,
|
|
9
|
+
* relationships, and configuration. This is the single, unified tool for
|
|
10
|
+
* describing objects — used by both data_chat and metadata_assistant agents.
|
|
11
|
+
*/
|
|
12
|
+
export const describeObjectTool = defineTool({
|
|
13
|
+
name: 'describe_object',
|
|
14
|
+
label: 'Describe Object',
|
|
15
|
+
description:
|
|
16
|
+
'Returns the full schema details of a data object, including all fields, types, relationships, and configuration. ' +
|
|
17
|
+
'Use this to understand the structure of a table before querying or modifying it.',
|
|
18
|
+
category: 'data',
|
|
19
|
+
builtIn: true,
|
|
20
|
+
parameters: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
objectName: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Object machine name to describe (snake_case)',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ['objectName'],
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
},
|
|
31
|
+
});
|
package/src/tools/index.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
|
|
3
3
|
export { ToolRegistry } from './tool-registry.js';
|
|
4
|
-
export type { ToolHandler } from './tool-registry.js';
|
|
4
|
+
export type { ToolHandler, ToolExecutionResult } from './tool-registry.js';
|
|
5
5
|
|
|
6
6
|
export { registerDataTools, DATA_TOOL_DEFINITIONS } from './data-tools.js';
|
|
7
7
|
export type { DataToolContext } from './data-tools.js';
|
|
8
|
+
|
|
9
|
+
export { registerMetadataTools, METADATA_TOOL_DEFINITIONS } from './metadata-tools.js';
|
|
10
|
+
export type { MetadataToolContext } from './metadata-tools.js';
|
|
11
|
+
|
|
12
|
+
// Individual tool metadata exports
|
|
13
|
+
export { createObjectTool } from './create-object.tool.js';
|
|
14
|
+
export { addFieldTool } from './add-field.tool.js';
|
|
15
|
+
export { modifyFieldTool } from './modify-field.tool.js';
|
|
16
|
+
export { deleteFieldTool } from './delete-field.tool.js';
|
|
17
|
+
export { listObjectsTool } from './list-objects.tool.js';
|
|
18
|
+
export { describeObjectTool } from './describe-object.tool.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { defineTool } from '@objectstack/spec/ai';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* list_objects — AI Tool Metadata
|
|
7
|
+
*
|
|
8
|
+
* Lists all registered data objects (tables) with optional filtering
|
|
9
|
+
* and field summaries. This is the single, unified tool for listing
|
|
10
|
+
* objects — used by both data_chat and metadata_assistant agents.
|
|
11
|
+
*/
|
|
12
|
+
export const listObjectsTool = defineTool({
|
|
13
|
+
name: 'list_objects',
|
|
14
|
+
label: 'List Objects',
|
|
15
|
+
description:
|
|
16
|
+
'Lists all registered data objects (tables) in the current environment. ' +
|
|
17
|
+
'Use this when the user wants to see what tables, entities, or data models are available.',
|
|
18
|
+
category: 'data',
|
|
19
|
+
builtIn: true,
|
|
20
|
+
parameters: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
filter: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Optional name or label substring to filter objects',
|
|
26
|
+
},
|
|
27
|
+
includeFields: {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
description: 'Whether to include field summaries for each object (default: false)',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false,
|
|
33
|
+
},
|
|
34
|
+
});
|