@svadmin/core 0.47.1 → 0.49.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 +8 -2
- package/src/admin-tool.ts +99 -0
- package/src/chatProvider.svelte.ts +89 -109
- package/src/index.ts +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svadmin/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"author": "zuohuadong",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -68,6 +68,10 @@
|
|
|
68
68
|
"./inferencer-graphql": {
|
|
69
69
|
"types": "./src/inferencer-graphql.ts",
|
|
70
70
|
"default": "./src/inferencer-graphql.ts"
|
|
71
|
+
},
|
|
72
|
+
"./admin-tool": {
|
|
73
|
+
"types": "./src/admin-tool.ts",
|
|
74
|
+
"default": "./src/admin-tool.ts"
|
|
71
75
|
}
|
|
72
76
|
},
|
|
73
77
|
"bugs": {
|
|
@@ -86,7 +90,9 @@
|
|
|
86
90
|
"sideEffects": false,
|
|
87
91
|
"type": "module",
|
|
88
92
|
"types": "src/index.ts",
|
|
89
|
-
"dependencies": {
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"@sinclair/typebox": "^0.34.52"
|
|
95
|
+
},
|
|
90
96
|
"peerDependenciesMeta": {
|
|
91
97
|
"svelte": {
|
|
92
98
|
"optional": true
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { StaticDecode, TObject } from '@sinclair/typebox';
|
|
2
|
+
import { Value } from '@sinclair/typebox/value';
|
|
3
|
+
|
|
4
|
+
/** Result returned by a tool execution. */
|
|
5
|
+
export interface ToolResult {
|
|
6
|
+
success: boolean;
|
|
7
|
+
data?: unknown;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A tool that the Agent can invoke within the admin panel.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const deletePostTool = defineAdminTool({
|
|
17
|
+
* name: 'deletePosts',
|
|
18
|
+
* description: 'Delete posts matching a filter',
|
|
19
|
+
* parameters: Type.Object({
|
|
20
|
+
* status: Type.Union([Type.Literal('draft'), Type.Literal('archived')]),
|
|
21
|
+
* }),
|
|
22
|
+
* needsApproval: true,
|
|
23
|
+
* execute: async ({ status }) => {
|
|
24
|
+
* const result = await dataProvider.getList({
|
|
25
|
+
* resource: 'posts',
|
|
26
|
+
* filters: [{ field: 'status', operator: 'eq', value: status }],
|
|
27
|
+
* });
|
|
28
|
+
* return { success: true, data: result };
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export interface AdminTool<Schema extends TObject = TObject> {
|
|
34
|
+
/** Unique tool name (e.g. 'getList', 'deleteRecords', 'generateReport') */
|
|
35
|
+
name: string;
|
|
36
|
+
/** Human-readable description for LLM tool-use prompting */
|
|
37
|
+
description: string;
|
|
38
|
+
/** TypeBox schema used for static inference, runtime validation, and LLM projection. */
|
|
39
|
+
parameters: Schema;
|
|
40
|
+
/** Pause execution until the user explicitly approves the invocation. */
|
|
41
|
+
needsApproval?: boolean;
|
|
42
|
+
/** 显式声明是否为只读操作(只读操作可并发执行,无副作用)。 */
|
|
43
|
+
readOnly?: boolean;
|
|
44
|
+
/** 显式声明是否为破坏性或高风险操作。 */
|
|
45
|
+
destructive?: boolean;
|
|
46
|
+
/** 显式声明是否支持并发调用。 */
|
|
47
|
+
concurrent?: boolean;
|
|
48
|
+
/** Execute the tool with decoded arguments. */
|
|
49
|
+
execute(args: StaticDecode<Schema>): Promise<ToolResult>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 保留具体 TypeBox Schema,让 execute 参数获得精确的静态类型。 */
|
|
53
|
+
export function defineAdminTool<const Schema extends TObject>(
|
|
54
|
+
tool: AdminTool<Schema>,
|
|
55
|
+
): AdminTool<Schema> {
|
|
56
|
+
return { ...tool, parameters: strictAdminToolSchema(tool.parameters) };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 在进入工具实现前统一执行 TypeBox 校验与转换。 */
|
|
60
|
+
export function decodeAdminToolArgs<const Schema extends TObject>(
|
|
61
|
+
tool: AdminTool<Schema>,
|
|
62
|
+
input: unknown,
|
|
63
|
+
): StaticDecode<Schema> {
|
|
64
|
+
return Value.Decode(strictAdminToolSchema(tool.parameters), input);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 解码并执行工具,避免调用方绕过运行时参数边界。 */
|
|
68
|
+
export async function executeAdminTool<const Schema extends TObject>(
|
|
69
|
+
tool: AdminTool<Schema>,
|
|
70
|
+
input: unknown,
|
|
71
|
+
): Promise<ToolResult> {
|
|
72
|
+
return tool.execute(decodeAdminToolArgs(tool, input));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 将工具投影为发给 LLM 或 MCP 客户端的公开 Schema。 */
|
|
76
|
+
export function projectAdminToolSchema<Schema extends TObject>(tool: AdminTool<Schema>): {
|
|
77
|
+
name: string;
|
|
78
|
+
description: string;
|
|
79
|
+
parameters: Schema;
|
|
80
|
+
readOnly?: boolean;
|
|
81
|
+
destructive?: boolean;
|
|
82
|
+
concurrent?: boolean;
|
|
83
|
+
needsApproval?: boolean;
|
|
84
|
+
} {
|
|
85
|
+
return {
|
|
86
|
+
name: tool.name,
|
|
87
|
+
description: tool.description,
|
|
88
|
+
parameters: strictAdminToolSchema(tool.parameters),
|
|
89
|
+
...(tool.readOnly !== undefined ? { readOnly: tool.readOnly } : {}),
|
|
90
|
+
...(tool.destructive !== undefined ? { destructive: tool.destructive } : {}),
|
|
91
|
+
...(tool.concurrent !== undefined ? { concurrent: tool.concurrent } : {}),
|
|
92
|
+
...(tool.needsApproval !== undefined ? { needsApproval: tool.needsApproval } : {}),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function strictAdminToolSchema<Schema extends TObject>(schema: Schema): Schema {
|
|
97
|
+
if (schema.additionalProperties === false) return schema;
|
|
98
|
+
return { ...schema, additionalProperties: false };
|
|
99
|
+
}
|
|
@@ -1,49 +1,93 @@
|
|
|
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
|
+
import type { AdminTool, ToolResult } from './admin-tool';
|
|
4
|
+
|
|
5
|
+
export type { AdminTool, ToolResult } from './admin-tool';
|
|
6
|
+
export {
|
|
7
|
+
decodeAdminToolArgs,
|
|
8
|
+
defineAdminTool,
|
|
9
|
+
executeAdminTool,
|
|
10
|
+
projectAdminToolSchema,
|
|
11
|
+
} from './admin-tool';
|
|
12
|
+
|
|
13
|
+
export type ToolState =
|
|
14
|
+
| 'input-streaming'
|
|
15
|
+
| 'input-available'
|
|
16
|
+
| 'approval-requested'
|
|
17
|
+
| 'approval-responded'
|
|
18
|
+
| 'output-available'
|
|
19
|
+
| 'output-denied'
|
|
20
|
+
| 'output-error';
|
|
21
|
+
|
|
22
|
+
export type MessageStatus = 'submitted' | 'streaming' | 'complete' | 'aborted' | 'error';
|
|
23
|
+
|
|
24
|
+
export interface ChatAttachment {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
mediaType?: string;
|
|
28
|
+
url?: string;
|
|
29
|
+
size?: number;
|
|
30
|
+
/** 原始浏览器文件,供上传适配器直接读取;持久化恢复时不会保留。 */
|
|
31
|
+
file?: File;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ChatSource {
|
|
35
|
+
id?: string;
|
|
36
|
+
title: string;
|
|
37
|
+
url?: string;
|
|
38
|
+
quote?: string;
|
|
39
|
+
description?: string;
|
|
16
40
|
}
|
|
17
41
|
|
|
42
|
+
export type ChatMessagePart =
|
|
43
|
+
| { id?: string; type: 'text'; text: string }
|
|
44
|
+
| { id?: string; type: 'reasoning'; text: string; streaming?: boolean }
|
|
45
|
+
| { id?: string; type: 'tool-call'; tool: string; input: unknown; state: ToolState; callId?: string }
|
|
46
|
+
| { id?: string; type: 'tool-result'; tool: string; output: unknown; error?: string; callId?: string }
|
|
47
|
+
| { id?: string; type: 'source'; source: ChatSource }
|
|
48
|
+
| { id?: string; type: 'image'; src: string; alt?: string; width?: number; height?: number }
|
|
49
|
+
| { id?: string; type: 'file'; file: ChatAttachment }
|
|
50
|
+
| {
|
|
51
|
+
id?: string;
|
|
52
|
+
type: 'approval';
|
|
53
|
+
approvalId: string;
|
|
54
|
+
tool: string;
|
|
55
|
+
input: Record<string, unknown>;
|
|
56
|
+
description: string;
|
|
57
|
+
approved?: boolean;
|
|
58
|
+
}
|
|
59
|
+
| { id?: string; type: 'component'; name: string; props: Record<string, unknown> };
|
|
60
|
+
|
|
18
61
|
export interface ChatMessage {
|
|
19
62
|
id: string;
|
|
20
63
|
role: 'user' | 'assistant' | 'system';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
64
|
+
parts: ChatMessagePart[];
|
|
65
|
+
status?: MessageStatus;
|
|
66
|
+
createdAt: number;
|
|
67
|
+
attachments?: ChatAttachment[];
|
|
25
68
|
}
|
|
26
69
|
|
|
27
|
-
/** Admin context
|
|
70
|
+
/** Admin context passed separately from untrusted conversation messages. */
|
|
28
71
|
export interface ChatContext {
|
|
29
72
|
currentResource?: string;
|
|
30
|
-
selectedRecordId?: string;
|
|
31
|
-
currentView?: 'list' | 'edit' | 'create' | 'show';
|
|
73
|
+
selectedRecordId?: string | number;
|
|
74
|
+
currentView?: 'list' | 'edit' | 'create' | 'show' | string;
|
|
32
75
|
pathname?: string;
|
|
76
|
+
metadata?: Record<string, unknown>;
|
|
33
77
|
}
|
|
34
78
|
|
|
35
79
|
/**
|
|
36
80
|
* ChatProvider interface for integrating AI chat into admin panels.
|
|
37
81
|
* Implement `sendMessage` to connect to any AI backend (OpenAI, self-hosted, etc.)
|
|
38
82
|
*
|
|
39
|
-
* Return
|
|
40
|
-
*
|
|
83
|
+
* Return text or structured parts for non-streaming responses. For streaming
|
|
84
|
+
* transports, yield text chunks and/or structured parts from an async generator.
|
|
41
85
|
*/
|
|
42
86
|
export interface ChatProvider {
|
|
43
87
|
sendMessage(
|
|
44
88
|
messages: ChatMessage[],
|
|
45
|
-
options?: { signal?: AbortSignal },
|
|
46
|
-
): Promise<string> | AsyncGenerator<string, void, unknown>;
|
|
89
|
+
options?: { signal?: AbortSignal; context?: ChatContext },
|
|
90
|
+
): Promise<ChatMessagePart[] | string> | AsyncGenerator<ChatMessagePart | string, void, unknown>;
|
|
47
91
|
}
|
|
48
92
|
|
|
49
93
|
// ─── Agent Provider (extends ChatProvider concept) ─────────────
|
|
@@ -53,72 +97,20 @@ export interface ChatProvider {
|
|
|
53
97
|
// • Approval gates — Dangerous tools require user confirmation before execution
|
|
54
98
|
// • Generative UI — Agent returns component name + props for dynamic rendering
|
|
55
99
|
|
|
56
|
-
/** JSON-Schema-compatible parameter definition for an admin tool. */
|
|
57
|
-
export interface AdminToolParameter {
|
|
58
|
-
type: 'string' | 'number' | 'boolean' | 'object' | 'array';
|
|
59
|
-
description?: string;
|
|
60
|
-
enum?: string[];
|
|
61
|
-
required?: boolean;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* A tool that the Agent can invoke within the admin panel.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```ts
|
|
69
|
-
* const deletePostTool: AdminTool = {
|
|
70
|
-
* name: 'deletePosts',
|
|
71
|
-
* description: 'Delete posts matching a filter',
|
|
72
|
-
* parameters: {
|
|
73
|
-
* status: { type: 'string', description: 'Filter by status', enum: ['draft', 'archived'] },
|
|
74
|
-
* },
|
|
75
|
-
* needsApproval: true,
|
|
76
|
-
* execute: async (params) => {
|
|
77
|
-
* const result = await dataProvider.deleteMany({ resource: 'posts', ids: params.ids });
|
|
78
|
-
* return { success: true, data: result };
|
|
79
|
-
* },
|
|
80
|
-
* };
|
|
81
|
-
* ```
|
|
82
|
-
*/
|
|
83
|
-
export interface AdminTool {
|
|
84
|
-
/** Unique tool name (e.g. 'getList', 'deleteRecords', 'generateReport') */
|
|
85
|
-
name: string;
|
|
86
|
-
/** Human-readable description for LLM tool-use prompting */
|
|
87
|
-
description: string;
|
|
88
|
-
/** JSON-Schema-style parameter definitions */
|
|
89
|
-
parameters: Record<string, AdminToolParameter>;
|
|
90
|
-
/**
|
|
91
|
-
* When `true`, execution is paused until the user explicitly approves.
|
|
92
|
-
* The agent emits an `approval_request` event and waits for `approveToolCall()`.
|
|
93
|
-
*/
|
|
94
|
-
needsApproval?: boolean;
|
|
95
|
-
/** 显式声明是否为只读操作(只读操作可并发执行,无副作用) */
|
|
96
|
-
readOnly?: boolean;
|
|
97
|
-
/** 显式声明是否为破坏性/高风险操作(默认触发 Approval Gate) */
|
|
98
|
-
destructive?: boolean;
|
|
99
|
-
/** 显式声明是否支持并发调用 */
|
|
100
|
-
concurrent?: boolean;
|
|
101
|
-
/** Execute the tool with the given arguments. */
|
|
102
|
-
execute(args: Record<string, unknown>): Promise<ToolResult>;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Result returned by a tool execution. */
|
|
106
|
-
export interface ToolResult {
|
|
107
|
-
success: boolean;
|
|
108
|
-
data?: unknown;
|
|
109
|
-
error?: string;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
100
|
/** Discriminated union of events emitted by an AgentProvider. */
|
|
113
101
|
export type AgentEvent =
|
|
114
102
|
/** Streaming text chunk */
|
|
115
103
|
| { type: 'text'; content: string }
|
|
104
|
+
/** Streaming reasoning chunk */
|
|
105
|
+
| { type: 'reasoning'; content: string; streaming?: boolean }
|
|
116
106
|
/** Agent wants to call a tool (informational — execution is handled internally) */
|
|
117
|
-
| { type: 'tool_call'; tool: string; args: Record<string, unknown
|
|
107
|
+
| { type: 'tool_call'; tool: string; args: Record<string, unknown>; callId?: string }
|
|
118
108
|
/** Agent wants to call a tool that requires user approval before execution */
|
|
119
109
|
| { type: 'approval_request'; id: string; tool: string; args: Record<string, unknown>; description: string }
|
|
120
110
|
/** Tool execution completed */
|
|
121
|
-
| { type: 'tool_result'; tool: string; result: ToolResult }
|
|
111
|
+
| { type: 'tool_result'; tool: string; result: ToolResult; callId?: string }
|
|
112
|
+
/** Source attribution emitted by a retrieval step */
|
|
113
|
+
| { type: 'source'; source: ChatSource }
|
|
122
114
|
/** Agent wants to render a named component (Generative UI) */
|
|
123
115
|
| { type: 'component'; name: string; props: Record<string, unknown> }
|
|
124
116
|
/** Stream finished */
|
|
@@ -131,6 +123,14 @@ export interface AgentOptions {
|
|
|
131
123
|
context?: ChatContext;
|
|
132
124
|
}
|
|
133
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Cancellation signal supplied while an approval decision is being submitted.
|
|
128
|
+
* Providers must stop before committing tool execution when this signal aborts.
|
|
129
|
+
*/
|
|
130
|
+
export interface ApprovalResponseOptions {
|
|
131
|
+
signal: AbortSignal;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
134
|
/**
|
|
135
135
|
* AgentProvider — advanced AI integration with tool calling and approval gates.
|
|
136
136
|
*
|
|
@@ -159,7 +159,11 @@ export interface AgentProvider {
|
|
|
159
159
|
messages: ChatMessage[],
|
|
160
160
|
options?: AgentOptions,
|
|
161
161
|
): AsyncGenerator<AgentEvent, void, unknown>;
|
|
162
|
-
approveToolCall?(
|
|
162
|
+
approveToolCall?(
|
|
163
|
+
id: string,
|
|
164
|
+
approved: boolean,
|
|
165
|
+
options: ApprovalResponseOptions,
|
|
166
|
+
): void | Promise<void>;
|
|
163
167
|
}
|
|
164
168
|
|
|
165
169
|
// ─── Chat Provider singleton ───────────────────────────────────
|
|
@@ -230,30 +234,6 @@ export function getChatContext(): ChatContext {
|
|
|
230
234
|
return chatContext;
|
|
231
235
|
}
|
|
232
236
|
|
|
233
|
-
/**
|
|
234
|
-
* 将 AdminTool 投影为发给 LLM / MCP 客户端的白名单 Schema。
|
|
235
|
-
* 剥离底层执行逻辑(execute)与宿主私有状态,保留 name、description、parameters 及并发/安全声明。
|
|
236
|
-
*/
|
|
237
|
-
export function projectAdminToolSchema(tool: AdminTool): {
|
|
238
|
-
name: string;
|
|
239
|
-
description: string;
|
|
240
|
-
parameters: Record<string, AdminToolParameter>;
|
|
241
|
-
readOnly?: boolean;
|
|
242
|
-
destructive?: boolean;
|
|
243
|
-
concurrent?: boolean;
|
|
244
|
-
needsApproval?: boolean;
|
|
245
|
-
} {
|
|
246
|
-
return {
|
|
247
|
-
name: tool.name,
|
|
248
|
-
description: tool.description,
|
|
249
|
-
parameters: { ...tool.parameters },
|
|
250
|
-
...(tool.readOnly !== undefined ? { readOnly: tool.readOnly } : {}),
|
|
251
|
-
...(tool.destructive !== undefined ? { destructive: tool.destructive } : {}),
|
|
252
|
-
...(tool.concurrent !== undefined ? { concurrent: tool.concurrent } : {}),
|
|
253
|
-
...(tool.needsApproval !== undefined ? { needsApproval: tool.needsApproval } : {}),
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
237
|
export function resetChatProvider(): void {
|
|
258
238
|
chatProvider = null;
|
|
259
239
|
agentProvider = null;
|
package/src/index.ts
CHANGED
|
@@ -116,8 +116,12 @@ export type {
|
|
|
116
116
|
WebhookSummary,
|
|
117
117
|
} from './enterprise';
|
|
118
118
|
export { assertEnterpriseRequestContext, createEnterpriseRequestContext } from './enterprise';
|
|
119
|
-
export { setChatProvider, getChatProvider, setChatContext, getChatContext, setAgentProvider, getAgentProvider, registerApproval, resolveApproval, hasPendingApprovals, resetChatProvider, projectAdminToolSchema } from './chatProvider.svelte';
|
|
120
|
-
export type {
|
|
119
|
+
export { setChatProvider, getChatProvider, setChatContext, getChatContext, setAgentProvider, getAgentProvider, registerApproval, resolveApproval, hasPendingApprovals, resetChatProvider, defineAdminTool, decodeAdminToolArgs, executeAdminTool, projectAdminToolSchema } from './chatProvider.svelte';
|
|
120
|
+
export type {
|
|
121
|
+
ChatProvider, ChatMessage, ChatMessagePart, ChatContext, ChatAttachment, ChatSource,
|
|
122
|
+
MessageStatus, ToolState, AgentProvider, AgentEvent, AgentOptions, ApprovalResponseOptions,
|
|
123
|
+
AdminTool, 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';
|