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.
Files changed (48) hide show
  1. package/dist/client/23.e96ecf13e6072dce.js +10 -0
  2. package/dist/client/{824c2f7487ee05fd.js → 503.29bcdb426b01e715.js} +10 -1
  3. package/dist/client/index.js +10 -1
  4. package/dist/externalVersion.js +19 -8
  5. package/dist/index.js +9 -0
  6. package/dist/locale/en-US.json +10 -10
  7. package/dist/locale/zh-CN.json +10 -10
  8. package/dist/server/collections/ai-api-config.js +9 -0
  9. package/dist/server/collections/ai-api-role-permissions.js +62 -0
  10. package/dist/server/index.js +9 -0
  11. package/dist/server/middleware/rate-limit.js +9 -0
  12. package/dist/server/middleware/role-permission.js +66 -0
  13. package/dist/server/plugin.js +15 -1
  14. package/dist/server/resource/ai-api-config.js +10 -1
  15. package/dist/server/routes/agent-completions.js +39 -5
  16. package/dist/server/routes/auth.js +11 -12
  17. package/dist/server/routes/chat-completions.js +27 -4
  18. package/dist/server/routes/completions.js +16 -4
  19. package/dist/server/routes/embeddings.js +10 -3
  20. package/dist/server/routes/models.js +11 -8
  21. package/dist/server/routes/router.js +37 -5
  22. package/dist/server/utils/openai-format.js +9 -0
  23. package/dist/server/utils/rate-limiter.js +9 -0
  24. package/dist/server/utils/resolve-service.js +9 -0
  25. package/dist/swagger.js +345 -0
  26. package/package.json +14 -13
  27. package/dist/client/AiApiConfigPage.d.ts +0 -3
  28. package/dist/client/index.d.ts +0 -1
  29. package/dist/client/locale.d.ts +0 -10
  30. package/dist/client/models/index.d.ts +0 -10
  31. package/dist/client/plugin.d.ts +0 -6
  32. package/dist/index.d.ts +0 -2
  33. package/dist/server/collections/ai-api-config.d.ts +0 -2
  34. package/dist/server/index.d.ts +0 -1
  35. package/dist/server/middleware/rate-limit.d.ts +0 -18
  36. package/dist/server/plugin.d.ts +0 -18
  37. package/dist/server/resource/ai-api-config.d.ts +0 -10
  38. package/dist/server/routes/agent-completions.d.ts +0 -34
  39. package/dist/server/routes/auth.d.ts +0 -8
  40. package/dist/server/routes/chat-completions.d.ts +0 -9
  41. package/dist/server/routes/completions.d.ts +0 -10
  42. package/dist/server/routes/embeddings.d.ts +0 -22
  43. package/dist/server/routes/models.d.ts +0 -21
  44. package/dist/server/routes/router.d.ts +0 -25
  45. package/dist/server/utils/openai-format.d.ts +0 -99
  46. package/dist/server/utils/rate-limiter.d.ts +0 -35
  47. package/dist/server/utils/resolve-service.d.ts +0 -19
  48. 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)(404, `LLM service '${service.title || service.name}' is disabled`, "invalid_request_error", "model_not_found");
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 (0, import_completions.handleCompletions)(ctx, plugin);
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 ((config == null ? void 0 : config.mode) === "agent" || (config == null ? void 0 : config.mode) === "llm") {
174
- return config.mode;
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;
@@ -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.1",
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-ai": "2.x",
11
- "@nocobase/plugin-api-keys": "2.x"
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
+ }
@@ -1,3 +0,0 @@
1
- import React from 'react';
2
- export declare function AiApiConfigPage(): React.JSX.Element;
3
- export default AiApiConfigPage;
@@ -1 +0,0 @@
1
- export { default } from './plugin';
@@ -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;
@@ -1,6 +0,0 @@
1
- import { Plugin } from '@nocobase/client';
2
- export declare class PluginAiApiClient extends Plugin {
3
- load(): Promise<void>;
4
- t: (key: string) => string;
5
- }
6
- export default PluginAiApiClient;
package/dist/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './server';
2
- export { default } from './server';
@@ -1,2 +0,0 @@
1
- declare const _default: any;
2
- export default _default;
@@ -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>;