@svadmin/core 0.47.0 → 0.48.0
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/package.json +1 -1
- package/src/chatProvider.svelte.ts +79 -29
- package/src/helpers-pure.ts +14 -158
- package/src/helpers.ts +2 -3
- package/src/index.ts +6 -3
package/package.json
CHANGED
|
@@ -1,49 +1,83 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
// Headless AI contracts. Rendering lives in @svadmin/ai-elements.
|
|
2
|
+
|
|
3
|
+
export type ToolState =
|
|
4
|
+
| 'input-streaming'
|
|
5
|
+
| 'input-available'
|
|
6
|
+
| 'approval-requested'
|
|
7
|
+
| 'approval-responded'
|
|
8
|
+
| 'output-available'
|
|
9
|
+
| 'output-denied'
|
|
10
|
+
| 'output-error';
|
|
11
|
+
|
|
12
|
+
export type MessageStatus = 'submitted' | 'streaming' | 'complete' | 'aborted' | 'error';
|
|
13
|
+
|
|
14
|
+
export interface ChatAttachment {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
mediaType?: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
size?: number;
|
|
20
|
+
/** 原始浏览器文件,供上传适配器直接读取;持久化恢复时不会保留。 */
|
|
21
|
+
file?: File;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChatSource {
|
|
25
|
+
id?: string;
|
|
26
|
+
title: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
quote?: string;
|
|
29
|
+
description?: string;
|
|
16
30
|
}
|
|
17
31
|
|
|
32
|
+
export type ChatMessagePart =
|
|
33
|
+
| { id?: string; type: 'text'; text: string }
|
|
34
|
+
| { id?: string; type: 'reasoning'; text: string; streaming?: boolean }
|
|
35
|
+
| { id?: string; type: 'tool-call'; tool: string; input: unknown; state: ToolState; callId?: string }
|
|
36
|
+
| { id?: string; type: 'tool-result'; tool: string; output: unknown; error?: string; callId?: string }
|
|
37
|
+
| { id?: string; type: 'source'; source: ChatSource }
|
|
38
|
+
| { id?: string; type: 'image'; src: string; alt?: string; width?: number; height?: number }
|
|
39
|
+
| { id?: string; type: 'file'; file: ChatAttachment }
|
|
40
|
+
| {
|
|
41
|
+
id?: string;
|
|
42
|
+
type: 'approval';
|
|
43
|
+
approvalId: string;
|
|
44
|
+
tool: string;
|
|
45
|
+
input: Record<string, unknown>;
|
|
46
|
+
description: string;
|
|
47
|
+
approved?: boolean;
|
|
48
|
+
}
|
|
49
|
+
| { id?: string; type: 'component'; name: string; props: Record<string, unknown> };
|
|
50
|
+
|
|
18
51
|
export interface ChatMessage {
|
|
19
52
|
id: string;
|
|
20
53
|
role: 'user' | 'assistant' | 'system';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
54
|
+
parts: ChatMessagePart[];
|
|
55
|
+
status?: MessageStatus;
|
|
56
|
+
createdAt: number;
|
|
57
|
+
attachments?: ChatAttachment[];
|
|
25
58
|
}
|
|
26
59
|
|
|
27
|
-
/** Admin context
|
|
60
|
+
/** Admin context passed separately from untrusted conversation messages. */
|
|
28
61
|
export interface ChatContext {
|
|
29
62
|
currentResource?: string;
|
|
30
|
-
selectedRecordId?: string;
|
|
31
|
-
currentView?: 'list' | 'edit' | 'create' | 'show';
|
|
63
|
+
selectedRecordId?: string | number;
|
|
64
|
+
currentView?: 'list' | 'edit' | 'create' | 'show' | string;
|
|
32
65
|
pathname?: string;
|
|
66
|
+
metadata?: Record<string, unknown>;
|
|
33
67
|
}
|
|
34
68
|
|
|
35
69
|
/**
|
|
36
70
|
* ChatProvider interface for integrating AI chat into admin panels.
|
|
37
71
|
* Implement `sendMessage` to connect to any AI backend (OpenAI, self-hosted, etc.)
|
|
38
72
|
*
|
|
39
|
-
* Return
|
|
40
|
-
*
|
|
73
|
+
* Return text or structured parts for non-streaming responses. For streaming
|
|
74
|
+
* transports, yield text chunks and/or structured parts from an async generator.
|
|
41
75
|
*/
|
|
42
76
|
export interface ChatProvider {
|
|
43
77
|
sendMessage(
|
|
44
78
|
messages: ChatMessage[],
|
|
45
|
-
options?: { signal?: AbortSignal },
|
|
46
|
-
): Promise<string> | AsyncGenerator<string, void, unknown>;
|
|
79
|
+
options?: { signal?: AbortSignal; context?: ChatContext },
|
|
80
|
+
): Promise<ChatMessagePart[] | string> | AsyncGenerator<ChatMessagePart | string, void, unknown>;
|
|
47
81
|
}
|
|
48
82
|
|
|
49
83
|
// ─── Agent Provider (extends ChatProvider concept) ─────────────
|
|
@@ -113,12 +147,16 @@ export interface ToolResult {
|
|
|
113
147
|
export type AgentEvent =
|
|
114
148
|
/** Streaming text chunk */
|
|
115
149
|
| { type: 'text'; content: string }
|
|
150
|
+
/** Streaming reasoning chunk */
|
|
151
|
+
| { type: 'reasoning'; content: string; streaming?: boolean }
|
|
116
152
|
/** Agent wants to call a tool (informational — execution is handled internally) */
|
|
117
|
-
| { type: 'tool_call'; tool: string; args: Record<string, unknown
|
|
153
|
+
| { type: 'tool_call'; tool: string; args: Record<string, unknown>; callId?: string }
|
|
118
154
|
/** Agent wants to call a tool that requires user approval before execution */
|
|
119
155
|
| { type: 'approval_request'; id: string; tool: string; args: Record<string, unknown>; description: string }
|
|
120
156
|
/** Tool execution completed */
|
|
121
|
-
| { type: 'tool_result'; tool: string; result: ToolResult }
|
|
157
|
+
| { type: 'tool_result'; tool: string; result: ToolResult; callId?: string }
|
|
158
|
+
/** Source attribution emitted by a retrieval step */
|
|
159
|
+
| { type: 'source'; source: ChatSource }
|
|
122
160
|
/** Agent wants to render a named component (Generative UI) */
|
|
123
161
|
| { type: 'component'; name: string; props: Record<string, unknown> }
|
|
124
162
|
/** Stream finished */
|
|
@@ -131,6 +169,14 @@ export interface AgentOptions {
|
|
|
131
169
|
context?: ChatContext;
|
|
132
170
|
}
|
|
133
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Cancellation signal supplied while an approval decision is being submitted.
|
|
174
|
+
* Providers must stop before committing tool execution when this signal aborts.
|
|
175
|
+
*/
|
|
176
|
+
export interface ApprovalResponseOptions {
|
|
177
|
+
signal: AbortSignal;
|
|
178
|
+
}
|
|
179
|
+
|
|
134
180
|
/**
|
|
135
181
|
* AgentProvider — advanced AI integration with tool calling and approval gates.
|
|
136
182
|
*
|
|
@@ -159,7 +205,11 @@ export interface AgentProvider {
|
|
|
159
205
|
messages: ChatMessage[],
|
|
160
206
|
options?: AgentOptions,
|
|
161
207
|
): AsyncGenerator<AgentEvent, void, unknown>;
|
|
162
|
-
approveToolCall?(
|
|
208
|
+
approveToolCall?(
|
|
209
|
+
id: string,
|
|
210
|
+
approved: boolean,
|
|
211
|
+
options: ApprovalResponseOptions,
|
|
212
|
+
): void | Promise<void>;
|
|
163
213
|
}
|
|
164
214
|
|
|
165
215
|
// ─── Chat Provider singleton ───────────────────────────────────
|
package/src/helpers-pure.ts
CHANGED
|
@@ -165,34 +165,7 @@ export function parseCSV(text: string): string[][] {
|
|
|
165
165
|
return rows;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
// ───
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Universal schema interface compatible with TypeBox, Standard Schema (v1), Valibot, Zod,
|
|
172
|
-
* Yup, Joi, etc.
|
|
173
|
-
*/
|
|
174
|
-
export interface SchemaValidatorLike {
|
|
175
|
-
Check?: (value: unknown) => boolean;
|
|
176
|
-
Errors?: (value: unknown) => Iterable<{ path?: string; message: string; schema?: unknown }>;
|
|
177
|
-
safeParse?: (data: unknown) => {
|
|
178
|
-
success: boolean;
|
|
179
|
-
data?: unknown;
|
|
180
|
-
error?: {
|
|
181
|
-
issues?: Array<{ path?: Array<string | number>; message: string }>;
|
|
182
|
-
errors?: Array<{ path?: Array<string | number>; message: string }>;
|
|
183
|
-
};
|
|
184
|
-
};
|
|
185
|
-
parse?: (data: unknown) => unknown;
|
|
186
|
-
validate?: (data: unknown) => {
|
|
187
|
-
error?: {
|
|
188
|
-
details?: Array<{ path?: Array<string | number>; message: string }>;
|
|
189
|
-
message?: string;
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
'~standard'?: {
|
|
193
|
-
validate: (data: unknown) => unknown | Promise<unknown>;
|
|
194
|
-
};
|
|
195
|
-
}
|
|
168
|
+
// ─── TypeBox Validator Helper ───────────────────────────────────
|
|
196
169
|
|
|
197
170
|
/**
|
|
198
171
|
* TypeBox validator interface matching TypeCompiler.Compile or Value.Errors
|
|
@@ -202,134 +175,6 @@ export type TypeBoxValidatorLike = {
|
|
|
202
175
|
Errors: (value: unknown) => Iterable<{ path?: string; message: string }>;
|
|
203
176
|
};
|
|
204
177
|
|
|
205
|
-
/**
|
|
206
|
-
* Creates a synchronous form validator function from any TypeBox, Standard Schema v1,
|
|
207
|
-
* Valibot, Zod, Yup, or Joi object.
|
|
208
|
-
*
|
|
209
|
-
* @example
|
|
210
|
-
* ```ts
|
|
211
|
-
* import { Type } from '@sinclair/typebox';
|
|
212
|
-
* import { TypeCompiler } from '@sinclair/typebox/compiler';
|
|
213
|
-
* import { createSchemaValidator, useForm } from '@svadmin/core';
|
|
214
|
-
*
|
|
215
|
-
* const userSchema = Type.Object({
|
|
216
|
-
* name: Type.String({ minLength: 2 }),
|
|
217
|
-
* email: Type.String({ format: 'email' }),
|
|
218
|
-
* });
|
|
219
|
-
*
|
|
220
|
-
* const form = useForm({
|
|
221
|
-
* resource: 'users',
|
|
222
|
-
* validate: createSchemaValidator(TypeCompiler.Compile(userSchema)),
|
|
223
|
-
* });
|
|
224
|
-
* ```
|
|
225
|
-
*/
|
|
226
|
-
export function createSchemaValidator(
|
|
227
|
-
schema: SchemaValidatorLike | unknown
|
|
228
|
-
): (values: Record<string, unknown>) => Record<string, string> | null {
|
|
229
|
-
if (!schema || typeof schema !== 'object') {
|
|
230
|
-
return () => null;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const s = schema as SchemaValidatorLike;
|
|
234
|
-
|
|
235
|
-
return (values: Record<string, unknown>) => {
|
|
236
|
-
const errors: Record<string, string> = {};
|
|
237
|
-
|
|
238
|
-
// 0. TypeBox TypeCompiler compiled validator (.Check & .Errors)
|
|
239
|
-
if (typeof s.Check === 'function' && typeof s.Errors === 'function') {
|
|
240
|
-
if (s.Check(values)) return null;
|
|
241
|
-
for (const err of s.Errors(values)) {
|
|
242
|
-
const rawPath = err.path || '';
|
|
243
|
-
const cleanPath = rawPath.replace(/^\//, '').replace(/\//g, '.');
|
|
244
|
-
const key = cleanPath || '_root';
|
|
245
|
-
if (!errors[key]) errors[key] = err.message;
|
|
246
|
-
}
|
|
247
|
-
return Object.keys(errors).length > 0 ? errors : null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// 0b. TypeBox Value.Errors iterator provider
|
|
251
|
-
if (typeof s.Errors === 'function') {
|
|
252
|
-
for (const err of s.Errors(values)) {
|
|
253
|
-
const rawPath = err.path || '';
|
|
254
|
-
const cleanPath = rawPath.replace(/^\//, '').replace(/\//g, '.');
|
|
255
|
-
const key = cleanPath || '_root';
|
|
256
|
-
if (!errors[key]) errors[key] = err.message;
|
|
257
|
-
}
|
|
258
|
-
return Object.keys(errors).length > 0 ? errors : null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// 1. Standard Schema v1 (~standard)
|
|
262
|
-
if (s['~standard'] && typeof s['~standard'].validate === 'function') {
|
|
263
|
-
const res = s['~standard'].validate(values);
|
|
264
|
-
if (res && typeof res === 'object' && 'issues' in res && Array.isArray((res as { issues?: Array<{ path?: Array<unknown>; message: string }> }).issues)) {
|
|
265
|
-
for (const issue of (res as { issues: Array<{ path?: Array<unknown>; message: string }> }).issues) {
|
|
266
|
-
const rawPath = issue.path?.[0];
|
|
267
|
-
const key = typeof rawPath === 'object' && rawPath !== null && 'key' in rawPath
|
|
268
|
-
? String((rawPath as { key: unknown }).key)
|
|
269
|
-
: rawPath != null
|
|
270
|
-
? String(rawPath)
|
|
271
|
-
: '_root';
|
|
272
|
-
if (!errors[key]) errors[key] = issue.message;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return Object.keys(errors).length > 0 ? errors : null;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// 2. Zod / Valibot safeParse
|
|
279
|
-
if (typeof s.safeParse === 'function') {
|
|
280
|
-
const res = s.safeParse(values);
|
|
281
|
-
if (!res.success && res.error) {
|
|
282
|
-
const issues = res.error.issues ?? res.error.errors ?? [];
|
|
283
|
-
for (const issue of issues) {
|
|
284
|
-
const key = issue.path?.[0] != null ? String(issue.path[0]) : '_root';
|
|
285
|
-
if (!errors[key]) errors[key] = issue.message;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return Object.keys(errors).length > 0 ? errors : null;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// 3. Joi / generic .validate()
|
|
292
|
-
if (typeof s.validate === 'function') {
|
|
293
|
-
const res = s.validate(values);
|
|
294
|
-
if (res && typeof res === 'object' && res.error) {
|
|
295
|
-
const details = res.error.details;
|
|
296
|
-
if (Array.isArray(details) && details.length > 0) {
|
|
297
|
-
for (const d of details) {
|
|
298
|
-
const key = d.path?.[0] != null ? String(d.path[0]) : '_root';
|
|
299
|
-
if (!errors[key]) errors[key] = d.message;
|
|
300
|
-
}
|
|
301
|
-
} else if (res.error.message) {
|
|
302
|
-
errors._root = res.error.message;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return Object.keys(errors).length > 0 ? errors : null;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// 4. Synchronous .parse() that throws (e.g. standard Zod parse or Yup validateSync)
|
|
309
|
-
if (typeof s.parse === 'function') {
|
|
310
|
-
try {
|
|
311
|
-
s.parse(values);
|
|
312
|
-
} catch (err: unknown) {
|
|
313
|
-
if (err && typeof err === 'object') {
|
|
314
|
-
const issues = (err as { issues?: Array<{ path?: Array<string | number>; message: string }>; errors?: Array<{ path?: Array<string | number>; message: string }> }).issues ??
|
|
315
|
-
(err as { errors?: Array<{ path?: Array<string | number>; message: string }> }).errors;
|
|
316
|
-
if (Array.isArray(issues)) {
|
|
317
|
-
for (const issue of issues) {
|
|
318
|
-
const key = issue.path?.[0] != null ? String(issue.path[0]) : '_root';
|
|
319
|
-
if (!errors[key]) errors[key] = issue.message;
|
|
320
|
-
}
|
|
321
|
-
} else if ('message' in err && typeof (err as { message: unknown }).message === 'string') {
|
|
322
|
-
errors._root = (err as { message: string }).message;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
return Object.keys(errors).length > 0 ? errors : null;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return null;
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
178
|
/**
|
|
334
179
|
* Creates a synchronous form validator function specifically optimized for TypeBox
|
|
335
180
|
* compiled validators (from TypeCompiler.Compile) or TypeBox value checks.
|
|
@@ -354,7 +199,18 @@ export function createSchemaValidator(
|
|
|
354
199
|
* ```
|
|
355
200
|
*/
|
|
356
201
|
export function createTypeBoxValidator(
|
|
357
|
-
|
|
202
|
+
validator: TypeBoxValidatorLike
|
|
358
203
|
): (values: Record<string, unknown>) => Record<string, string> | null {
|
|
359
|
-
return
|
|
204
|
+
return (values) => {
|
|
205
|
+
if (validator.Check?.(values)) return null;
|
|
206
|
+
|
|
207
|
+
const errors: Record<string, string> = {};
|
|
208
|
+
for (const error of validator.Errors(values)) {
|
|
209
|
+
const path = (error.path ?? '').replace(/^\//, '').replace(/\//g, '.');
|
|
210
|
+
const key = path || '_root';
|
|
211
|
+
if (!errors[key]) errors[key] = error.message;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return Object.keys(errors).length > 0 ? errors : null;
|
|
215
|
+
};
|
|
360
216
|
}
|
package/src/helpers.ts
CHANGED
|
@@ -8,10 +8,9 @@ export {
|
|
|
8
8
|
getDefaultFilter, getDefaultSortOrder,
|
|
9
9
|
unionFilters, unionSorters,
|
|
10
10
|
file2Base64, parseCSV,
|
|
11
|
-
createSchemaValidator,
|
|
12
11
|
createTypeBoxValidator,
|
|
13
12
|
} from './helpers-pure';
|
|
14
|
-
export type {
|
|
13
|
+
export type { TypeBoxValidatorLike } from './helpers-pure';
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
// ─── Document Title Helper ────────────────────────────────────
|
|
@@ -61,7 +60,7 @@ export function generateDefaultDocumentTitle(options?: {
|
|
|
61
60
|
* - `type === 'number'` fields (must be a valid number)
|
|
62
61
|
* - Custom per-field `field.validate()` callback
|
|
63
62
|
*
|
|
64
|
-
* This eliminates the need for hand-written
|
|
63
|
+
* This eliminates the need for hand-written schemas in most CRUD scenarios.
|
|
65
64
|
*
|
|
66
65
|
* @example
|
|
67
66
|
* ```ts
|
package/src/index.ts
CHANGED
|
@@ -117,7 +117,11 @@ export type {
|
|
|
117
117
|
} from './enterprise';
|
|
118
118
|
export { assertEnterpriseRequestContext, createEnterpriseRequestContext } from './enterprise';
|
|
119
119
|
export { setChatProvider, getChatProvider, setChatContext, getChatContext, setAgentProvider, getAgentProvider, registerApproval, resolveApproval, hasPendingApprovals, resetChatProvider, projectAdminToolSchema } from './chatProvider.svelte';
|
|
120
|
-
export type {
|
|
120
|
+
export type {
|
|
121
|
+
ChatProvider, ChatMessage, ChatMessagePart, ChatContext, ChatAttachment, ChatSource,
|
|
122
|
+
MessageStatus, ToolState, AgentProvider, AgentEvent, AgentOptions, ApprovalResponseOptions,
|
|
123
|
+
AdminTool, AdminToolParameter, ToolResult,
|
|
124
|
+
} from './chatProvider.svelte';
|
|
121
125
|
export { getTheme, setTheme, toggleTheme, getResolvedTheme, getColorTheme, setColorTheme, getColorThemes, configureTheme, getThemeConfig, clearCssOverrides, builtinPresets, registerColorPreset, getColorPresets, registerThemeOwner, updateThemeOwner, unregisterThemeOwner, resetTheme } from './theme.svelte';
|
|
122
126
|
export type { ThemeMode, ColorTheme, ThemeStrategy, ThemeConfig, ColorPreset, ThemeOwnerOptions, ThemeOwnerToken } from './theme.svelte';
|
|
123
127
|
export { setUnsavedChanges, getUnsavedChanges, initUnsavedChangesNotifier, resetUnsavedChanges } from './unsaved-changes.svelte';
|
|
@@ -203,10 +207,9 @@ export {
|
|
|
203
207
|
unionFilters, unionSorters,
|
|
204
208
|
file2Base64, generateDefaultDocumentTitle,
|
|
205
209
|
deriveValidator,
|
|
206
|
-
createSchemaValidator,
|
|
207
210
|
createTypeBoxValidator,
|
|
208
211
|
} from './helpers';
|
|
209
|
-
export type {
|
|
212
|
+
export type { TypeBoxValidatorLike } from './helpers';
|
|
210
213
|
export { TableState } from './table-state.svelte';
|
|
211
214
|
export type { TableStateOptions } from './table-state.svelte';
|
|
212
215
|
export type { UseInfiniteListOptions } from './hooks.svelte';
|