plugin-ai-api 1.0.24 → 1.0.28
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/client/{286.01c0e3c5fff3cccb.js → 286.a1ee0420172cd5de.js} +1 -1
- package/dist/client/302.fbc46ebf5bf300d7.js +10 -0
- package/dist/client/562.44b16aad4718b4c7.js +10 -0
- package/dist/client/685.ae483e17b6b49c98.js +10 -0
- package/dist/client/757.6568d3504ad29352.js +10 -0
- package/dist/client/{97.72979a11a067a7c9.js → 97.9b6b2d2b01a4c060.js} +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client-v2/302.3971233415999b2c.js +10 -0
- package/dist/client-v2/562.45d5c504433be38b.js +10 -0
- package/dist/client-v2/685.1030370b309b7d4b.js +10 -0
- package/dist/client-v2/757.f2bc9cfba07004b0.js +10 -0
- package/dist/client-v2/{952.94100128b7757f56.js → 952.f0249eddc153bde1.js} +1 -1
- package/dist/client-v2/{97.29c663318eebbd57.js → 97.36a42eff36bb3d8a.js} +1 -1
- package/dist/client-v2/index.js +1 -1
- package/dist/constants.js +2 -5
- package/dist/externalVersion.js +8 -8
- package/dist/locale/en-US.json +27 -8
- package/dist/locale/vi-VN.json +27 -8
- package/dist/locale/zh-CN.json +27 -8
- package/dist/server/billing.js +31 -33
- package/dist/server/collections/ai-api-config.js +7 -7
- package/dist/server/collections/ai-api-group-members.js +62 -0
- package/dist/server/collections/ai-api-group-quota-buckets.js +63 -0
- package/dist/server/collections/ai-api-model-metadata.js +6 -0
- package/dist/server/collections/ai-api-usage-groups.js +74 -0
- package/dist/server/collections/ai-api-usage-records.js +2 -0
- package/dist/server/middleware/rate-limit.js +7 -6
- package/dist/server/migrations/20260813000000-add-prompt-cache-tokens.js +69 -0
- package/dist/server/migrations/20260815000000-add-usage-groups.js +149 -0
- package/dist/server/migrations/20260816000000-migrate-user-permissions-to-groups.js +169 -0
- package/dist/server/migrations/20260816100000-add-model-metadata-system-prompt.js +69 -0
- package/dist/server/plugin.js +100 -22
- package/dist/server/quota-groups.js +108 -0
- package/dist/server/resource/ai-api-config.js +5 -3
- package/dist/server/resource/ai-api-usage-groups.js +168 -0
- package/dist/server/resource/ai-api-usage-monitor.js +3 -1
- package/dist/server/routes/agent-completions.js +2 -1
- package/dist/server/routes/chat-completions.js +121 -42
- package/dist/server/routes/completions.js +48 -29
- package/dist/server/routes/embeddings.js +2 -1
- package/dist/server/routes/models.js +2 -1
- package/dist/server/routes/router.js +3 -2
- package/dist/server/services/file-processor.js +426 -0
- package/dist/server/usage.js +37 -3
- package/dist/server/utils/direct-llm-context.js +163 -26
- package/dist/server/utils/openai-format.js +21 -2
- package/dist/server/utils/rate-limiter.js +1 -1
- package/dist/server/utils/request-cache.js +61 -0
- package/dist/server/utils/resolve-service.js +2 -1
- package/dist/server/utils/user-permissions.js +25 -39
- package/dist/server/validation.js +7 -0
- package/dist/swagger.js +48 -10
- package/package.json +1 -1
- package/src/client/__tests__/settings-registration.test.tsx +6 -29
- package/src/client/plugin.tsx +5 -16
- package/src/client-v2/__tests__/settings-registration.test.tsx +6 -32
- package/src/client-v2/locale.ts +3 -1
- package/src/client-v2/pages/GeneralPage.tsx +0 -5
- package/src/client-v2/pages/ModelMetadataPage.tsx +20 -1
- package/src/client-v2/pages/UsageGroupsPage.tsx +548 -0
- package/src/client-v2/pages/UsagePage.tsx +9 -0
- package/src/client-v2/plugin.tsx +4 -13
- package/src/constants.ts +0 -7
- package/src/locale/en-US.json +27 -8
- package/src/locale/vi-VN.json +27 -8
- package/src/locale/zh-CN.json +27 -8
- package/src/server/__tests__/billing-quota.test.ts +28 -9
- package/src/server/__tests__/direct-llm-context.test.ts +209 -10
- package/src/server/__tests__/file-processor.test.ts +225 -0
- package/src/server/__tests__/models.test.ts +1 -1
- package/src/server/__tests__/openai-format.test.ts +12 -2
- package/src/server/__tests__/permission-sync.test.ts +34 -35
- package/src/server/__tests__/request-body.test.ts +45 -2
- package/src/server/__tests__/usage-groups.test.ts +160 -0
- package/src/server/__tests__/usage-monitor.test.ts +2 -0
- package/src/server/__tests__/usage-route.test.ts +382 -5
- package/src/server/__tests__/usage.test.ts +57 -0
- package/src/server/__tests__/user-permissions.test.ts +214 -133
- package/src/server/__tests__/validation.test.ts +11 -0
- package/src/server/billing.ts +36 -39
- package/src/server/collections/ai-api-config.ts +9 -7
- package/src/server/collections/ai-api-group-members.ts +41 -0
- package/src/server/collections/ai-api-group-quota-buckets.ts +42 -0
- package/src/server/collections/ai-api-model-metadata.ts +7 -0
- package/src/server/collections/ai-api-role-permissions.ts +41 -41
- package/src/server/collections/ai-api-usage-groups.ts +53 -0
- package/src/server/collections/ai-api-usage-records.ts +2 -0
- package/src/server/index.ts +10 -10
- package/src/server/middleware/rate-limit.ts +68 -70
- package/src/server/migrations/20260813000000-add-prompt-cache-tokens.ts +46 -0
- package/src/server/migrations/20260815000000-add-usage-groups.ts +147 -0
- package/src/server/migrations/20260816000000-migrate-user-permissions-to-groups.ts +190 -0
- package/src/server/migrations/20260816100000-add-model-metadata-system-prompt.ts +46 -0
- package/src/server/plugin.ts +121 -30
- package/src/server/quota-groups.ts +117 -0
- package/src/server/resource/ai-api-config.ts +5 -3
- package/src/server/resource/ai-api-usage-groups.ts +171 -0
- package/src/server/resource/ai-api-usage-monitor.ts +3 -0
- package/src/server/routes/agent-completions.ts +2 -1
- package/src/server/routes/chat-completions.ts +173 -47
- package/src/server/routes/completions.ts +50 -27
- package/src/server/routes/embeddings.ts +2 -1
- package/src/server/routes/models.ts +4 -3
- package/src/server/routes/router.ts +4 -3
- package/src/server/services/__tests__/file-processor.test.ts +184 -0
- package/src/server/services/file-processor.ts +513 -0
- package/src/server/usage.ts +51 -1
- package/src/server/utils/direct-llm-context.ts +218 -31
- package/src/server/utils/openai-format.ts +25 -2
- package/src/server/utils/rate-limiter.ts +83 -83
- package/src/server/utils/request-cache.ts +59 -0
- package/src/server/utils/resolve-service.ts +83 -82
- package/src/server/utils/user-permissions.ts +49 -69
- package/src/server/validation.ts +7 -0
- package/src/swagger.ts +52 -11
- package/dist/client/123.e6fe04c856ce6417.js +0 -10
- package/dist/client/302.fc3a3491b4ec2dfd.js +0 -10
- package/dist/client/562.17a0a299d2e5152c.js +0 -10
- package/dist/client/757.a01403fb7a1bea01.js +0 -10
- package/dist/client/902.e74518750f1e4201.js +0 -10
- package/dist/client-v2/123.05f1f649923f93eb.js +0 -10
- package/dist/client-v2/302.d27fe4ea9b0b3bf5.js +0 -10
- package/dist/client-v2/562.fb2948ee6402de95.js +0 -10
- package/dist/client-v2/757.a117ce1cf7119cea.js +0 -10
- package/dist/client-v2/902.c7c00a565085438a.js +0 -10
- package/dist/server/resource/ai-api-user-permissions.js +0 -75
- package/src/client-v2/pages/UserPermissionsPage.tsx +0 -322
- package/src/client-v2/pages/UserQuotasPage.tsx +0 -276
- package/src/server/__tests__/user-permissions-resource.test.ts +0 -66
- package/src/server/resource/ai-api-user-permissions.ts +0 -76
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { Application } from '@nocobase/client';
|
|
11
11
|
import { describe, expect, it } from 'vitest';
|
|
12
|
-
import { AI_API_ACL_SNIPPET
|
|
12
|
+
import { AI_API_ACL_SNIPPET } from '../../constants';
|
|
13
13
|
import { PluginAiApiClient } from '../plugin';
|
|
14
14
|
|
|
15
15
|
describe('AI API v1 settings registration', () => {
|
|
@@ -29,41 +29,18 @@ describe('AI API v1 settings registration', () => {
|
|
|
29
29
|
'ai-api.config',
|
|
30
30
|
'ai-api.model-pricing',
|
|
31
31
|
'ai-api.model-metadata',
|
|
32
|
-
'ai-api.
|
|
33
|
-
'ai-api.user-quotas',
|
|
32
|
+
'ai-api.usage-groups',
|
|
34
33
|
'ai-api.usage',
|
|
35
34
|
]);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
40
|
-
expect(app.pluginSettingsManager.getList().map((item) => item.name)).not.toContain('ai-api-user-permissions');
|
|
35
|
+
for (const child of menu?.children ?? []) {
|
|
36
|
+
expect(app.pluginSettingsManager.getAclSnippet(child.name), child.name).toBe(AI_API_ACL_SNIPPET);
|
|
37
|
+
}
|
|
41
38
|
});
|
|
42
39
|
|
|
43
|
-
it('
|
|
40
|
+
it('hides the whole settings surface when the snippet is denied', async () => {
|
|
44
41
|
const app = await loadPlugin();
|
|
45
42
|
app.pluginSettingsManager.setAclSnippets([`!${AI_API_ACL_SNIPPET}`]);
|
|
46
43
|
|
|
47
44
|
expect(app.pluginSettingsManager.getList()).toEqual([]);
|
|
48
45
|
});
|
|
49
|
-
|
|
50
|
-
it('hides only the user permissions tab when its own snippet is denied', async () => {
|
|
51
|
-
const app = await loadPlugin();
|
|
52
|
-
app.pluginSettingsManager.setAclSnippets([`!${AI_API_USER_PERMISSIONS_SNIPPET}`]);
|
|
53
|
-
|
|
54
|
-
expect(app.pluginSettingsManager.get('ai-api')?.children?.map((item) => item.name)).toEqual([
|
|
55
|
-
'ai-api.config',
|
|
56
|
-
'ai-api.model-pricing',
|
|
57
|
-
'ai-api.model-metadata',
|
|
58
|
-
'ai-api.user-quotas',
|
|
59
|
-
'ai-api.usage',
|
|
60
|
-
]);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('hides both settings surfaces when both snippets are denied', async () => {
|
|
64
|
-
const app = await loadPlugin();
|
|
65
|
-
app.pluginSettingsManager.setAclSnippets([`!${AI_API_ACL_SNIPPET}`, `!${AI_API_USER_PERMISSIONS_SNIPPET}`]);
|
|
66
|
-
|
|
67
|
-
expect(app.pluginSettingsManager.getList()).toEqual([]);
|
|
68
|
-
});
|
|
69
46
|
});
|
package/src/client/plugin.tsx
CHANGED
|
@@ -9,14 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
import { Plugin, lazy } from '@nocobase/client';
|
|
11
11
|
import PluginACLClient from '@nocobase/plugin-acl/client';
|
|
12
|
-
import { AI_API_ACL_SNIPPET
|
|
12
|
+
import { AI_API_ACL_SNIPPET } from '../constants';
|
|
13
13
|
import React from 'react';
|
|
14
14
|
|
|
15
15
|
const AiApiConfigPage = React.lazy(() => import('../client-v2/pages/GeneralPage'));
|
|
16
16
|
const AiApiModelPricingPage = React.lazy(() => import('../client-v2/pages/ModelPricingPage'));
|
|
17
17
|
const AiApiModelMetadataPage = React.lazy(() => import('../client-v2/pages/ModelMetadataPage'));
|
|
18
|
-
const
|
|
19
|
-
const AiApiUserQuotasPage = React.lazy(() => import('../client-v2/pages/UserQuotasPage'));
|
|
18
|
+
const AiApiUsageGroupsPage = React.lazy(() => import('../client-v2/pages/UsageGroupsPage'));
|
|
20
19
|
const AiApiUsagePage = React.lazy(() => import('../client-v2/pages/UsagePage'));
|
|
21
20
|
const { AiApiRolePermissions } = lazy(() => import('./components/AiApiRolePermissions'), 'AiApiRolePermissions');
|
|
22
21
|
|
|
@@ -49,19 +48,9 @@ export class PluginAiApiClient extends Plugin {
|
|
|
49
48
|
sort: 3,
|
|
50
49
|
});
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.app.pluginSettingsManager.add('ai-api.user-permissions', {
|
|
56
|
-
title: this.t('User LLM permissions'),
|
|
57
|
-
Component: AiApiUserPermissionsPage,
|
|
58
|
-
aclSnippet: AI_API_USER_PERMISSIONS_SNIPPET,
|
|
59
|
-
sort: 4,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
this.app.pluginSettingsManager.add('ai-api.user-quotas', {
|
|
63
|
-
title: this.t('User quotas'),
|
|
64
|
-
Component: AiApiUserQuotasPage,
|
|
51
|
+
this.app.pluginSettingsManager.add('ai-api.usage-groups', {
|
|
52
|
+
title: this.t('Usage groups'),
|
|
53
|
+
Component: AiApiUsageGroupsPage,
|
|
65
54
|
aclSnippet: AI_API_ACL_SNIPPET,
|
|
66
55
|
sort: 5,
|
|
67
56
|
});
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { createMockClient } from '@nocobase/client-v2';
|
|
11
|
-
import { AI_API_ACL_SNIPPET
|
|
11
|
+
import { AI_API_ACL_SNIPPET } from '../../constants';
|
|
12
12
|
import { PluginAiApiClient } from '../plugin';
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -18,10 +18,8 @@ import { PluginAiApiClient } from '../plugin';
|
|
|
18
18
|
* omitting `aclSnippet` makes addPageTabItem default to `pm.ai-api.<key>`, a snippet the
|
|
19
19
|
* server never registers, and omitting `sort` silently reorders the tabs alphabetically.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* configuration. Each snippet is evaluated independently, so denying one leaves the other's
|
|
24
|
-
* tabs — and therefore the menu — reachable.
|
|
21
|
+
* Every tab sits under AI_API_ACL_SNIPPET — model access is managed on the usage groups
|
|
22
|
+
* page, so there is no longer a separately-gated permissions tab.
|
|
25
23
|
*/
|
|
26
24
|
describe('AI API v2 settings registration', () => {
|
|
27
25
|
async function loadPlugin() {
|
|
@@ -36,41 +34,18 @@ describe('AI API v2 settings registration', () => {
|
|
|
36
34
|
|
|
37
35
|
expect(menu?.aclSnippet).toBe(AI_API_ACL_SNIPPET);
|
|
38
36
|
for (const child of menu?.children ?? []) {
|
|
39
|
-
expect(app.pluginSettingsManager.getAclSnippet(child.name), child.name).toBe(
|
|
40
|
-
child.name === 'ai-api.user-permissions' ? AI_API_USER_PERMISSIONS_SNIPPET : AI_API_ACL_SNIPPET,
|
|
41
|
-
);
|
|
37
|
+
expect(app.pluginSettingsManager.getAclSnippet(child.name), child.name).toBe(AI_API_ACL_SNIPPET);
|
|
42
38
|
}
|
|
43
39
|
});
|
|
44
40
|
|
|
45
|
-
it('
|
|
41
|
+
it('hides the menu and every tab when the snippet is denied', async () => {
|
|
46
42
|
const app = await loadPlugin();
|
|
47
43
|
app.pluginSettingsManager.setAclSnippets([`!${AI_API_ACL_SNIPPET}`]);
|
|
48
44
|
|
|
49
|
-
// User permissions carries its own snippet, so it survives on its own merit and keeps the
|
|
50
|
-
// menu reachable. That separation is the point: a role can be allowed to hand out model
|
|
51
|
-
// access without also being allowed to reconfigure the gateway.
|
|
52
|
-
expect(app.pluginSettingsManager.get('ai-api')?.children?.map((item) => item.name)).toEqual([
|
|
53
|
-
'ai-api.user-permissions',
|
|
54
|
-
]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('hides the menu and every tab when both snippets are denied', async () => {
|
|
58
|
-
const app = await loadPlugin();
|
|
59
|
-
app.pluginSettingsManager.setAclSnippets([`!${AI_API_ACL_SNIPPET}`, `!${AI_API_USER_PERMISSIONS_SNIPPET}`]);
|
|
60
|
-
|
|
61
45
|
expect(app.pluginSettingsManager.get('ai-api')).toBeNull();
|
|
62
46
|
expect(app.pluginSettingsManager.getList().map((item) => item.name)).not.toContain('ai-api');
|
|
63
47
|
});
|
|
64
48
|
|
|
65
|
-
it('hides only the user permissions tab when its own snippet is denied', async () => {
|
|
66
|
-
const app = await loadPlugin();
|
|
67
|
-
app.pluginSettingsManager.setAclSnippets([`!${AI_API_USER_PERMISSIONS_SNIPPET}`]);
|
|
68
|
-
|
|
69
|
-
const children = app.pluginSettingsManager.get('ai-api')?.children?.map((item) => item.name) ?? [];
|
|
70
|
-
expect(children).not.toContain('ai-api.user-permissions');
|
|
71
|
-
expect(children).toContain('ai-api.user-quotas');
|
|
72
|
-
});
|
|
73
|
-
|
|
74
49
|
it('keeps registration order instead of sorting tabs by name', async () => {
|
|
75
50
|
const app = await loadPlugin();
|
|
76
51
|
app.pluginSettingsManager.setAclSnippets([]);
|
|
@@ -79,8 +54,7 @@ describe('AI API v2 settings registration', () => {
|
|
|
79
54
|
'ai-api.index',
|
|
80
55
|
'ai-api.model-pricing',
|
|
81
56
|
'ai-api.model-metadata',
|
|
82
|
-
'ai-api.
|
|
83
|
-
'ai-api.user-quotas',
|
|
57
|
+
'ai-api.usage-groups',
|
|
84
58
|
'ai-api.usage',
|
|
85
59
|
]);
|
|
86
60
|
});
|
package/src/client-v2/locale.ts
CHANGED
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { useCallback } from 'react';
|
|
10
11
|
import { tExpr as _tExpr, useFlowEngine } from '@nocobase/flow-engine';
|
|
11
12
|
// @ts-ignore
|
|
12
13
|
import pkg from './../../package.json';
|
|
13
14
|
|
|
14
15
|
export function useT() {
|
|
15
16
|
const engine = useFlowEngine();
|
|
16
|
-
|
|
17
|
+
// Stable identity so callers can safely list `t` in hook dependency arrays.
|
|
18
|
+
return useCallback((str: string) => engine.context.t(str, { ns: [pkg.name, 'client'] }), [engine]);
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export function tExpr(key: string) {
|
|
@@ -9,7 +9,6 @@ interface GeneralSettings {
|
|
|
9
9
|
defaultAiEmployee?: string;
|
|
10
10
|
defaultLlmService?: string;
|
|
11
11
|
enabledLlmServices: string[];
|
|
12
|
-
rateLimitPerMinute: number;
|
|
13
12
|
maxRequestBodyMb: number;
|
|
14
13
|
quotaEnabled: boolean;
|
|
15
14
|
defaultReservationOutputTokens: number;
|
|
@@ -28,7 +27,6 @@ interface AiEmployee {
|
|
|
28
27
|
const defaults: GeneralSettings = {
|
|
29
28
|
mode: 'llm',
|
|
30
29
|
enabledLlmServices: [],
|
|
31
|
-
rateLimitPerMinute: 60,
|
|
32
30
|
maxRequestBodyMb: 10,
|
|
33
31
|
quotaEnabled: false,
|
|
34
32
|
defaultReservationOutputTokens: 4096,
|
|
@@ -123,9 +121,6 @@ export default function GeneralPage() {
|
|
|
123
121
|
/>
|
|
124
122
|
</Form.Item>
|
|
125
123
|
) : null}
|
|
126
|
-
<Form.Item name="rateLimitPerMinute" label={t('Rate Limit')} rules={[{ required: true }]}>
|
|
127
|
-
<InputNumber min={1} style={{ width: '100%' }} />
|
|
128
|
-
</Form.Item>
|
|
129
124
|
<Form.Item
|
|
130
125
|
name="maxRequestBodyMb"
|
|
131
126
|
label={t('Max request body size (MB)')}
|
|
@@ -28,6 +28,7 @@ interface ModelMetadata {
|
|
|
28
28
|
ownedByOverride?: string | null;
|
|
29
29
|
displayName?: string | null;
|
|
30
30
|
description?: string | null;
|
|
31
|
+
systemPrompt?: string | null;
|
|
31
32
|
enabled: boolean;
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -136,6 +137,7 @@ export default function ModelMetadataPage() {
|
|
|
136
137
|
ownedByOverride: values.ownedByOverride?.trim() || null,
|
|
137
138
|
displayName: values.displayName?.trim() || null,
|
|
138
139
|
description: values.description?.trim() || null,
|
|
140
|
+
systemPrompt: values.systemPrompt?.trim() || null,
|
|
139
141
|
};
|
|
140
142
|
setSaving(true);
|
|
141
143
|
try {
|
|
@@ -171,6 +173,14 @@ export default function ModelMetadataPage() {
|
|
|
171
173
|
{ title: t('Max completion tokens'), dataIndex: 'maxCompletionTokens', key: 'maxCompletionTokens', width: 170 },
|
|
172
174
|
{ title: t('Owned by'), dataIndex: 'ownedByOverride', key: 'ownedByOverride', width: 140 },
|
|
173
175
|
{ title: t('Display name'), dataIndex: 'displayName', key: 'displayName', width: 160 },
|
|
176
|
+
{
|
|
177
|
+
title: t('Initial system prompt'),
|
|
178
|
+
dataIndex: 'systemPrompt',
|
|
179
|
+
key: 'systemPrompt',
|
|
180
|
+
width: 220,
|
|
181
|
+
ellipsis: true,
|
|
182
|
+
render: (value?: string | null) => value || '-',
|
|
183
|
+
},
|
|
174
184
|
{
|
|
175
185
|
title: t('Status'),
|
|
176
186
|
dataIndex: 'enabled',
|
|
@@ -209,7 +219,7 @@ export default function ModelMetadataPage() {
|
|
|
209
219
|
</Button>
|
|
210
220
|
}
|
|
211
221
|
>
|
|
212
|
-
<Table rowKey="id" columns={columns} dataSource={rows} loading={loading} scroll={{ x:
|
|
222
|
+
<Table rowKey="id" columns={columns} dataSource={rows} loading={loading} scroll={{ x: 1450 }} />
|
|
213
223
|
<Modal
|
|
214
224
|
title={editing ? t('Edit override') : t('Add override')}
|
|
215
225
|
open={open}
|
|
@@ -270,6 +280,15 @@ export default function ModelMetadataPage() {
|
|
|
270
280
|
<Form.Item name="description" label={t('Description')}>
|
|
271
281
|
<Input.TextArea rows={3} />
|
|
272
282
|
</Form.Item>
|
|
283
|
+
<Form.Item
|
|
284
|
+
name="systemPrompt"
|
|
285
|
+
label={t('Initial system prompt')}
|
|
286
|
+
tooltip={t(
|
|
287
|
+
'Prepended as the first system message, before any system prompt sent by the client. If the client sends no system prompt, this becomes the system prompt sent to the provider.',
|
|
288
|
+
)}
|
|
289
|
+
>
|
|
290
|
+
<Input.TextArea rows={4} placeholder={t('Leave empty to not override')} />
|
|
291
|
+
</Form.Item>
|
|
273
292
|
<Form.Item name="enabled" label={t('Enabled')} valuePropName="checked">
|
|
274
293
|
<Switch />
|
|
275
294
|
</Form.Item>
|