plugin-ai-api 1.0.3 → 1.0.4
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/README.md +15 -1
- package/client-v2.d.ts +2 -0
- package/client-v2.js +1 -0
- package/dist/client/778.180aac1b13a38702.js +10 -0
- package/dist/client/950.2797862732563625.js +10 -0
- package/dist/client/index.js +1 -1
- package/dist/client-v2/950.8991b849cd7c1351.js +10 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/externalVersion.js +9 -8
- package/package.json +32 -14
- package/src/client/AiApiConfigPage.tsx +309 -0
- package/src/client/client.d.ts +258 -0
- package/src/client/components/AiApiRolePermissions.tsx +169 -0
- package/src/client/index.tsx +10 -0
- package/src/client/locale.ts +21 -0
- package/src/client/models/index.ts +12 -0
- package/src/client/plugin.tsx +48 -0
- package/src/client-v2/index.tsx +1 -0
- package/src/client-v2/plugin.tsx +24 -0
- package/src/index.ts +11 -0
- package/src/locale/en-US.json +10 -0
- package/src/locale/zh-CN.json +10 -0
- package/src/server/collections/.gitkeep +0 -0
- package/src/server/collections/ai-api-config.ts +51 -0
- package/src/server/collections/ai-api-role-permissions.ts +41 -0
- package/src/server/index.ts +10 -0
- package/src/server/middleware/rate-limit.ts +70 -0
- package/src/server/middleware/role-permission.ts +66 -0
- package/src/server/plugin.ts +89 -0
- package/src/server/resource/ai-api-config.ts +74 -0
- package/src/server/routes/agent-completions.ts +428 -0
- package/src/server/routes/auth.ts +111 -0
- package/src/server/routes/chat-completions.ts +318 -0
- package/src/server/routes/completions.ts +299 -0
- package/src/server/routes/embeddings.ts +191 -0
- package/src/server/routes/models.ts +195 -0
- package/src/server/routes/router.ts +283 -0
- package/src/server/utils/openai-format.ts +142 -0
- package/src/server/utils/rate-limiter.ts +83 -0
- package/src/server/utils/resolve-service.ts +82 -0
- package/src/swagger.ts +325 -0
- package/dist/client/23.e96ecf13e6072dce.js +0 -10
- package/dist/client/503.29bcdb426b01e715.js +0 -10
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* This file is part of the NocoBase (R) project.
|
|
12
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
13
|
+
* Authors: NocoBase Team.
|
|
14
|
+
*
|
|
15
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
16
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// CSS modules
|
|
20
|
+
type CSSModuleClasses = { readonly [key: string]: string };
|
|
21
|
+
|
|
22
|
+
declare module '*.module.css' {
|
|
23
|
+
const classes: CSSModuleClasses;
|
|
24
|
+
export default classes;
|
|
25
|
+
}
|
|
26
|
+
declare module '*.module.scss' {
|
|
27
|
+
const classes: CSSModuleClasses;
|
|
28
|
+
export default classes;
|
|
29
|
+
}
|
|
30
|
+
declare module '*.module.sass' {
|
|
31
|
+
const classes: CSSModuleClasses;
|
|
32
|
+
export default classes;
|
|
33
|
+
}
|
|
34
|
+
declare module '*.module.less' {
|
|
35
|
+
const classes: CSSModuleClasses;
|
|
36
|
+
export default classes;
|
|
37
|
+
}
|
|
38
|
+
declare module '*.module.styl' {
|
|
39
|
+
const classes: CSSModuleClasses;
|
|
40
|
+
export default classes;
|
|
41
|
+
}
|
|
42
|
+
declare module '*.module.stylus' {
|
|
43
|
+
const classes: CSSModuleClasses;
|
|
44
|
+
export default classes;
|
|
45
|
+
}
|
|
46
|
+
declare module '*.module.pcss' {
|
|
47
|
+
const classes: CSSModuleClasses;
|
|
48
|
+
export default classes;
|
|
49
|
+
}
|
|
50
|
+
declare module '*.module.sss' {
|
|
51
|
+
const classes: CSSModuleClasses;
|
|
52
|
+
export default classes;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// CSS
|
|
56
|
+
declare module '*.css' { }
|
|
57
|
+
declare module '*.scss' { }
|
|
58
|
+
declare module '*.sass' { }
|
|
59
|
+
declare module '*.less' { }
|
|
60
|
+
declare module '*.styl' { }
|
|
61
|
+
declare module '*.stylus' { }
|
|
62
|
+
declare module '*.pcss' { }
|
|
63
|
+
declare module '*.sss' { }
|
|
64
|
+
|
|
65
|
+
// Built-in asset types
|
|
66
|
+
// see `src/node/constants.ts`
|
|
67
|
+
|
|
68
|
+
// images
|
|
69
|
+
declare module '*.apng' {
|
|
70
|
+
const src: string;
|
|
71
|
+
export default src;
|
|
72
|
+
}
|
|
73
|
+
declare module '*.png' {
|
|
74
|
+
const src: string;
|
|
75
|
+
export default src;
|
|
76
|
+
}
|
|
77
|
+
declare module '*.jpg' {
|
|
78
|
+
const src: string;
|
|
79
|
+
export default src;
|
|
80
|
+
}
|
|
81
|
+
declare module '*.jpeg' {
|
|
82
|
+
const src: string;
|
|
83
|
+
export default src;
|
|
84
|
+
}
|
|
85
|
+
declare module '*.jfif' {
|
|
86
|
+
const src: string;
|
|
87
|
+
export default src;
|
|
88
|
+
}
|
|
89
|
+
declare module '*.pjpeg' {
|
|
90
|
+
const src: string;
|
|
91
|
+
export default src;
|
|
92
|
+
}
|
|
93
|
+
declare module '*.pjp' {
|
|
94
|
+
const src: string;
|
|
95
|
+
export default src;
|
|
96
|
+
}
|
|
97
|
+
declare module '*.gif' {
|
|
98
|
+
const src: string;
|
|
99
|
+
export default src;
|
|
100
|
+
}
|
|
101
|
+
declare module '*.svg' {
|
|
102
|
+
const src: string;
|
|
103
|
+
export default src;
|
|
104
|
+
}
|
|
105
|
+
declare module '*.ico' {
|
|
106
|
+
const src: string;
|
|
107
|
+
export default src;
|
|
108
|
+
}
|
|
109
|
+
declare module '*.webp' {
|
|
110
|
+
const src: string;
|
|
111
|
+
export default src;
|
|
112
|
+
}
|
|
113
|
+
declare module '*.avif' {
|
|
114
|
+
const src: string;
|
|
115
|
+
export default src;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// media
|
|
119
|
+
declare module '*.mp4' {
|
|
120
|
+
const src: string;
|
|
121
|
+
export default src;
|
|
122
|
+
}
|
|
123
|
+
declare module '*.webm' {
|
|
124
|
+
const src: string;
|
|
125
|
+
export default src;
|
|
126
|
+
}
|
|
127
|
+
declare module '*.ogg' {
|
|
128
|
+
const src: string;
|
|
129
|
+
export default src;
|
|
130
|
+
}
|
|
131
|
+
declare module '*.mp3' {
|
|
132
|
+
const src: string;
|
|
133
|
+
export default src;
|
|
134
|
+
}
|
|
135
|
+
declare module '*.wav' {
|
|
136
|
+
const src: string;
|
|
137
|
+
export default src;
|
|
138
|
+
}
|
|
139
|
+
declare module '*.flac' {
|
|
140
|
+
const src: string;
|
|
141
|
+
export default src;
|
|
142
|
+
}
|
|
143
|
+
declare module '*.aac' {
|
|
144
|
+
const src: string;
|
|
145
|
+
export default src;
|
|
146
|
+
}
|
|
147
|
+
declare module '*.opus' {
|
|
148
|
+
const src: string;
|
|
149
|
+
export default src;
|
|
150
|
+
}
|
|
151
|
+
declare module '*.mov' {
|
|
152
|
+
const src: string;
|
|
153
|
+
export default src;
|
|
154
|
+
}
|
|
155
|
+
declare module '*.m4a' {
|
|
156
|
+
const src: string;
|
|
157
|
+
export default src;
|
|
158
|
+
}
|
|
159
|
+
declare module '*.vtt' {
|
|
160
|
+
const src: string;
|
|
161
|
+
export default src;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// fonts
|
|
165
|
+
declare module '*.woff' {
|
|
166
|
+
const src: string;
|
|
167
|
+
export default src;
|
|
168
|
+
}
|
|
169
|
+
declare module '*.woff2' {
|
|
170
|
+
const src: string;
|
|
171
|
+
export default src;
|
|
172
|
+
}
|
|
173
|
+
declare module '*.eot' {
|
|
174
|
+
const src: string;
|
|
175
|
+
export default src;
|
|
176
|
+
}
|
|
177
|
+
declare module '*.ttf' {
|
|
178
|
+
const src: string;
|
|
179
|
+
export default src;
|
|
180
|
+
}
|
|
181
|
+
declare module '*.otf' {
|
|
182
|
+
const src: string;
|
|
183
|
+
export default src;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// other
|
|
187
|
+
declare module '*.webmanifest' {
|
|
188
|
+
const src: string;
|
|
189
|
+
export default src;
|
|
190
|
+
}
|
|
191
|
+
declare module '*.pdf' {
|
|
192
|
+
const src: string;
|
|
193
|
+
export default src;
|
|
194
|
+
}
|
|
195
|
+
declare module '*.txt' {
|
|
196
|
+
const src: string;
|
|
197
|
+
export default src;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// wasm?init
|
|
201
|
+
declare module '*.wasm?init' {
|
|
202
|
+
const initWasm: (options?: WebAssembly.Imports) => Promise<WebAssembly.Instance>;
|
|
203
|
+
export default initWasm;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// web worker
|
|
207
|
+
declare module '*?worker' {
|
|
208
|
+
const workerConstructor: {
|
|
209
|
+
new(options?: { name?: string }): Worker;
|
|
210
|
+
};
|
|
211
|
+
export default workerConstructor;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
declare module '*?worker&inline' {
|
|
215
|
+
const workerConstructor: {
|
|
216
|
+
new(options?: { name?: string }): Worker;
|
|
217
|
+
};
|
|
218
|
+
export default workerConstructor;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
declare module '*?worker&url' {
|
|
222
|
+
const src: string;
|
|
223
|
+
export default src;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
declare module '*?sharedworker' {
|
|
227
|
+
const sharedWorkerConstructor: {
|
|
228
|
+
new(options?: { name?: string }): SharedWorker;
|
|
229
|
+
};
|
|
230
|
+
export default sharedWorkerConstructor;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
declare module '*?sharedworker&inline' {
|
|
234
|
+
const sharedWorkerConstructor: {
|
|
235
|
+
new(options?: { name?: string }): SharedWorker;
|
|
236
|
+
};
|
|
237
|
+
export default sharedWorkerConstructor;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
declare module '*?sharedworker&url' {
|
|
241
|
+
const src: string;
|
|
242
|
+
export default src;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
declare module '*?raw' {
|
|
246
|
+
const src: string;
|
|
247
|
+
export default src;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
declare module '*?url' {
|
|
251
|
+
const src: string;
|
|
252
|
+
export default src;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
declare module '*?inline' {
|
|
256
|
+
const src: string;
|
|
257
|
+
export default src;
|
|
258
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React, { useEffect, useState } from 'react';
|
|
11
|
+
import { useAPIClient } from '@nocobase/client';
|
|
12
|
+
import { Card, Switch, Select, Space, Typography, Spin, Divider } from 'antd';
|
|
13
|
+
|
|
14
|
+
const { Text } = Typography;
|
|
15
|
+
|
|
16
|
+
interface AiEmployee {
|
|
17
|
+
username: string;
|
|
18
|
+
nickname: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface RolePermRecord {
|
|
22
|
+
id?: number;
|
|
23
|
+
roleName: string;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
allowAllEmployees: boolean;
|
|
26
|
+
allowedEmployees: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Permission tab shown in Settings → Users & Permissions → [Role] → "AI API" tab.
|
|
31
|
+
* Lets admins control whether a role can use the AI API and which AI Employees it may access.
|
|
32
|
+
*/
|
|
33
|
+
export function AiApiRolePermissions({ role }: { role: any }) {
|
|
34
|
+
const api = useAPIClient();
|
|
35
|
+
const [loading, setLoading] = useState(true);
|
|
36
|
+
const [saving, setSaving] = useState(false);
|
|
37
|
+
const [employees, setEmployees] = useState<AiEmployee[]>([]);
|
|
38
|
+
const [record, setRecord] = useState<RolePermRecord | null>(null);
|
|
39
|
+
|
|
40
|
+
const roleName = role?.name;
|
|
41
|
+
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!roleName) return;
|
|
44
|
+
loadData();
|
|
45
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
+
}, [roleName]);
|
|
47
|
+
|
|
48
|
+
const loadData = async () => {
|
|
49
|
+
setLoading(true);
|
|
50
|
+
try {
|
|
51
|
+
const [permRes, empRes] = await Promise.all([
|
|
52
|
+
api.request({
|
|
53
|
+
url: 'aiApiRolePermissions',
|
|
54
|
+
params: { filter: { roleName }, paginate: false },
|
|
55
|
+
}),
|
|
56
|
+
api.request({ url: 'aiEmployees:list', params: { paginate: false } }),
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
const existing = permRes?.data?.data?.[0];
|
|
60
|
+
setRecord(
|
|
61
|
+
existing
|
|
62
|
+
? {
|
|
63
|
+
id: existing.id,
|
|
64
|
+
roleName: existing.roleName,
|
|
65
|
+
enabled: !!existing.enabled,
|
|
66
|
+
allowAllEmployees: existing.allowAllEmployees !== false,
|
|
67
|
+
allowedEmployees: existing.allowedEmployees || [],
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
70
|
+
roleName,
|
|
71
|
+
enabled: false,
|
|
72
|
+
allowAllEmployees: true,
|
|
73
|
+
allowedEmployees: [],
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
setEmployees(
|
|
78
|
+
(empRes?.data?.data || []).map((e: any) => ({
|
|
79
|
+
username: e.username,
|
|
80
|
+
nickname: e.nickname || e.username,
|
|
81
|
+
})),
|
|
82
|
+
);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error('Failed to load AI API role permissions:', err);
|
|
85
|
+
} finally {
|
|
86
|
+
setLoading(false);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const save = async (patch: Partial<RolePermRecord>) => {
|
|
91
|
+
if (!record) return;
|
|
92
|
+
const next = { ...record, ...patch };
|
|
93
|
+
setRecord(next);
|
|
94
|
+
setSaving(true);
|
|
95
|
+
try {
|
|
96
|
+
if (next.id) {
|
|
97
|
+
await api.request({
|
|
98
|
+
url: `aiApiRolePermissions/${next.id}`,
|
|
99
|
+
method: 'PUT',
|
|
100
|
+
data: next,
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
const res = await api.request({
|
|
104
|
+
url: 'aiApiRolePermissions',
|
|
105
|
+
method: 'POST',
|
|
106
|
+
data: next,
|
|
107
|
+
});
|
|
108
|
+
const created = res?.data?.data;
|
|
109
|
+
if (created?.id) {
|
|
110
|
+
setRecord({ ...next, id: created.id });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error('Failed to save AI API role permissions:', err);
|
|
115
|
+
// Revert on error
|
|
116
|
+
setRecord(record);
|
|
117
|
+
} finally {
|
|
118
|
+
setSaving(false);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (loading) return <Spin />;
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Card bordered={false}>
|
|
126
|
+
<Space direction="vertical" style={{ width: '100%' }} size="middle">
|
|
127
|
+
<Space>
|
|
128
|
+
<Switch checked={!!record?.enabled} loading={saving} onChange={(checked) => save({ enabled: checked })} />
|
|
129
|
+
<Text strong>Allow this role to use the AI API</Text>
|
|
130
|
+
</Space>
|
|
131
|
+
|
|
132
|
+
{record?.enabled && (
|
|
133
|
+
<>
|
|
134
|
+
<Divider style={{ margin: '8px 0' }} />
|
|
135
|
+
<Space>
|
|
136
|
+
<Switch
|
|
137
|
+
checked={!!record?.allowAllEmployees}
|
|
138
|
+
loading={saving}
|
|
139
|
+
onChange={(checked) => save({ allowAllEmployees: checked })}
|
|
140
|
+
/>
|
|
141
|
+
<Text>Allow all AI Employees</Text>
|
|
142
|
+
</Space>
|
|
143
|
+
|
|
144
|
+
{!record?.allowAllEmployees && (
|
|
145
|
+
<div>
|
|
146
|
+
<Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
|
147
|
+
Select which AI Employees this role may use:
|
|
148
|
+
</Text>
|
|
149
|
+
<Select
|
|
150
|
+
mode="multiple"
|
|
151
|
+
allowClear
|
|
152
|
+
style={{ width: '100%', maxWidth: 480 }}
|
|
153
|
+
placeholder="Select allowed AI Employees"
|
|
154
|
+
value={record?.allowedEmployees || []}
|
|
155
|
+
options={employees.map((e) => ({
|
|
156
|
+
label: `${e.nickname} (${e.username})`,
|
|
157
|
+
value: e.username,
|
|
158
|
+
}))}
|
|
159
|
+
onChange={(vals) => save({ allowedEmployees: vals })}
|
|
160
|
+
disabled={saving}
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
</>
|
|
165
|
+
)}
|
|
166
|
+
</Space>
|
|
167
|
+
</Card>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { default } from './plugin';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { tExpr as _tExpr, useFlowEngine } from '@nocobase/flow-engine';
|
|
11
|
+
// @ts-ignore
|
|
12
|
+
import pkg from './../../package.json';
|
|
13
|
+
|
|
14
|
+
export function useT() {
|
|
15
|
+
const engine = useFlowEngine();
|
|
16
|
+
return (str: string) => engine.context.t(str, { ns: [pkg.name, 'client'] });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function tExpr(key: string) {
|
|
20
|
+
return _tExpr(key, { ns: [pkg.name, 'client'] });
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ModelConstructor } from '@nocobase/flow-engine';
|
|
11
|
+
|
|
12
|
+
export default {} as Record<string, ModelConstructor>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Plugin, lazy } from '@nocobase/client';
|
|
11
|
+
import PluginACLClient from '@nocobase/plugin-acl/client';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
|
|
14
|
+
const AiApiConfigPage = React.lazy(() => import('./AiApiConfigPage'));
|
|
15
|
+
const { AiApiRolePermissions } = lazy(() => import('./components/AiApiRolePermissions'), 'AiApiRolePermissions');
|
|
16
|
+
|
|
17
|
+
export class PluginAiApiClient extends Plugin {
|
|
18
|
+
async load() {
|
|
19
|
+
this.app.pluginSettingsManager.add('ai-api', {
|
|
20
|
+
icon: 'ApiOutlined',
|
|
21
|
+
title: this.t('AI API Gateway'),
|
|
22
|
+
aclSnippet: 'pm.ai-api.configuration',
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
this.app.pluginSettingsManager.add('ai-api.config', {
|
|
26
|
+
title: this.t('Configuration'),
|
|
27
|
+
Component: AiApiConfigPage,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Add "AI API" tab in Settings → Users & Permissions → [Role]
|
|
31
|
+
const aclPlugin = this.app.pm.get(PluginACLClient);
|
|
32
|
+
if (aclPlugin?.settingsUI) {
|
|
33
|
+
aclPlugin.settingsUI.addPermissionsTab(({ t, TabLayout, activeRole }) => ({
|
|
34
|
+
key: 'aiApi',
|
|
35
|
+
label: 'AI API',
|
|
36
|
+
sort: 25,
|
|
37
|
+
children: (
|
|
38
|
+
<TabLayout>
|
|
39
|
+
<AiApiRolePermissions role={activeRole} />
|
|
40
|
+
</TabLayout>
|
|
41
|
+
),
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default PluginAiApiClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './plugin';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Plugin, Application } from '@nocobase/client-v2';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
export class PluginAiApiClient extends Plugin<Record<string, never>, Application> {
|
|
5
|
+
async load() {
|
|
6
|
+
this.pluginSettingsManager.addMenuItem({
|
|
7
|
+
key: 'ai-api',
|
|
8
|
+
title: this.t('AI API Gateway'),
|
|
9
|
+
icon: 'ApiOutlined',
|
|
10
|
+
aclSnippet: 'pm.ai-api.configuration',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
this.pluginSettingsManager.addPageTabItem({
|
|
14
|
+
menuKey: 'ai-api',
|
|
15
|
+
key: 'index',
|
|
16
|
+
title: this.t('Configuration'),
|
|
17
|
+
|
|
18
|
+
componentLoader: () => import('../client/AiApiConfigPage'),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default PluginAiApiClient;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from './server';
|
|
11
|
+
export { default } from './server';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AI API Gateway": "AI API Gateway",
|
|
3
|
+
"Configuration": "Configuration",
|
|
4
|
+
"Default AI Employee": "Default AI Employee",
|
|
5
|
+
"Enabled LLM Services": "Enabled LLM Services",
|
|
6
|
+
"Rate Limit": "Rate Limit",
|
|
7
|
+
"Save Configuration": "Save Configuration",
|
|
8
|
+
"Configuration saved": "Configuration saved",
|
|
9
|
+
"Failed to save configuration": "Failed to save configuration"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AI API Gateway": "AI API 网关",
|
|
3
|
+
"Configuration": "配置",
|
|
4
|
+
"Default AI Employee": "默认 AI 员工",
|
|
5
|
+
"Enabled LLM Services": "已启用 LLM 服务",
|
|
6
|
+
"Rate Limit": "速率限制",
|
|
7
|
+
"Save Configuration": "保存配置",
|
|
8
|
+
"Configuration saved": "配置已保存",
|
|
9
|
+
"Failed to save configuration": "保存配置失败"
|
|
10
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { defineCollection } from '@nocobase/database';
|
|
11
|
+
|
|
12
|
+
export default defineCollection({
|
|
13
|
+
name: 'aiApiConfig',
|
|
14
|
+
autoGenId: true,
|
|
15
|
+
fields: [
|
|
16
|
+
{
|
|
17
|
+
name: 'mode',
|
|
18
|
+
type: 'string',
|
|
19
|
+
defaultValue: 'llm',
|
|
20
|
+
comment: "API mode: 'llm' = direct LLM proxy, 'agent' = full AI Employee agent with tools/RAG",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'defaultAiEmployee',
|
|
24
|
+
type: 'string',
|
|
25
|
+
comment: 'Username of the default AI Employee for system prompt injection',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'defaultLlmService',
|
|
29
|
+
type: 'string',
|
|
30
|
+
comment: 'Name (UID) of the default LLM service. Clients can send just modelId without service prefix.',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'enabledLlmServices',
|
|
34
|
+
type: 'json',
|
|
35
|
+
defaultValue: [],
|
|
36
|
+
comment: 'Array of llmService names to expose. Empty = expose all enabled services',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'rateLimitPerMinute',
|
|
40
|
+
type: 'integer',
|
|
41
|
+
defaultValue: 60,
|
|
42
|
+
comment: 'Max requests per user per minute',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'options',
|
|
46
|
+
type: 'jsonb',
|
|
47
|
+
defaultValue: {},
|
|
48
|
+
comment: 'Reserved for future extensibility',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { defineCollection } from '@nocobase/database';
|
|
11
|
+
|
|
12
|
+
export default defineCollection({
|
|
13
|
+
name: 'aiApiRolePermissions',
|
|
14
|
+
autoGenId: true,
|
|
15
|
+
fields: [
|
|
16
|
+
{
|
|
17
|
+
name: 'roleName',
|
|
18
|
+
type: 'string',
|
|
19
|
+
unique: true,
|
|
20
|
+
comment: 'Role name (links to roles.name)',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'enabled',
|
|
24
|
+
type: 'boolean',
|
|
25
|
+
defaultValue: false,
|
|
26
|
+
comment: 'Whether this role can use the AI API at all',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'allowAllEmployees',
|
|
30
|
+
type: 'boolean',
|
|
31
|
+
defaultValue: true,
|
|
32
|
+
comment: 'If true, the role may use any AI Employee. If false, only those in allowedEmployees.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'allowedEmployees',
|
|
36
|
+
type: 'json',
|
|
37
|
+
defaultValue: [],
|
|
38
|
+
comment: 'Array of AI Employee usernames this role is allowed to use (when allowAllEmployees=false)',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { default } from './plugin';
|