@objectstack/service-ai 4.0.4 → 4.0.5
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/dist/index.cjs +1176 -135
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1225 -430
- package/dist/index.d.ts +1225 -430
- package/dist/index.js +1160 -128
- package/dist/index.js.map +1 -1
- package/package.json +35 -8
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -61
- package/src/__tests__/ai-service.test.ts +0 -981
- package/src/__tests__/auth-and-toolcalling.test.ts +0 -677
- package/src/__tests__/chatbot-features.test.ts +0 -1116
- package/src/__tests__/metadata-tools.test.ts +0 -970
- package/src/__tests__/objectql-conversation-service.test.ts +0 -382
- package/src/__tests__/tool-routes.test.ts +0 -191
- package/src/__tests__/vercel-stream-encoder.test.ts +0 -310
- package/src/adapters/index.ts +0 -6
- package/src/adapters/memory-adapter.ts +0 -72
- package/src/adapters/types.ts +0 -3
- package/src/adapters/vercel-adapter.ts +0 -148
- package/src/agent-runtime.ts +0 -154
- package/src/agents/data-chat-agent.ts +0 -79
- package/src/agents/index.ts +0 -4
- package/src/agents/metadata-assistant-agent.ts +0 -87
- package/src/ai-service.ts +0 -364
- package/src/conversation/in-memory-conversation-service.ts +0 -103
- package/src/conversation/index.ts +0 -4
- package/src/conversation/objectql-conversation-service.ts +0 -301
- package/src/index.ts +0 -60
- package/src/objects/ai-conversation.object.ts +0 -86
- package/src/objects/ai-message.object.ts +0 -86
- package/src/objects/index.ts +0 -10
- package/src/plugin.ts +0 -391
- package/src/routes/agent-routes.ts +0 -190
- package/src/routes/ai-routes.ts +0 -439
- package/src/routes/index.ts +0 -5
- package/src/routes/message-utils.ts +0 -90
- package/src/routes/tool-routes.ts +0 -142
- package/src/stream/index.ts +0 -3
- package/src/stream/vercel-stream-encoder.ts +0 -153
- package/src/tools/add-field.tool.ts +0 -70
- package/src/tools/create-object.tool.ts +0 -66
- package/src/tools/data-tools.ts +0 -293
- package/src/tools/delete-field.tool.ts +0 -38
- package/src/tools/describe-object.tool.ts +0 -31
- package/src/tools/index.ts +0 -18
- package/src/tools/list-objects.tool.ts +0 -34
- package/src/tools/metadata-tools.ts +0 -430
- package/src/tools/modify-field.tool.ts +0 -44
- package/src/tools/tool-registry.ts +0 -132
- package/tsconfig.json +0 -17
- package/vitest.config.ts +0 -23
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import type {
|
|
5
|
-
AIConversation,
|
|
6
|
-
ModelMessage,
|
|
7
|
-
ToolCallPart,
|
|
8
|
-
ToolResultPart,
|
|
9
|
-
IAIConversationService,
|
|
10
|
-
IDataEngine,
|
|
11
|
-
} from '@objectstack/spec/contracts';
|
|
12
|
-
|
|
13
|
-
/** Object names used for persistence. */
|
|
14
|
-
const CONVERSATIONS_OBJECT = 'ai_conversations';
|
|
15
|
-
const MESSAGES_OBJECT = 'ai_messages';
|
|
16
|
-
|
|
17
|
-
/** Database row shape for ai_conversations. */
|
|
18
|
-
interface DbConversationRow {
|
|
19
|
-
id: string;
|
|
20
|
-
title: string | null;
|
|
21
|
-
agent_id: string | null;
|
|
22
|
-
user_id: string | null;
|
|
23
|
-
metadata: string | null;
|
|
24
|
-
created_at: string;
|
|
25
|
-
updated_at: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Database row shape for ai_messages. */
|
|
29
|
-
interface DbMessageRow {
|
|
30
|
-
id: string;
|
|
31
|
-
conversation_id: string;
|
|
32
|
-
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
33
|
-
content: string;
|
|
34
|
-
tool_calls: string | null;
|
|
35
|
-
tool_call_id: string | null;
|
|
36
|
-
created_at: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Deterministic ordering for conversations (total order). */
|
|
40
|
-
const CONVERSATION_ORDER = [
|
|
41
|
-
{ field: 'created_at', order: 'asc' as const },
|
|
42
|
-
{ field: 'id', order: 'asc' as const },
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
/** Deterministic ordering for messages within a conversation. */
|
|
46
|
-
const MESSAGE_ORDER = [
|
|
47
|
-
{ field: 'created_at', order: 'asc' as const },
|
|
48
|
-
{ field: 'id', order: 'asc' as const },
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* ObjectQLConversationService — Persistent implementation of IAIConversationService.
|
|
53
|
-
*
|
|
54
|
-
* Delegates all storage to an {@link IDataEngine} instance, using the
|
|
55
|
-
* `ai_conversations` and `ai_messages` objects. This decouples the service
|
|
56
|
-
* from any specific database driver (Turso, Postgres, SQLite, etc.).
|
|
57
|
-
*
|
|
58
|
-
* Production environments should use this implementation to ensure
|
|
59
|
-
* conversation history survives service restarts.
|
|
60
|
-
*/
|
|
61
|
-
export class ObjectQLConversationService implements IAIConversationService {
|
|
62
|
-
private readonly engine: IDataEngine;
|
|
63
|
-
|
|
64
|
-
constructor(engine: IDataEngine) {
|
|
65
|
-
this.engine = engine;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async create(options: {
|
|
69
|
-
title?: string;
|
|
70
|
-
agentId?: string;
|
|
71
|
-
userId?: string;
|
|
72
|
-
metadata?: Record<string, unknown>;
|
|
73
|
-
} = {}): Promise<AIConversation> {
|
|
74
|
-
const now = new Date().toISOString();
|
|
75
|
-
const id = `conv_${randomUUID()}`;
|
|
76
|
-
|
|
77
|
-
const record = {
|
|
78
|
-
id,
|
|
79
|
-
title: options.title ?? null,
|
|
80
|
-
agent_id: options.agentId ?? null,
|
|
81
|
-
user_id: options.userId ?? null,
|
|
82
|
-
metadata: options.metadata ? JSON.stringify(options.metadata) : null,
|
|
83
|
-
created_at: now,
|
|
84
|
-
updated_at: now,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
await this.engine.insert(CONVERSATIONS_OBJECT, record);
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
id,
|
|
91
|
-
title: options.title,
|
|
92
|
-
agentId: options.agentId,
|
|
93
|
-
userId: options.userId,
|
|
94
|
-
messages: [],
|
|
95
|
-
createdAt: now,
|
|
96
|
-
updatedAt: now,
|
|
97
|
-
metadata: options.metadata,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
async get(conversationId: string): Promise<AIConversation | null> {
|
|
102
|
-
const row: DbConversationRow | null = await this.engine.findOne(CONVERSATIONS_OBJECT, {
|
|
103
|
-
where: { id: conversationId },
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
if (!row) return null;
|
|
107
|
-
|
|
108
|
-
const messages: DbMessageRow[] = await this.engine.find(MESSAGES_OBJECT, {
|
|
109
|
-
where: { conversation_id: conversationId },
|
|
110
|
-
orderBy: MESSAGE_ORDER,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
return this.toConversation(row, messages);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async list(options: {
|
|
117
|
-
userId?: string;
|
|
118
|
-
agentId?: string;
|
|
119
|
-
limit?: number;
|
|
120
|
-
cursor?: string;
|
|
121
|
-
} = {}): Promise<AIConversation[]> {
|
|
122
|
-
const where: Record<string, unknown> = {};
|
|
123
|
-
if (options.userId) where.user_id = options.userId;
|
|
124
|
-
if (options.agentId) where.agent_id = options.agentId;
|
|
125
|
-
|
|
126
|
-
// Stable cursor-based pagination using composite (created_at, id) order.
|
|
127
|
-
// This avoids skips/duplicates when multiple conversations share a timestamp.
|
|
128
|
-
if (options.cursor) {
|
|
129
|
-
const cursorRow = await this.engine.findOne(CONVERSATIONS_OBJECT, {
|
|
130
|
-
where: { id: options.cursor },
|
|
131
|
-
fields: ['created_at', 'id'],
|
|
132
|
-
});
|
|
133
|
-
if (cursorRow) {
|
|
134
|
-
where.$or = [
|
|
135
|
-
{ created_at: { $gt: cursorRow.created_at } },
|
|
136
|
-
{ created_at: cursorRow.created_at, id: { $gt: cursorRow.id } },
|
|
137
|
-
];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const rows: DbConversationRow[] = await this.engine.find(CONVERSATIONS_OBJECT, {
|
|
142
|
-
where: Object.keys(where).length > 0 ? where : undefined,
|
|
143
|
-
orderBy: CONVERSATION_ORDER,
|
|
144
|
-
limit: options.limit && options.limit > 0 ? options.limit : undefined,
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// Load messages per conversation in parallel.
|
|
148
|
-
// N+1 is bounded by the pagination limit; driver-agnostic $in is not guaranteed.
|
|
149
|
-
const conversations: AIConversation[] = await Promise.all(
|
|
150
|
-
rows.map(async (row) => {
|
|
151
|
-
const messages: DbMessageRow[] = await this.engine.find(MESSAGES_OBJECT, {
|
|
152
|
-
where: { conversation_id: row.id },
|
|
153
|
-
orderBy: MESSAGE_ORDER,
|
|
154
|
-
});
|
|
155
|
-
return this.toConversation(row, messages);
|
|
156
|
-
}),
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
return conversations;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async addMessage(conversationId: string, message: ModelMessage): Promise<AIConversation> {
|
|
163
|
-
// Verify conversation exists
|
|
164
|
-
const row: DbConversationRow | null = await this.engine.findOne(CONVERSATIONS_OBJECT, {
|
|
165
|
-
where: { id: conversationId },
|
|
166
|
-
});
|
|
167
|
-
if (!row) {
|
|
168
|
-
throw new Error(`Conversation "${conversationId}" not found`);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const now = new Date().toISOString();
|
|
172
|
-
const msgId = `msg_${randomUUID()}`;
|
|
173
|
-
|
|
174
|
-
// Extract flat fields from the discriminated union
|
|
175
|
-
let contentStr: string;
|
|
176
|
-
let toolCallsJson: string | null = null;
|
|
177
|
-
let toolCallId: string | null = null;
|
|
178
|
-
|
|
179
|
-
if (message.role === 'system' || message.role === 'user') {
|
|
180
|
-
contentStr = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
|
|
181
|
-
} else if (message.role === 'assistant') {
|
|
182
|
-
if (typeof message.content === 'string') {
|
|
183
|
-
contentStr = message.content;
|
|
184
|
-
} else {
|
|
185
|
-
const parts = message.content;
|
|
186
|
-
const textParts = parts.filter((p): p is { type: 'text'; text: string } => p.type === 'text').map(p => p.text);
|
|
187
|
-
const toolCalls = parts.filter(p => p.type === 'tool-call');
|
|
188
|
-
contentStr = textParts.join('');
|
|
189
|
-
if (toolCalls.length > 0) toolCallsJson = JSON.stringify(toolCalls);
|
|
190
|
-
}
|
|
191
|
-
} else if (message.role === 'tool') {
|
|
192
|
-
contentStr = JSON.stringify(message.content);
|
|
193
|
-
const firstResult = Array.isArray(message.content) ? message.content[0] : undefined;
|
|
194
|
-
if (firstResult && 'toolCallId' in firstResult) toolCallId = firstResult.toolCallId;
|
|
195
|
-
} else {
|
|
196
|
-
contentStr = '';
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Insert the message
|
|
200
|
-
await this.engine.insert(MESSAGES_OBJECT, {
|
|
201
|
-
id: msgId,
|
|
202
|
-
conversation_id: conversationId,
|
|
203
|
-
role: message.role,
|
|
204
|
-
content: contentStr,
|
|
205
|
-
tool_calls: toolCallsJson,
|
|
206
|
-
tool_call_id: toolCallId,
|
|
207
|
-
created_at: now,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Update conversation timestamp
|
|
211
|
-
await this.engine.update(CONVERSATIONS_OBJECT, { id: conversationId, updated_at: now }, {
|
|
212
|
-
where: { id: conversationId },
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// Return the full updated conversation
|
|
216
|
-
return (await this.get(conversationId))!;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async delete(conversationId: string): Promise<void> {
|
|
220
|
-
// Delete messages first (child records)
|
|
221
|
-
await this.engine.delete(MESSAGES_OBJECT, {
|
|
222
|
-
where: { conversation_id: conversationId },
|
|
223
|
-
multi: true,
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Delete the conversation
|
|
227
|
-
await this.engine.delete(CONVERSATIONS_OBJECT, {
|
|
228
|
-
where: { id: conversationId },
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// ── Private helpers ──────────────────────────────────────────────
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Safely parse a JSON string, returning `undefined` on failure.
|
|
236
|
-
*/
|
|
237
|
-
private safeParse<T>(value: string | null, fallback?: T): T | undefined {
|
|
238
|
-
if (!value) return undefined;
|
|
239
|
-
try {
|
|
240
|
-
return JSON.parse(value) as T;
|
|
241
|
-
} catch {
|
|
242
|
-
return fallback;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Map a database row + message rows to an AIConversation.
|
|
248
|
-
*/
|
|
249
|
-
private toConversation(row: DbConversationRow, messageRows: DbMessageRow[]): AIConversation {
|
|
250
|
-
return {
|
|
251
|
-
id: row.id,
|
|
252
|
-
title: row.title ?? undefined,
|
|
253
|
-
agentId: row.agent_id ?? undefined,
|
|
254
|
-
userId: row.user_id ?? undefined,
|
|
255
|
-
messages: messageRows.map(m => this.toMessage(m)),
|
|
256
|
-
createdAt: row.created_at,
|
|
257
|
-
updatedAt: row.updated_at,
|
|
258
|
-
metadata: this.safeParse<Record<string, unknown>>(row.metadata),
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Map a database row to a ModelMessage.
|
|
264
|
-
*/
|
|
265
|
-
private toMessage(row: DbMessageRow): ModelMessage {
|
|
266
|
-
switch (row.role) {
|
|
267
|
-
case 'system':
|
|
268
|
-
return { role: 'system', content: row.content };
|
|
269
|
-
case 'user':
|
|
270
|
-
return { role: 'user', content: row.content };
|
|
271
|
-
case 'assistant': {
|
|
272
|
-
const toolCalls = this.safeParse<ToolCallPart[]>(row.tool_calls);
|
|
273
|
-
if (toolCalls && toolCalls.length > 0) {
|
|
274
|
-
const content: Array<{ type: 'text'; text: string } | ToolCallPart> = [];
|
|
275
|
-
if (row.content) content.push({ type: 'text', text: row.content });
|
|
276
|
-
content.push(...toolCalls);
|
|
277
|
-
return { role: 'assistant', content };
|
|
278
|
-
}
|
|
279
|
-
return { role: 'assistant', content: row.content };
|
|
280
|
-
}
|
|
281
|
-
case 'tool': {
|
|
282
|
-
const toolResults = this.safeParse<ToolResultPart[]>(row.content);
|
|
283
|
-
if (toolResults && toolResults.length > 0 && toolResults[0]?.type === 'tool-result') {
|
|
284
|
-
return { role: 'tool', content: toolResults };
|
|
285
|
-
}
|
|
286
|
-
// Backward compat: old format was a plain string
|
|
287
|
-
return {
|
|
288
|
-
role: 'tool',
|
|
289
|
-
content: [{
|
|
290
|
-
type: 'tool-result' as const,
|
|
291
|
-
toolCallId: row.tool_call_id ?? '',
|
|
292
|
-
toolName: 'unknown',
|
|
293
|
-
output: { type: 'text' as const, value: row.content },
|
|
294
|
-
}],
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
default:
|
|
298
|
-
return { role: 'user', content: row.content };
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
// Core service
|
|
4
|
-
export { AIService } from './ai-service.js';
|
|
5
|
-
export type { AIServiceConfig } from './ai-service.js';
|
|
6
|
-
|
|
7
|
-
// Kernel plugin
|
|
8
|
-
export { AIServicePlugin } from './plugin.js';
|
|
9
|
-
export type { AIServicePluginOptions } from './plugin.js';
|
|
10
|
-
|
|
11
|
-
// Adapters
|
|
12
|
-
export { MemoryLLMAdapter } from './adapters/memory-adapter.js';
|
|
13
|
-
export { VercelLLMAdapter } from './adapters/vercel-adapter.js';
|
|
14
|
-
export type { VercelLLMAdapterConfig } from './adapters/vercel-adapter.js';
|
|
15
|
-
export type { LLMAdapter } from '@objectstack/spec/contracts';
|
|
16
|
-
|
|
17
|
-
// Vercel Data Stream encoder
|
|
18
|
-
export { encodeStreamPart, encodeVercelDataStream } from './stream/vercel-stream-encoder.js';
|
|
19
|
-
|
|
20
|
-
// Conversation
|
|
21
|
-
export { InMemoryConversationService } from './conversation/in-memory-conversation-service.js';
|
|
22
|
-
export { ObjectQLConversationService } from './conversation/objectql-conversation-service.js';
|
|
23
|
-
|
|
24
|
-
// Tool registry
|
|
25
|
-
export { ToolRegistry } from './tools/tool-registry.js';
|
|
26
|
-
export type { ToolHandler, ToolExecutionResult } from './tools/tool-registry.js';
|
|
27
|
-
|
|
28
|
-
// Data tools
|
|
29
|
-
export { registerDataTools, DATA_TOOL_DEFINITIONS } from './tools/data-tools.js';
|
|
30
|
-
export type { DataToolContext } from './tools/data-tools.js';
|
|
31
|
-
|
|
32
|
-
// Metadata tools
|
|
33
|
-
export { registerMetadataTools, METADATA_TOOL_DEFINITIONS } from './tools/metadata-tools.js';
|
|
34
|
-
export type { MetadataToolContext } from './tools/metadata-tools.js';
|
|
35
|
-
|
|
36
|
-
// Individual tool metadata (first-class Tool definitions via defineTool)
|
|
37
|
-
export {
|
|
38
|
-
createObjectTool,
|
|
39
|
-
addFieldTool,
|
|
40
|
-
modifyFieldTool,
|
|
41
|
-
deleteFieldTool,
|
|
42
|
-
listObjectsTool,
|
|
43
|
-
describeObjectTool,
|
|
44
|
-
} from './tools/metadata-tools.js';
|
|
45
|
-
|
|
46
|
-
// Agent runtime
|
|
47
|
-
export { AgentRuntime } from './agent-runtime.js';
|
|
48
|
-
export type { AgentChatContext } from './agent-runtime.js';
|
|
49
|
-
|
|
50
|
-
// Built-in agents
|
|
51
|
-
export { DATA_CHAT_AGENT, METADATA_ASSISTANT_AGENT } from './agents/index.js';
|
|
52
|
-
|
|
53
|
-
// Object definitions
|
|
54
|
-
export { AiConversationObject, AiMessageObject } from './objects/index.js';
|
|
55
|
-
|
|
56
|
-
// Routes
|
|
57
|
-
export { buildAIRoutes } from './routes/ai-routes.js';
|
|
58
|
-
export { buildAgentRoutes } from './routes/agent-routes.js';
|
|
59
|
-
export { buildToolRoutes } from './routes/tool-routes.js';
|
|
60
|
-
export type { RouteDefinition, RouteRequest, RouteResponse, RouteUserContext } from './routes/ai-routes.js';
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* ai_conversations — AI Conversation Object
|
|
7
|
-
*
|
|
8
|
-
* Stores conversation metadata for persistent AI conversation management.
|
|
9
|
-
* Messages are stored separately in `ai_messages` to support efficient
|
|
10
|
-
* querying and pagination.
|
|
11
|
-
*
|
|
12
|
-
* @namespace ai
|
|
13
|
-
*/
|
|
14
|
-
export const AiConversationObject = ObjectSchema.create({
|
|
15
|
-
namespace: 'ai',
|
|
16
|
-
name: 'conversations',
|
|
17
|
-
label: 'AI Conversation',
|
|
18
|
-
pluralLabel: 'AI Conversations',
|
|
19
|
-
icon: 'message-square',
|
|
20
|
-
isSystem: true,
|
|
21
|
-
description: 'Persistent AI conversation metadata',
|
|
22
|
-
|
|
23
|
-
fields: {
|
|
24
|
-
id: Field.text({
|
|
25
|
-
label: 'Conversation ID',
|
|
26
|
-
required: true,
|
|
27
|
-
readonly: true,
|
|
28
|
-
}),
|
|
29
|
-
|
|
30
|
-
title: Field.text({
|
|
31
|
-
label: 'Title',
|
|
32
|
-
required: false,
|
|
33
|
-
maxLength: 500,
|
|
34
|
-
description: 'Conversation title or summary',
|
|
35
|
-
}),
|
|
36
|
-
|
|
37
|
-
agent_id: Field.text({
|
|
38
|
-
label: 'Agent ID',
|
|
39
|
-
required: false,
|
|
40
|
-
maxLength: 255,
|
|
41
|
-
description: 'Associated AI agent identifier',
|
|
42
|
-
}),
|
|
43
|
-
|
|
44
|
-
user_id: Field.text({
|
|
45
|
-
label: 'User ID',
|
|
46
|
-
required: false,
|
|
47
|
-
maxLength: 255,
|
|
48
|
-
description: 'User who owns the conversation',
|
|
49
|
-
}),
|
|
50
|
-
|
|
51
|
-
metadata: Field.textarea({
|
|
52
|
-
label: 'Metadata',
|
|
53
|
-
required: false,
|
|
54
|
-
description: 'JSON-serialized conversation metadata',
|
|
55
|
-
}),
|
|
56
|
-
|
|
57
|
-
created_at: Field.datetime({
|
|
58
|
-
label: 'Created At',
|
|
59
|
-
required: true,
|
|
60
|
-
defaultValue: 'NOW()',
|
|
61
|
-
readonly: true,
|
|
62
|
-
}),
|
|
63
|
-
|
|
64
|
-
updated_at: Field.datetime({
|
|
65
|
-
label: 'Updated At',
|
|
66
|
-
required: true,
|
|
67
|
-
defaultValue: 'NOW()',
|
|
68
|
-
readonly: true,
|
|
69
|
-
}),
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
indexes: [
|
|
73
|
-
{ fields: ['user_id'] },
|
|
74
|
-
{ fields: ['agent_id'] },
|
|
75
|
-
{ fields: ['created_at'] },
|
|
76
|
-
],
|
|
77
|
-
|
|
78
|
-
enable: {
|
|
79
|
-
trackHistory: false,
|
|
80
|
-
searchable: false,
|
|
81
|
-
apiEnabled: true,
|
|
82
|
-
apiMethods: ['get', 'list', 'create', 'update', 'delete'],
|
|
83
|
-
trash: false,
|
|
84
|
-
mru: false,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { ObjectSchema, Field } from '@objectstack/spec/data';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* ai_messages — AI Message Object
|
|
7
|
-
*
|
|
8
|
-
* Stores individual messages within an AI conversation.
|
|
9
|
-
* Each message belongs to a conversation via `conversation_id` foreign key.
|
|
10
|
-
*
|
|
11
|
-
* @namespace ai
|
|
12
|
-
*/
|
|
13
|
-
export const AiMessageObject = ObjectSchema.create({
|
|
14
|
-
namespace: 'ai',
|
|
15
|
-
name: 'messages',
|
|
16
|
-
label: 'AI Message',
|
|
17
|
-
pluralLabel: 'AI Messages',
|
|
18
|
-
icon: 'message-circle',
|
|
19
|
-
isSystem: true,
|
|
20
|
-
description: 'Individual messages within AI conversations',
|
|
21
|
-
|
|
22
|
-
fields: {
|
|
23
|
-
id: Field.text({
|
|
24
|
-
label: 'Message ID',
|
|
25
|
-
required: true,
|
|
26
|
-
readonly: true,
|
|
27
|
-
}),
|
|
28
|
-
|
|
29
|
-
conversation_id: Field.text({
|
|
30
|
-
label: 'Conversation ID',
|
|
31
|
-
required: true,
|
|
32
|
-
description: 'Foreign key to ai_conversations',
|
|
33
|
-
}),
|
|
34
|
-
|
|
35
|
-
role: Field.select({
|
|
36
|
-
label: 'Role',
|
|
37
|
-
required: true,
|
|
38
|
-
options: [
|
|
39
|
-
{ label: 'System', value: 'system' },
|
|
40
|
-
{ label: 'User', value: 'user' },
|
|
41
|
-
{ label: 'Assistant', value: 'assistant' },
|
|
42
|
-
{ label: 'Tool', value: 'tool' },
|
|
43
|
-
],
|
|
44
|
-
}),
|
|
45
|
-
|
|
46
|
-
content: Field.textarea({
|
|
47
|
-
label: 'Content',
|
|
48
|
-
required: true,
|
|
49
|
-
description: 'Message content',
|
|
50
|
-
}),
|
|
51
|
-
|
|
52
|
-
tool_calls: Field.textarea({
|
|
53
|
-
label: 'Tool Calls',
|
|
54
|
-
required: false,
|
|
55
|
-
description: 'JSON-serialized tool calls (when role=assistant)',
|
|
56
|
-
}),
|
|
57
|
-
|
|
58
|
-
tool_call_id: Field.text({
|
|
59
|
-
label: 'Tool Call ID',
|
|
60
|
-
required: false,
|
|
61
|
-
maxLength: 255,
|
|
62
|
-
description: 'ID of the tool call this message responds to (when role=tool)',
|
|
63
|
-
}),
|
|
64
|
-
|
|
65
|
-
created_at: Field.datetime({
|
|
66
|
-
label: 'Created At',
|
|
67
|
-
required: true,
|
|
68
|
-
defaultValue: 'NOW()',
|
|
69
|
-
readonly: true,
|
|
70
|
-
}),
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
indexes: [
|
|
74
|
-
{ fields: ['conversation_id'] },
|
|
75
|
-
{ fields: ['conversation_id', 'created_at'] },
|
|
76
|
-
],
|
|
77
|
-
|
|
78
|
-
enable: {
|
|
79
|
-
trackHistory: false,
|
|
80
|
-
searchable: false,
|
|
81
|
-
apiEnabled: true,
|
|
82
|
-
apiMethods: ['get', 'list', 'create'],
|
|
83
|
-
trash: false,
|
|
84
|
-
mru: false,
|
|
85
|
-
},
|
|
86
|
-
});
|
package/src/objects/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* AI Service — System Object Definitions (ai namespace)
|
|
5
|
-
*
|
|
6
|
-
* Canonical ObjectSchema definitions for AI conversation persistence.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export { AiConversationObject } from './ai-conversation.object.js';
|
|
10
|
-
export { AiMessageObject } from './ai-message.object.js';
|