@xpert-ai/plugin-sdk 0.0.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/.eslintrc.json +30 -0
- package/.swcrc +29 -0
- package/PERMISSIONS.md +181 -0
- package/README.md +19 -0
- package/SCHEMA_SPECIFICATION.md +332 -0
- package/jest.config.ts +28 -0
- package/package.json +11 -0
- package/project.json +29 -0
- package/src/index.ts +12 -0
- package/src/lib/core/file-system.ts +96 -0
- package/src/lib/core/index.ts +3 -0
- package/src/lib/core/permissions.ts +97 -0
- package/src/lib/core/schema.ts +23 -0
- package/src/lib/integration/index.ts +3 -0
- package/src/lib/integration/strategy.decorator.ts +6 -0
- package/src/lib/integration/strategy.interface.ts +10 -0
- package/src/lib/integration/strategy.registry.ts +12 -0
- package/src/lib/knowledge/index.ts +3 -0
- package/src/lib/knowledge/knowledge-strategy.decorator.ts +6 -0
- package/src/lib/knowledge/knowledge-strategy.interface.ts +15 -0
- package/src/lib/knowledge/knowledge-strategy.registry.ts +36 -0
- package/src/lib/logger.ts +18 -0
- package/src/lib/plugin-metadata.ts +20 -0
- package/src/lib/plugin.hook.ts +54 -0
- package/src/lib/plugin.interface.ts +67 -0
- package/src/lib/plugin.ts +32 -0
- package/src/lib/rag/image/index.ts +3 -0
- package/src/lib/rag/image/strategy.decorator.ts +9 -0
- package/src/lib/rag/image/strategy.interface.ts +42 -0
- package/src/lib/rag/image/strategy.registry.ts +17 -0
- package/src/lib/rag/index.ts +6 -0
- package/src/lib/rag/retriever/index.ts +3 -0
- package/src/lib/rag/retriever/strategy.decorator.ts +9 -0
- package/src/lib/rag/retriever/strategy.interface.ts +32 -0
- package/src/lib/rag/retriever/strategy.registry.ts +12 -0
- package/src/lib/rag/source/index.ts +3 -0
- package/src/lib/rag/source/strategy.decorator.ts +9 -0
- package/src/lib/rag/source/strategy.interface.ts +28 -0
- package/src/lib/rag/source/strategy.registry.ts +17 -0
- package/src/lib/rag/textsplitter/index.ts +3 -0
- package/src/lib/rag/textsplitter/strategy.decorator.ts +6 -0
- package/src/lib/rag/textsplitter/strategy.interface.ts +28 -0
- package/src/lib/rag/textsplitter/strategy.registry.ts +17 -0
- package/src/lib/rag/transformer/index.ts +3 -0
- package/src/lib/rag/transformer/strategy.decorator.ts +9 -0
- package/src/lib/rag/transformer/strategy.interface.ts +48 -0
- package/src/lib/rag/transformer/strategy.registry.ts +14 -0
- package/src/lib/rag/types.ts +86 -0
- package/src/lib/strategy.ts +37 -0
- package/src/lib/toolset/builtin.ts +111 -0
- package/src/lib/toolset/index.ts +5 -0
- package/src/lib/toolset/strategy.decorator.ts +9 -0
- package/src/lib/toolset/strategy.interface.ts +32 -0
- package/src/lib/toolset/strategy.registry.ts +17 -0
- package/src/lib/toolset/toolset.ts +76 -0
- package/src/lib/types.ts +47 -0
- package/src/lib/vectorstore/index.ts +3 -0
- package/src/lib/vectorstore/strategy.decorator.ts +6 -0
- package/src/lib/vectorstore/strategy.interface.ts +25 -0
- package/src/lib/vectorstore/strategy.registry.ts +17 -0
- package/src/lib/workflow/index.ts +2 -0
- package/src/lib/workflow/node/index.ts +3 -0
- package/src/lib/workflow/node/strategy.decorator.ts +9 -0
- package/src/lib/workflow/node/strategy.interface.ts +49 -0
- package/src/lib/workflow/node/strategy.registry.ts +18 -0
- package/src/lib/workflow/trigger/index.ts +3 -0
- package/src/lib/workflow/trigger/strategy.decorator.ts +6 -0
- package/src/lib/workflow/trigger/strategy.interface.ts +27 -0
- package/src/lib/workflow/trigger/strategy.registry.ts +17 -0
- package/tsconfig.json +22 -0
- package/tsconfig.lib.json +10 -0
- package/tsconfig.spec.json +9 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["../../.eslintrc.json"],
|
|
3
|
+
"ignorePatterns": ["!**/*"],
|
|
4
|
+
"overrides": [
|
|
5
|
+
{
|
|
6
|
+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
+
"rules": {}
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"files": ["*.ts", "*.tsx"],
|
|
11
|
+
"rules": {}
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"files": ["*.js", "*.jsx"],
|
|
15
|
+
"rules": {}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"files": ["*.json"],
|
|
19
|
+
"parser": "jsonc-eslint-parser",
|
|
20
|
+
"rules": {
|
|
21
|
+
"@nx/dependency-checks": [
|
|
22
|
+
"error",
|
|
23
|
+
{
|
|
24
|
+
"ignoredFiles": ["{projectRoot}/rollup.config.{js,ts,mjs,mts}"]
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
package/.swcrc
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"jsc": {
|
|
3
|
+
"target": "es2017",
|
|
4
|
+
"parser": {
|
|
5
|
+
"syntax": "typescript",
|
|
6
|
+
"decorators": true,
|
|
7
|
+
"dynamicImport": true
|
|
8
|
+
},
|
|
9
|
+
"transform": {
|
|
10
|
+
"decoratorMetadata": true,
|
|
11
|
+
"legacyDecorator": true
|
|
12
|
+
},
|
|
13
|
+
"keepClassNames": true,
|
|
14
|
+
"externalHelpers": true,
|
|
15
|
+
"loose": true
|
|
16
|
+
},
|
|
17
|
+
"module": {
|
|
18
|
+
"type": "es6"
|
|
19
|
+
},
|
|
20
|
+
"sourceMaps": true,
|
|
21
|
+
"exclude": [
|
|
22
|
+
"jest.config.ts",
|
|
23
|
+
".*\\.spec.tsx?$",
|
|
24
|
+
".*\\.test.tsx?$",
|
|
25
|
+
"./src/jest-setup.ts$",
|
|
26
|
+
"./**/jest-setup.ts$",
|
|
27
|
+
".*.js$"
|
|
28
|
+
]
|
|
29
|
+
}
|
package/PERMISSIONS.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# 插件权限设计指南
|
|
2
|
+
|
|
3
|
+
:::tip
|
|
4
|
+
在 meta 中设计一套可以向核心系统反向请求 大模型 调用权限的(如请求一个 OpenAI Client实例)的配置,例如在 meta中声明大模型的权限请求,在 configSchema 中设计属性让用户配置具体的模型提供商和模型名称,核心系统在识别到权限请求时会根据 config 中用户配置的具体参数实例化大模型 Client 通过 config 传入到 understandImages 这个方法:
|
|
5
|
+
:::
|
|
6
|
+
|
|
7
|
+
👌明白了,你希望 **在插件 Strategy 的 meta 中声明依赖的大模型权限请求**,核心系统解析后根据用户配置(模型提供商 + 模型名称 + 其他参数)自动注入一个 `client` 到 `config`,这样插件不用自己创建 OpenAI / Anthropic / 其他 VLM 客户端。
|
|
8
|
+
|
|
9
|
+
我帮你重新设计 `OpenAIGPT4VStrategy` 的 meta 和 configSchema,使它能 **反向请求核心系统提供的 Client**。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 新版 Strategy
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { Injectable } from '@nestjs/common';
|
|
17
|
+
import { Document } from 'langchain/document';
|
|
18
|
+
import {
|
|
19
|
+
ImageUnderstandingStrategy,
|
|
20
|
+
IImageUnderstandingStrategy,
|
|
21
|
+
TImageUnderstandingFile,
|
|
22
|
+
TImageUnderstandingResult
|
|
23
|
+
} from './strategy.interface';
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
@ImageUnderstandingStrategy('openai-gpt4v')
|
|
27
|
+
export class OpenAIGPT4VStrategy implements IImageUnderstandingStrategy<any> {
|
|
28
|
+
readonly meta = {
|
|
29
|
+
name: 'openai-gpt4v',
|
|
30
|
+
label: { en: 'OpenAI GPT-4 Vision', zh: 'OpenAI GPT-4 图像理解' },
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 权限声明:告诉核心系统,这个插件需要一个 "llm" 权限,
|
|
34
|
+
* 提供方是 openai,能力是 vision。
|
|
35
|
+
* 核心系统识别后会根据 config.provider + config.model 实例化对应的 Client,
|
|
36
|
+
* 并注入到 config.client
|
|
37
|
+
*/
|
|
38
|
+
permissions: [
|
|
39
|
+
{
|
|
40
|
+
type: 'llm',
|
|
41
|
+
provider: 'openai',
|
|
42
|
+
capability: 'vision',
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 配置 schema,用户需要填写 provider 和 model,
|
|
48
|
+
* 核心系统会根据 provider + model + 用户账号 API Key,
|
|
49
|
+
* 实例化好 Client 注入。
|
|
50
|
+
*/
|
|
51
|
+
configSchema: {
|
|
52
|
+
type: 'object',
|
|
53
|
+
properties: {
|
|
54
|
+
provider: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
enum: ['openai'],
|
|
57
|
+
description: '模型提供商'
|
|
58
|
+
},
|
|
59
|
+
model: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
default: 'gpt-4-vision-preview',
|
|
62
|
+
description: '大模型名称'
|
|
63
|
+
},
|
|
64
|
+
prompt: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
default: 'Describe this image in detail.',
|
|
67
|
+
description: '提示词(可以自定义理解任务)'
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
required: ['provider', 'model']
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
icon: {
|
|
74
|
+
svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" stroke="black" fill="none"/><text x="6" y="14">VLM</text></svg>',
|
|
75
|
+
color: '#10a37f'
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
async validateConfig(config: any): Promise<void> {
|
|
80
|
+
if (!config.client) {
|
|
81
|
+
throw new Error('Missing injected LLM client in config. Core system must inject client.');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async understandImages(
|
|
86
|
+
files: TImageUnderstandingFile[],
|
|
87
|
+
config: any
|
|
88
|
+
): Promise<TImageUnderstandingResult[]> {
|
|
89
|
+
const client = config.client; // ✅ 已由核心系统注入
|
|
90
|
+
const results: TImageUnderstandingResult[] = [];
|
|
91
|
+
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
const description = await this.runGPT4V(client, file.path, config);
|
|
94
|
+
|
|
95
|
+
const doc = new Document({
|
|
96
|
+
pageContent: description,
|
|
97
|
+
metadata: {
|
|
98
|
+
chunkId: `img-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
99
|
+
parentChunkId: file.parentChunkId,
|
|
100
|
+
imagePath: file.path,
|
|
101
|
+
source: file.filename,
|
|
102
|
+
type: 'vlm',
|
|
103
|
+
engine: config.model
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
results.push({
|
|
108
|
+
docs: [doc],
|
|
109
|
+
metadata: { engine: config.model, file: file.filename }
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async runGPT4V(client: any, imagePath: string, config: any): Promise<string> {
|
|
117
|
+
// client 已经是 openai client,由核心系统实例化
|
|
118
|
+
const response = await client.chat.completions.create({
|
|
119
|
+
model: config.model,
|
|
120
|
+
messages: [
|
|
121
|
+
{
|
|
122
|
+
role: 'user',
|
|
123
|
+
content: [
|
|
124
|
+
{ type: 'text', text: config.prompt || 'Describe this image in detail.' },
|
|
125
|
+
{ type: 'image_url', image_url: { url: imagePath } }
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
max_tokens: 500
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return response.choices[0]?.message?.content || '';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 🔑 核心系统如何处理
|
|
140
|
+
|
|
141
|
+
* 解析插件的 `meta.permissions`,发现需要:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{ "type": "llm", "provider": "openai", "capability": "vision" }
|
|
145
|
+
```
|
|
146
|
+
* 根据用户 `config.provider = "openai"`, `config.model = "gpt-4-vision-preview"`
|
|
147
|
+
实例化 `OpenAI({ apiKey: userApiKey })`
|
|
148
|
+
* 将 client 注入到 `config.client`
|
|
149
|
+
* 调用 `strategy.understandImages(files, config)`
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## 📌 使用示例
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const strategy = this.imageUnderstandingRegistry.get('openai-gpt4v');
|
|
157
|
+
const result = await strategy.understandImages(
|
|
158
|
+
[
|
|
159
|
+
{
|
|
160
|
+
path: 'https://example.com/chart.png',
|
|
161
|
+
filename: 'chart.png',
|
|
162
|
+
extname: 'png',
|
|
163
|
+
parentChunkId: 'uuid-123'
|
|
164
|
+
}
|
|
165
|
+
],
|
|
166
|
+
{
|
|
167
|
+
provider: 'openai',
|
|
168
|
+
model: 'gpt-4-vision-preview',
|
|
169
|
+
prompt: '请用中文详细描述这张图片。',
|
|
170
|
+
client: openaiClient // ✅ 核心系统注入
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
console.log(result[0].docs[0].pageContent);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
这样插件本身不依赖任何 `openai` 包装逻辑,**只声明权限 + 要求 provider/model**,核心系统就能注入合适的 client。
|
|
180
|
+
|
|
181
|
+
要不要我帮你写一个 **抽象的权限声明接口 (例如 ILLMPermissionRequest)**,这样 OCR/VLM/Embedding 插件都可以统一声明依赖?
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# plugin-sdk
|
|
2
|
+
|
|
3
|
+
This library was generated with [Nx](https://nx.dev).
|
|
4
|
+
|
|
5
|
+
## Building
|
|
6
|
+
|
|
7
|
+
Run `nx build plugin-sdk` to build the library.
|
|
8
|
+
|
|
9
|
+
## Running unit tests
|
|
10
|
+
|
|
11
|
+
Run `nx test plugin-sdk` to execute the unit tests via [Jest](https://jestjs.io).
|
|
12
|
+
|
|
13
|
+
## Schema config
|
|
14
|
+
|
|
15
|
+
[Schema UI 扩展规范](./SCHEMA_SPECIFICATION.md)
|
|
16
|
+
|
|
17
|
+
## Permissions
|
|
18
|
+
|
|
19
|
+
[插件权限设计指南](./PERMISSIONS.md)
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# 🔧 Schema UI 扩展规范
|
|
2
|
+
|
|
3
|
+
:::tip
|
|
4
|
+
设计一套 通用的 schema UI 扩展规范
|
|
5
|
+
:::
|
|
6
|
+
|
|
7
|
+
👌好的,我来帮你设计一套 **通用的 Schema UI 扩展规范**,可以应用在你的插件系统所有配置 schema 中,让前端渲染时有统一的规则。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
在 JSON Schema 基础上扩展一个保留字段:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
"x-ui": { ... }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
前端 UI 渲染器根据 `x-ui` 的内容决定用什么组件、怎么展示、怎么动态联动。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. 基础字段
|
|
22
|
+
|
|
23
|
+
| 字段名 | 类型 | 说明 |
|
|
24
|
+
| -------------- | ------ | ------------------------------------------------------------------------------------------------------------------------ |
|
|
25
|
+
| `component` | string | 指定 UI 组件类型,如 `textInput`, `textarea`, `select`, `switch`, `slider`, `promptEditor`, `modelProviderSelect`, `modelSelect` |
|
|
26
|
+
| `label` | string | UI 展示的标签 |
|
|
27
|
+
| `description` | string | UI 展示的帮助文本,优先覆盖 schema.description |
|
|
28
|
+
| `placeholder` | string | 输入占位符 |
|
|
29
|
+
| `defaultValue` | any | 默认值,优先覆盖 schema.default |
|
|
30
|
+
| `order` | number | 字段在 UI 中的展示顺序 |
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. 数据源相关
|
|
35
|
+
|
|
36
|
+
| 字段名 | 类型 | 说明 |
|
|
37
|
+
| ------------ | ------ | ---------------------------------------------------------- |
|
|
38
|
+
| `options` | array | 静态选项(下拉框、多选框等),形如 `[{ label: 'OpenAI', value: 'openai' }]` |
|
|
39
|
+
| `dataSource` | string | 动态选项来源标识,如 `"system.providers"`, `"system.models"` |
|
|
40
|
+
| `dependency` | string | 依赖其他字段的值来过滤选项,如 `"provider"` |
|
|
41
|
+
| `mapping` | object | 映射关系,例如 `{ label: 'name', value: 'id' }` |
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 3. 验证与交互
|
|
46
|
+
|
|
47
|
+
| 字段名 | 类型 | 说明 |
|
|
48
|
+
| ------------- | ------- | -------------------------------------------- |
|
|
49
|
+
| `required` | boolean | 是否必填(优先覆盖 schema.required) |
|
|
50
|
+
| `visibleWhen` | object | 条件渲染,只有满足条件才显示字段,例如 `{ provider: 'openai' }` |
|
|
51
|
+
| `enabledWhen` | object | 条件启用,例如 `{ useCustomPrompt: true }` |
|
|
52
|
+
| `maxLength` | number | 输入最大长度 |
|
|
53
|
+
| `minLength` | number | 输入最小长度 |
|
|
54
|
+
| `regex` | string | 自定义正则校验规则 |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 4. 特殊组件约定
|
|
59
|
+
|
|
60
|
+
### `modelProviderSelect`
|
|
61
|
+
|
|
62
|
+
* 用于选择模型提供商(OpenAI、Anthropic、Azure、Ollama 等)
|
|
63
|
+
* `dataSource = "system.providers"`
|
|
64
|
+
|
|
65
|
+
### `modelSelect`
|
|
66
|
+
|
|
67
|
+
* 用于选择具体模型
|
|
68
|
+
* 需要依赖 `provider`
|
|
69
|
+
* `dataSource = "system.models"`
|
|
70
|
+
* `dependency = "provider"`
|
|
71
|
+
|
|
72
|
+
### `promptEditor`
|
|
73
|
+
|
|
74
|
+
* 用于编辑 Prompt,支持多行、语法高亮、变量插值
|
|
75
|
+
|
|
76
|
+
### `jsonEditor`
|
|
77
|
+
|
|
78
|
+
* 用于编辑 JSON 对象,带格式校验
|
|
79
|
+
|
|
80
|
+
### `codeEditor`
|
|
81
|
+
|
|
82
|
+
* 用于编辑代码(支持语言高亮,如 js, ts, python)
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 5. 示例 Schema
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
configSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
properties: {
|
|
92
|
+
provider: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: '模型提供商',
|
|
95
|
+
enum: ['openai', 'anthropic', 'azure', 'ollama'],
|
|
96
|
+
'x-ui': {
|
|
97
|
+
component: 'modelProviderSelect',
|
|
98
|
+
label: 'Provider',
|
|
99
|
+
order: 1,
|
|
100
|
+
dataSource: 'system.providers'
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
model: {
|
|
104
|
+
type: 'string',
|
|
105
|
+
description: '大模型名称',
|
|
106
|
+
default: 'gpt-4-vision-preview',
|
|
107
|
+
'x-ui': {
|
|
108
|
+
component: 'modelSelect',
|
|
109
|
+
label: 'Model',
|
|
110
|
+
order: 2,
|
|
111
|
+
dependency: 'provider',
|
|
112
|
+
dataSource: 'system.models'
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
prompt: {
|
|
116
|
+
type: 'string',
|
|
117
|
+
default: 'Describe this image in detail.',
|
|
118
|
+
description: '提示词',
|
|
119
|
+
'x-ui': {
|
|
120
|
+
component: 'promptEditor',
|
|
121
|
+
label: 'Prompt',
|
|
122
|
+
order: 3,
|
|
123
|
+
placeholder: '请输入提示词,可使用 {{variables}} 占位符'
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
temperature: {
|
|
127
|
+
type: 'number',
|
|
128
|
+
default: 0.7,
|
|
129
|
+
'x-ui': {
|
|
130
|
+
component: 'slider',
|
|
131
|
+
label: 'Temperature',
|
|
132
|
+
order: 4,
|
|
133
|
+
min: 0,
|
|
134
|
+
max: 1,
|
|
135
|
+
step: 0.1
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
advanced: {
|
|
139
|
+
type: 'object',
|
|
140
|
+
'x-ui': {
|
|
141
|
+
component: 'jsonEditor',
|
|
142
|
+
label: 'Advanced Config',
|
|
143
|
+
order: 5,
|
|
144
|
+
visibleWhen: { provider: 'openai' }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
required: ['provider', 'model']
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
这样一来:
|
|
155
|
+
|
|
156
|
+
* 前端 UI 渲染器只需要识别 `x-ui.component`,其余逻辑由 `x-ui` 字段驱动。
|
|
157
|
+
* 插件开发者只需写 schema,就能自动获得智能化配置体验。
|
|
158
|
+
* 扩展性强,可以统一适配未来的 Embedding、OCR、VLM、VectorStore 插件。
|
|
159
|
+
|
|
160
|
+
## 如何表示 SECRET 的input 字段
|
|
161
|
+
|
|
162
|
+
好问题 👍,**SECRET 类型的字段**(比如 API Key、Token、密码)在 schema UI 里需要特殊处理:
|
|
163
|
+
|
|
164
|
+
1. **前端渲染时要显示为密码输入框**(`type="password"` 或遮罩)。
|
|
165
|
+
2. **存储时需要区分**:不回显原始值,而是用 `"******"` 或空值占位。
|
|
166
|
+
3. **Schema 中可以显式标记**,让前端 UI 渲染器知道这是一个密钥字段。
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 扩展规范方案
|
|
171
|
+
|
|
172
|
+
在 `x-ui` 里增加 `component: "secretInput"`,并支持以下属性:
|
|
173
|
+
|
|
174
|
+
| 字段名 | 类型 | 说明 |
|
|
175
|
+
| ------------- | ------- | --------------------------- |
|
|
176
|
+
| `component` | string | 固定为 `"secretInput"`,渲染密码输入框 |
|
|
177
|
+
| `label` | string | 字段标签 |
|
|
178
|
+
| `description` | string | 字段说明 |
|
|
179
|
+
| `placeholder` | string | 占位符(如 `请输入 API Key`) |
|
|
180
|
+
| `revealable` | boolean | 是否允许用户点按钮临时查看明文 |
|
|
181
|
+
| `maskSymbol` | string | 遮罩符号(默认 `*`) |
|
|
182
|
+
| `persist` | boolean | 是否持久保存,false 时表示仅运行时使用,不落盘 |
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 示例 Schema
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
configSchema: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
properties: {
|
|
192
|
+
apiKey: {
|
|
193
|
+
type: 'string',
|
|
194
|
+
description: 'OpenAI API Key',
|
|
195
|
+
'x-ui': {
|
|
196
|
+
component: 'secretInput',
|
|
197
|
+
label: 'API Key',
|
|
198
|
+
placeholder: '请输入您的 OpenAI API Key',
|
|
199
|
+
revealable: true,
|
|
200
|
+
maskSymbol: '*',
|
|
201
|
+
persist: true
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
provider: {
|
|
205
|
+
type: 'string',
|
|
206
|
+
enum: ['openai', 'anthropic', 'azure'],
|
|
207
|
+
'x-ui': {
|
|
208
|
+
component: 'modelProviderSelect',
|
|
209
|
+
label: 'Provider'
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
required: ['apiKey', 'provider']
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 前端渲染效果
|
|
220
|
+
|
|
221
|
+
* `apiKey` 显示为 **密码输入框**,默认遮罩显示。
|
|
222
|
+
* 如果 `revealable: true`,UI 上有 👁 按钮,用户可切换显示/隐藏。
|
|
223
|
+
* 编辑配置时,如果后端返回 `"******"` 代表已有值,用户不改就继续使用原值。
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 额外安全考虑
|
|
228
|
+
|
|
229
|
+
* 前端表单提交时,如果 `apiKey === "******"`,表示不修改,后端应保留旧值。
|
|
230
|
+
* 如果提交了新值,则后端替换旧值。
|
|
231
|
+
* 日志、导出配置时跳过 SECRET 字段。
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
要不要我帮你把 **SECRET 字段** 再抽象成一个通用接口(比如 `ISchemaSecretField`),这样和普通字段区分得更清晰?
|
|
236
|
+
|
|
237
|
+
## 抽象成一个通用接口
|
|
238
|
+
|
|
239
|
+
👌好的,我们把 **SECRET 字段**抽象成一个通用接口,既能和普通字段保持一致,又能明确标识“这是一个需要特殊处理的敏感字段”。
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
# 🔐 通用接口设计
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
/**
|
|
247
|
+
* 通用 UI Schema 字段定义
|
|
248
|
+
*/
|
|
249
|
+
export interface ISchemaUIBase {
|
|
250
|
+
component: string; // UI 组件类型
|
|
251
|
+
label?: string; // 字段标签
|
|
252
|
+
description?: string; // 字段说明
|
|
253
|
+
placeholder?: string; // 输入占位符
|
|
254
|
+
order?: number; // UI 显示顺序
|
|
255
|
+
required?: boolean; // 是否必填
|
|
256
|
+
visibleWhen?: Record<string, any>; // 条件渲染
|
|
257
|
+
enabledWhen?: Record<string, any>; // 条件启用
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Secret 字段扩展
|
|
262
|
+
*/
|
|
263
|
+
export interface ISchemaSecretField extends ISchemaUIBase {
|
|
264
|
+
component: 'secretInput'; // 固定组件类型
|
|
265
|
+
revealable?: boolean; // 是否允许明文显示(👁 按钮)
|
|
266
|
+
maskSymbol?: string; // 遮罩符号(默认 *)
|
|
267
|
+
persist?: boolean; // 是否持久保存,false 表示仅运行时使用
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
这样 SECRET 字段就和普通 UI 字段继承同一套规范,只是 **多了一些敏感信息处理的扩展属性**。
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
# 🔧 使用示例
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
configSchema: {
|
|
279
|
+
type: 'object',
|
|
280
|
+
properties: {
|
|
281
|
+
apiKey: {
|
|
282
|
+
type: 'string',
|
|
283
|
+
description: 'OpenAI API Key',
|
|
284
|
+
'x-ui': <ISchemaSecretField>{
|
|
285
|
+
component: 'secretInput',
|
|
286
|
+
label: 'API Key',
|
|
287
|
+
placeholder: '请输入您的 OpenAI API Key',
|
|
288
|
+
revealable: true,
|
|
289
|
+
maskSymbol: '*',
|
|
290
|
+
persist: true
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
provider: {
|
|
294
|
+
type: 'string',
|
|
295
|
+
enum: ['openai', 'anthropic', 'azure'],
|
|
296
|
+
'x-ui': {
|
|
297
|
+
component: 'modelProviderSelect',
|
|
298
|
+
label: 'Provider'
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
required: ['apiKey', 'provider']
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
# 🔒 前端处理规范
|
|
309
|
+
|
|
310
|
+
1. **渲染时**:
|
|
311
|
+
|
|
312
|
+
* `secretInput` 渲染为密码框(`type=password`)。
|
|
313
|
+
* 如果 `revealable: true`,增加 👁 切换按钮。
|
|
314
|
+
|
|
315
|
+
2. **加载配置时**:
|
|
316
|
+
|
|
317
|
+
* 后端返回 `******` 代表已有值,前端不展示真实内容。
|
|
318
|
+
|
|
319
|
+
3. **保存配置时**:
|
|
320
|
+
|
|
321
|
+
* 如果提交的值仍为 `******` → 保持旧值不变。
|
|
322
|
+
* 如果提交了新值 → 更新存储并覆盖旧值。
|
|
323
|
+
|
|
324
|
+
4. **导出配置/日志**:
|
|
325
|
+
|
|
326
|
+
* 忽略 SECRET 字段或输出 `******`,避免泄露。
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
这样以后 `secretInput` 就是一个 **一级公民 UI 组件**,和 `textInput`、`select` 一样可以被 schema 统一驱动。
|
|
331
|
+
|
|
332
|
+
要不要我再帮你定义一个 **完整的 ISchemaUIComponent 联合类型**,把所有 `x-ui` 组件(select、slider、secretInput、promptEditor 等)都规范起来?
|
package/jest.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import { readFileSync } from 'fs'
|
|
3
|
+
|
|
4
|
+
// Reading the SWC compilation config and remove the "exclude"
|
|
5
|
+
// for the test files to be compiled by SWC
|
|
6
|
+
const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(`${__dirname}/.swcrc`, 'utf-8'))
|
|
7
|
+
|
|
8
|
+
// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
|
|
9
|
+
// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
|
|
10
|
+
if (swcJestConfig.swcrc === undefined) {
|
|
11
|
+
swcJestConfig.swcrc = false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Uncomment if using global setup/teardown files being transformed via swc
|
|
15
|
+
// https://nx.dev/packages/jest/documents/overview#global-setup/teardown-with-nx-libraries
|
|
16
|
+
// jest needs EsModule Interop to find the default exported setup/teardown functions
|
|
17
|
+
// swcJestConfig.module.noInterop = false;
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
displayName: 'plugin-sdk',
|
|
21
|
+
preset: '../../jest.preset.js',
|
|
22
|
+
transform: {
|
|
23
|
+
'^.+\\.[tj]s$': ['@swc/jest', swcJestConfig]
|
|
24
|
+
},
|
|
25
|
+
moduleFileExtensions: ['ts', 'js', 'html'],
|
|
26
|
+
testEnvironment: 'node',
|
|
27
|
+
coverageDirectory: '../../coverage/packages/plugin-sdk'
|
|
28
|
+
}
|
package/package.json
ADDED
package/project.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "plugin-sdk",
|
|
3
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
+
"sourceRoot": "packages/plugin-sdk/src",
|
|
5
|
+
"projectType": "library",
|
|
6
|
+
"tags": [],
|
|
7
|
+
"targets": {
|
|
8
|
+
"build": {
|
|
9
|
+
"executor": "@nx/rollup:rollup",
|
|
10
|
+
"outputs": ["{options.outputPath}"],
|
|
11
|
+
"options": {
|
|
12
|
+
"outputPath": "dist/packages/plugin-sdk",
|
|
13
|
+
"main": "packages/plugin-sdk/src/index.ts",
|
|
14
|
+
"tsConfig": "packages/plugin-sdk/tsconfig.lib.json",
|
|
15
|
+
"assets": [],
|
|
16
|
+
"project": "packages/plugin-sdk/package.json",
|
|
17
|
+
"compiler": "swc",
|
|
18
|
+
"format": ["cjs", "esm"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"test": {
|
|
22
|
+
"executor": "@nx/jest:jest",
|
|
23
|
+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
|
24
|
+
"options": {
|
|
25
|
+
"jestConfig": "packages/plugin-sdk/jest.config.ts"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './lib/plugin'
|
|
2
|
+
export * from './lib/plugin.interface'
|
|
3
|
+
export * from './lib/plugin-metadata'
|
|
4
|
+
export * from './lib/types'
|
|
5
|
+
export * from './lib/logger'
|
|
6
|
+
export * from './lib/knowledge'
|
|
7
|
+
export * from './lib/integration/index'
|
|
8
|
+
export * from './lib/workflow/index'
|
|
9
|
+
export * from './lib/vectorstore/index'
|
|
10
|
+
export * from './lib/rag/index'
|
|
11
|
+
export * from './lib/toolset/index'
|
|
12
|
+
export * from './lib/core/index'
|