plugin-ai-api 1.0.1 → 1.0.3
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/23.e96ecf13e6072dce.js +10 -0
- package/dist/client/{824c2f7487ee05fd.js → 503.29bcdb426b01e715.js} +10 -1
- package/dist/client/index.js +10 -1
- package/dist/externalVersion.js +19 -8
- package/dist/index.js +9 -0
- package/dist/locale/en-US.json +10 -10
- package/dist/locale/zh-CN.json +10 -10
- package/dist/server/collections/ai-api-config.js +9 -0
- package/dist/server/collections/ai-api-role-permissions.js +62 -0
- package/dist/server/index.js +9 -0
- package/dist/server/middleware/rate-limit.js +9 -0
- package/dist/server/middleware/role-permission.js +66 -0
- package/dist/server/plugin.js +15 -1
- package/dist/server/resource/ai-api-config.js +10 -1
- package/dist/server/routes/agent-completions.js +39 -5
- package/dist/server/routes/auth.js +11 -12
- package/dist/server/routes/chat-completions.js +27 -4
- package/dist/server/routes/completions.js +16 -4
- package/dist/server/routes/embeddings.js +10 -3
- package/dist/server/routes/models.js +11 -8
- package/dist/server/routes/router.js +37 -5
- package/dist/server/utils/openai-format.js +9 -0
- package/dist/server/utils/rate-limiter.js +9 -0
- package/dist/server/utils/resolve-service.js +9 -0
- package/dist/swagger.js +345 -0
- package/package.json +14 -13
- package/dist/client/AiApiConfigPage.d.ts +0 -3
- package/dist/client/index.d.ts +0 -1
- package/dist/client/locale.d.ts +0 -10
- package/dist/client/models/index.d.ts +0 -10
- package/dist/client/plugin.d.ts +0 -6
- package/dist/index.d.ts +0 -2
- package/dist/server/collections/ai-api-config.d.ts +0 -2
- package/dist/server/index.d.ts +0 -1
- package/dist/server/middleware/rate-limit.d.ts +0 -18
- package/dist/server/plugin.d.ts +0 -18
- package/dist/server/resource/ai-api-config.d.ts +0 -10
- package/dist/server/routes/agent-completions.d.ts +0 -34
- package/dist/server/routes/auth.d.ts +0 -8
- package/dist/server/routes/chat-completions.d.ts +0 -9
- package/dist/server/routes/completions.d.ts +0 -10
- package/dist/server/routes/embeddings.d.ts +0 -22
- package/dist/server/routes/models.d.ts +0 -21
- package/dist/server/routes/router.d.ts +0 -25
- package/dist/server/utils/openai-format.d.ts +0 -99
- package/dist/server/utils/rate-limiter.d.ts +0 -35
- package/dist/server/utils/resolve-service.d.ts +0 -19
- package/nocobase-plugin-ai-api-2.0.20.tgz +0 -0
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -67,16 +76,19 @@ async function handleCompletions(ctx, plugin) {
|
|
|
67
76
|
}
|
|
68
77
|
if (service.enabled === false) {
|
|
69
78
|
ctx.status = 404;
|
|
70
|
-
ctx.body = (0, import_openai_format.toOpenAIError)(
|
|
79
|
+
ctx.body = (0, import_openai_format.toOpenAIError)(
|
|
80
|
+
404,
|
|
81
|
+
`LLM service '${service.title || service.name}' is disabled`,
|
|
82
|
+
"invalid_request_error",
|
|
83
|
+
"model_not_found"
|
|
84
|
+
);
|
|
71
85
|
return;
|
|
72
86
|
}
|
|
73
87
|
const config = await ctx.db.getRepository("aiApiConfig").findOne();
|
|
74
88
|
if ((_a = config == null ? void 0 : config.enabledLlmServices) == null ? void 0 : _a.length) {
|
|
75
89
|
const serviceName = service.name;
|
|
76
90
|
const serviceTitle = service.title;
|
|
77
|
-
const isAllowed = config.enabledLlmServices.some(
|
|
78
|
-
(s) => s === serviceName || s === serviceTitle
|
|
79
|
-
);
|
|
91
|
+
const isAllowed = config.enabledLlmServices.some((s) => s === serviceName || s === serviceTitle);
|
|
80
92
|
if (!isAllowed) {
|
|
81
93
|
ctx.status = 403;
|
|
82
94
|
ctx.body = (0, import_openai_format.toOpenAIError)(
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -95,9 +104,7 @@ async function handleEmbeddings(ctx, plugin) {
|
|
|
95
104
|
try {
|
|
96
105
|
const config = await ctx.db.getRepository("aiApiConfig").findOne();
|
|
97
106
|
if ((_a = config == null ? void 0 : config.enabledLlmServices) == null ? void 0 : _a.length) {
|
|
98
|
-
const allowed = config.enabledLlmServices.some(
|
|
99
|
-
(s) => s === service.name || s === service.title
|
|
100
|
-
);
|
|
107
|
+
const allowed = config.enabledLlmServices.some((s) => s === service.name || s === service.title);
|
|
101
108
|
if (!allowed) {
|
|
102
109
|
ctx.status = 403;
|
|
103
110
|
ctx.body = (0, import_openai_format.toOpenAIError)(
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -34,10 +43,7 @@ async function handleListModels(ctx, plugin) {
|
|
|
34
43
|
const config = await getPluginConfig(ctx);
|
|
35
44
|
const filter = {};
|
|
36
45
|
if ((_a = config == null ? void 0 : config.enabledLlmServices) == null ? void 0 : _a.length) {
|
|
37
|
-
filter.$or = [
|
|
38
|
-
{ name: { $in: config.enabledLlmServices } },
|
|
39
|
-
{ title: { $in: config.enabledLlmServices } }
|
|
40
|
-
];
|
|
46
|
+
filter.$or = [{ name: { $in: config.enabledLlmServices } }, { title: { $in: config.enabledLlmServices } }];
|
|
41
47
|
}
|
|
42
48
|
const services = await ctx.db.getRepository("llmServices").find({
|
|
43
49
|
filter,
|
|
@@ -77,10 +83,7 @@ async function handleGetModel(ctx, modelId, plugin) {
|
|
|
77
83
|
const config = await getPluginConfig(ctx);
|
|
78
84
|
const filter = {};
|
|
79
85
|
if ((_a = config == null ? void 0 : config.enabledLlmServices) == null ? void 0 : _a.length) {
|
|
80
|
-
filter.$or = [
|
|
81
|
-
{ name: { $in: config.enabledLlmServices } },
|
|
82
|
-
{ title: { $in: config.enabledLlmServices } }
|
|
83
|
-
];
|
|
86
|
+
filter.$or = [{ name: { $in: config.enabledLlmServices } }, { title: { $in: config.enabledLlmServices } }];
|
|
84
87
|
}
|
|
85
88
|
const services = await ctx.db.getRepository("llmServices").find({
|
|
86
89
|
filter,
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -39,6 +48,7 @@ var import_agent_completions = require("./agent-completions");
|
|
|
39
48
|
var import_embeddings = require("./embeddings");
|
|
40
49
|
var import_openai_format = require("../utils/openai-format");
|
|
41
50
|
var import_rate_limit = require("../middleware/rate-limit");
|
|
51
|
+
var import_role_permission = require("../middleware/role-permission");
|
|
42
52
|
const API_PREFIX = "/api/ai-llm/v1";
|
|
43
53
|
function createAiLlmRouter(plugin) {
|
|
44
54
|
const checkRateLimit = (0, import_rate_limit.createRateLimitMiddleware)(plugin.rateLimiter);
|
|
@@ -78,6 +88,11 @@ function createAiLlmRouter(plugin) {
|
|
|
78
88
|
logRequest(ctx, requestId, "-", "auth_failed", 0);
|
|
79
89
|
return;
|
|
80
90
|
}
|
|
91
|
+
const permitted = await (0, import_role_permission.checkRolePermission)(ctx);
|
|
92
|
+
if (!permitted) {
|
|
93
|
+
logRequest(ctx, requestId, "-", "forbidden", 0);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
81
96
|
const allowed = await checkRateLimit(ctx);
|
|
82
97
|
if (!allowed) {
|
|
83
98
|
logRequest(ctx, requestId, "-", "rate_limited", 0);
|
|
@@ -98,7 +113,17 @@ function createAiLlmRouter(plugin) {
|
|
|
98
113
|
return;
|
|
99
114
|
}
|
|
100
115
|
if (method === "POST" && subPath === "/completions") {
|
|
101
|
-
await (
|
|
116
|
+
const completionsMode = await resolveMode(ctx);
|
|
117
|
+
if (completionsMode === "agent") {
|
|
118
|
+
const reqBody = ctx.request.body;
|
|
119
|
+
if ((reqBody == null ? void 0 : reqBody.prompt) !== void 0) {
|
|
120
|
+
const prompt = typeof reqBody.prompt === "string" ? reqBody.prompt : Array.isArray(reqBody.prompt) ? reqBody.prompt.join("\n") : String(reqBody.prompt);
|
|
121
|
+
ctx.request.body = { ...reqBody, messages: [{ role: "user", content: prompt }] };
|
|
122
|
+
}
|
|
123
|
+
await (0, import_agent_completions.handleAgentCompletions)(ctx, plugin);
|
|
124
|
+
} else {
|
|
125
|
+
await (0, import_completions.handleCompletions)(ctx, plugin);
|
|
126
|
+
}
|
|
102
127
|
logRequest(ctx, requestId, model, "ok", Date.now() - t0);
|
|
103
128
|
return;
|
|
104
129
|
}
|
|
@@ -163,18 +188,25 @@ function getRawBody(ctx) {
|
|
|
163
188
|
});
|
|
164
189
|
}
|
|
165
190
|
async function resolveMode(ctx) {
|
|
166
|
-
var _a;
|
|
191
|
+
var _a, _b, _c, _d, _e;
|
|
167
192
|
const headerMode = (_a = ctx.get("X-AI-Mode")) == null ? void 0 : _a.toLowerCase();
|
|
168
193
|
if (headerMode === "agent" || headerMode === "llm") {
|
|
194
|
+
(_b = ctx.app.logger) == null ? void 0 : _b.info(`[ai-api] Mode resolved from header: ${headerMode}`);
|
|
169
195
|
return headerMode;
|
|
170
196
|
}
|
|
171
197
|
try {
|
|
172
198
|
const config = await ctx.db.getRepository("aiApiConfig").findOne();
|
|
173
|
-
if (
|
|
174
|
-
|
|
199
|
+
if (config) {
|
|
200
|
+
const dbMode = config.get("mode") || config.mode;
|
|
201
|
+
if (dbMode === "agent" || dbMode === "llm") {
|
|
202
|
+
(_c = ctx.app.logger) == null ? void 0 : _c.info(`[ai-api] Mode resolved from DB config: ${dbMode}`);
|
|
203
|
+
return dbMode;
|
|
204
|
+
}
|
|
175
205
|
}
|
|
176
|
-
} catch {
|
|
206
|
+
} catch (err) {
|
|
207
|
+
(_d = ctx.app.logger) == null ? void 0 : _d.error("[ai-api] Failed to get mode from config:", err);
|
|
177
208
|
}
|
|
209
|
+
(_e = ctx.app.logger) == null ? void 0 : _e.info(`[ai-api] Mode fallback to default: llm`);
|
|
178
210
|
return "llm";
|
|
179
211
|
}
|
|
180
212
|
function logRequest(ctx, requestId, model, status, durationMs) {
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __create = Object.create;
|
|
2
11
|
var __defProp = Object.defineProperty;
|
|
3
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -1,3 +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
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
2
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
package/dist/swagger.js
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
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
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var swagger_exports = {};
|
|
28
|
+
__export(swagger_exports, {
|
|
29
|
+
default: () => swagger_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(swagger_exports);
|
|
32
|
+
var swagger_default = {
|
|
33
|
+
info: {
|
|
34
|
+
title: "NocoBase API - AI API Plugin",
|
|
35
|
+
description: "OpenAI-compatible AI gateway and configuration API"
|
|
36
|
+
},
|
|
37
|
+
tags: [
|
|
38
|
+
{ name: "aiApiConfig", description: "AI API configuration management" },
|
|
39
|
+
{ name: "ai-llm", description: "OpenAI-compatible LLM gateway (prefix: /api/ai-llm/v1)" }
|
|
40
|
+
],
|
|
41
|
+
paths: {
|
|
42
|
+
"/aiApiConfig:get": {
|
|
43
|
+
get: {
|
|
44
|
+
tags: ["aiApiConfig"],
|
|
45
|
+
summary: "Get AI API configuration",
|
|
46
|
+
security: [{ BearerAuth: [] }],
|
|
47
|
+
responses: {
|
|
48
|
+
200: {
|
|
49
|
+
description: "Current AI API configuration",
|
|
50
|
+
content: {
|
|
51
|
+
"application/json": {
|
|
52
|
+
schema: { $ref: "#/components/schemas/AiApiConfig" }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"/aiApiConfig:save": {
|
|
60
|
+
post: {
|
|
61
|
+
tags: ["aiApiConfig"],
|
|
62
|
+
summary: "Save AI API configuration",
|
|
63
|
+
security: [{ BearerAuth: [] }],
|
|
64
|
+
requestBody: {
|
|
65
|
+
required: true,
|
|
66
|
+
content: {
|
|
67
|
+
"application/json": {
|
|
68
|
+
schema: { $ref: "#/components/schemas/AiApiConfig" }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
responses: {
|
|
73
|
+
200: { description: "Configuration saved successfully" }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"/ai-llm/v1/models": {
|
|
78
|
+
get: {
|
|
79
|
+
tags: ["ai-llm"],
|
|
80
|
+
summary: "List available models",
|
|
81
|
+
description: "Returns all LLM models available across registered services. Model IDs are formatted as `serviceName/modelId`.",
|
|
82
|
+
security: [{ BearerAuth: [] }],
|
|
83
|
+
responses: {
|
|
84
|
+
200: {
|
|
85
|
+
description: "List of available models",
|
|
86
|
+
content: {
|
|
87
|
+
"application/json": {
|
|
88
|
+
schema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
object: { type: "string", example: "list" },
|
|
92
|
+
data: {
|
|
93
|
+
type: "array",
|
|
94
|
+
items: { $ref: "#/components/schemas/ModelObject" }
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"/ai-llm/v1/models/{id}": {
|
|
105
|
+
get: {
|
|
106
|
+
tags: ["ai-llm"],
|
|
107
|
+
summary: "Get model details",
|
|
108
|
+
security: [{ BearerAuth: [] }],
|
|
109
|
+
parameters: [
|
|
110
|
+
{
|
|
111
|
+
name: "id",
|
|
112
|
+
in: "path",
|
|
113
|
+
required: true,
|
|
114
|
+
description: "Model ID in format `serviceName/modelId`",
|
|
115
|
+
schema: { type: "string", example: "openai/gpt-4o" }
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
responses: {
|
|
119
|
+
200: {
|
|
120
|
+
description: "Model details",
|
|
121
|
+
content: {
|
|
122
|
+
"application/json": {
|
|
123
|
+
schema: { $ref: "#/components/schemas/ModelObject" }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
404: { description: "Model not found" }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"/ai-llm/v1/chat/completions": {
|
|
132
|
+
post: {
|
|
133
|
+
tags: ["ai-llm"],
|
|
134
|
+
summary: "Chat completions",
|
|
135
|
+
description: "OpenAI-compatible chat completions endpoint. Supports streaming (`stream: true`). Use header `X-AI-Mode: llm` (default) or `X-AI-Mode: agent` to switch between direct LLM and agent mode.",
|
|
136
|
+
security: [{ BearerAuth: [] }],
|
|
137
|
+
parameters: [
|
|
138
|
+
{
|
|
139
|
+
name: "X-AI-Mode",
|
|
140
|
+
in: "header",
|
|
141
|
+
required: false,
|
|
142
|
+
description: "AI mode: `llm` (default) or `agent`",
|
|
143
|
+
schema: { type: "string", enum: ["llm", "agent"], default: "llm" }
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
requestBody: {
|
|
147
|
+
required: true,
|
|
148
|
+
content: {
|
|
149
|
+
"application/json": {
|
|
150
|
+
schema: { $ref: "#/components/schemas/ChatCompletionRequest" }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
responses: {
|
|
155
|
+
200: {
|
|
156
|
+
description: "Chat completion response (or SSE stream when `stream: true`)",
|
|
157
|
+
content: {
|
|
158
|
+
"application/json": {
|
|
159
|
+
schema: { $ref: "#/components/schemas/ChatCompletionResponse" }
|
|
160
|
+
},
|
|
161
|
+
"text/event-stream": {
|
|
162
|
+
schema: { type: "string", description: "SSE stream of completion chunks" }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
429: { description: "Rate limit exceeded" }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"/ai-llm/v1/completions": {
|
|
171
|
+
post: {
|
|
172
|
+
tags: ["ai-llm"],
|
|
173
|
+
summary: "Text completions (legacy)",
|
|
174
|
+
description: "Legacy text completions for LiteLLM compatibility.",
|
|
175
|
+
security: [{ BearerAuth: [] }],
|
|
176
|
+
requestBody: {
|
|
177
|
+
required: true,
|
|
178
|
+
content: {
|
|
179
|
+
"application/json": {
|
|
180
|
+
schema: {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: {
|
|
183
|
+
model: { type: "string" },
|
|
184
|
+
prompt: { type: "string" },
|
|
185
|
+
max_tokens: { type: "integer" },
|
|
186
|
+
stream: { type: "boolean", default: false }
|
|
187
|
+
},
|
|
188
|
+
required: ["model", "prompt"]
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
responses: {
|
|
194
|
+
200: { description: "Completion response" },
|
|
195
|
+
429: { description: "Rate limit exceeded" }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"/ai-llm/v1/embeddings": {
|
|
200
|
+
post: {
|
|
201
|
+
tags: ["ai-llm"],
|
|
202
|
+
summary: "Generate embeddings",
|
|
203
|
+
description: "OpenAI-compatible embeddings endpoint.",
|
|
204
|
+
security: [{ BearerAuth: [] }],
|
|
205
|
+
requestBody: {
|
|
206
|
+
required: true,
|
|
207
|
+
content: {
|
|
208
|
+
"application/json": {
|
|
209
|
+
schema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
model: { type: "string", example: "openai/text-embedding-ada-002" },
|
|
213
|
+
input: {
|
|
214
|
+
oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }]
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
required: ["model", "input"]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
responses: {
|
|
223
|
+
200: {
|
|
224
|
+
description: "Embedding vectors",
|
|
225
|
+
content: {
|
|
226
|
+
"application/json": {
|
|
227
|
+
schema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: {
|
|
230
|
+
object: { type: "string", example: "list" },
|
|
231
|
+
data: {
|
|
232
|
+
type: "array",
|
|
233
|
+
items: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {
|
|
236
|
+
object: { type: "string", example: "embedding" },
|
|
237
|
+
embedding: { type: "array", items: { type: "number" } },
|
|
238
|
+
index: { type: "integer" }
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
model: { type: "string" },
|
|
243
|
+
usage: {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
prompt_tokens: { type: "integer" },
|
|
247
|
+
total_tokens: { type: "integer" }
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
429: { description: "Rate limit exceeded" }
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
components: {
|
|
261
|
+
schemas: {
|
|
262
|
+
AiApiConfig: {
|
|
263
|
+
type: "object",
|
|
264
|
+
properties: {
|
|
265
|
+
mode: {
|
|
266
|
+
type: "string",
|
|
267
|
+
enum: ["llm", "agent"],
|
|
268
|
+
description: "Default AI mode"
|
|
269
|
+
},
|
|
270
|
+
defaultAiEmployee: { type: "string", description: "Default AI employee name" },
|
|
271
|
+
defaultLlmService: { type: "string", description: "Default LLM service name" },
|
|
272
|
+
enabledLlmServices: {
|
|
273
|
+
type: "array",
|
|
274
|
+
items: { type: "string" },
|
|
275
|
+
description: "List of enabled LLM service names"
|
|
276
|
+
},
|
|
277
|
+
rateLimitPerMinute: {
|
|
278
|
+
type: "integer",
|
|
279
|
+
description: "Max requests per minute per user (0 = unlimited)"
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
ModelObject: {
|
|
284
|
+
type: "object",
|
|
285
|
+
properties: {
|
|
286
|
+
id: { type: "string", example: "openai/gpt-4o", description: "Model ID (serviceName/modelId)" },
|
|
287
|
+
object: { type: "string", example: "model" },
|
|
288
|
+
created: { type: "integer" },
|
|
289
|
+
owned_by: { type: "string" }
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
ChatMessage: {
|
|
293
|
+
type: "object",
|
|
294
|
+
properties: {
|
|
295
|
+
role: { type: "string", enum: ["system", "user", "assistant", "tool"] },
|
|
296
|
+
content: { type: "string" },
|
|
297
|
+
name: { type: "string" }
|
|
298
|
+
},
|
|
299
|
+
required: ["role", "content"]
|
|
300
|
+
},
|
|
301
|
+
ChatCompletionRequest: {
|
|
302
|
+
type: "object",
|
|
303
|
+
properties: {
|
|
304
|
+
model: { type: "string", example: "openai/gpt-4o" },
|
|
305
|
+
messages: { type: "array", items: { $ref: "#/components/schemas/ChatMessage" } },
|
|
306
|
+
stream: { type: "boolean", default: false },
|
|
307
|
+
temperature: { type: "number", minimum: 0, maximum: 2 },
|
|
308
|
+
max_tokens: { type: "integer" },
|
|
309
|
+
top_p: { type: "number" },
|
|
310
|
+
frequency_penalty: { type: "number" },
|
|
311
|
+
presence_penalty: { type: "number" }
|
|
312
|
+
},
|
|
313
|
+
required: ["model", "messages"]
|
|
314
|
+
},
|
|
315
|
+
ChatCompletionResponse: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
id: { type: "string" },
|
|
319
|
+
object: { type: "string", example: "chat.completion" },
|
|
320
|
+
created: { type: "integer" },
|
|
321
|
+
model: { type: "string" },
|
|
322
|
+
choices: {
|
|
323
|
+
type: "array",
|
|
324
|
+
items: {
|
|
325
|
+
type: "object",
|
|
326
|
+
properties: {
|
|
327
|
+
index: { type: "integer" },
|
|
328
|
+
message: { $ref: "#/components/schemas/ChatMessage" },
|
|
329
|
+
finish_reason: { type: "string", enum: ["stop", "length", "tool_calls"] }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
usage: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
prompt_tokens: { type: "integer" },
|
|
337
|
+
completion_tokens: { type: "integer" },
|
|
338
|
+
total_tokens: { type: "integer" }
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
};
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "plugin-ai-api",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"main": "dist/server/index.js",
|
|
5
|
-
"dependencies": {},
|
|
6
|
-
"peerDependencies": {
|
|
7
|
-
"@nocobase/client": "2.x",
|
|
8
|
-
"@nocobase/server": "2.x",
|
|
9
|
-
"@nocobase/database": "2.x",
|
|
10
|
-
"@nocobase/plugin-
|
|
11
|
-
"@nocobase/plugin-
|
|
12
|
-
|
|
13
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "plugin-ai-api",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"main": "dist/server/index.js",
|
|
5
|
+
"dependencies": {},
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"@nocobase/client": "2.x",
|
|
8
|
+
"@nocobase/server": "2.x",
|
|
9
|
+
"@nocobase/database": "2.x",
|
|
10
|
+
"@nocobase/plugin-acl": "2.x",
|
|
11
|
+
"@nocobase/plugin-ai": "2.x",
|
|
12
|
+
"@nocobase/plugin-api-keys": "2.x"
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/client/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './plugin';
|
package/dist/client/locale.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
export declare function useT(): (str: string) => any;
|
|
10
|
-
export declare function tExpr(key: string): any;
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
declare const _default: Record<string, ModelConstructor>;
|
|
10
|
-
export default _default;
|
package/dist/client/plugin.d.ts
DELETED
package/dist/index.d.ts
DELETED
package/dist/server/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default } from './plugin';
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { RateLimiter } from '../utils/rate-limiter';
|
|
2
|
-
/**
|
|
3
|
-
* Creates a rate-limiting check function for use in the AI API router.
|
|
4
|
-
*
|
|
5
|
-
* Must be called AFTER authenticateBearer() so ctx.state.currentUser is set.
|
|
6
|
-
* Reads rateLimitPerMinute from aiApiConfig on each request (cheap single-row read,
|
|
7
|
-
* allows config changes to take effect immediately without restart).
|
|
8
|
-
* Falls back to 60 req/min if the config record is missing or the field is 0/null.
|
|
9
|
-
*
|
|
10
|
-
* Returns false (and writes the 429 response) when the rate limit is exceeded.
|
|
11
|
-
* Returns true when the request is allowed.
|
|
12
|
-
*
|
|
13
|
-
* Sets OpenAI-compatible rate limit response headers on every request:
|
|
14
|
-
* X-RateLimit-Limit: <limit>
|
|
15
|
-
* X-RateLimit-Remaining: <remaining> (on 429: 0)
|
|
16
|
-
* Retry-After: <seconds> (on 429 only)
|
|
17
|
-
*/
|
|
18
|
-
export declare function createRateLimitMiddleware(limiter: RateLimiter): (ctx: Context) => Promise<boolean>;
|