plugin-ai-api 1.0.2 → 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.
Files changed (88) hide show
  1. package/README.md +15 -1
  2. package/client-v2.d.ts +2 -0
  3. package/client-v2.js +1 -0
  4. package/dist/client/778.180aac1b13a38702.js +10 -0
  5. package/dist/client/950.2797862732563625.js +10 -0
  6. package/dist/client/index.js +10 -1
  7. package/dist/client-v2/950.8991b849cd7c1351.js +10 -0
  8. package/dist/client-v2/index.js +10 -0
  9. package/dist/externalVersion.js +19 -8
  10. package/dist/index.js +9 -0
  11. package/dist/locale/en-US.json +10 -10
  12. package/dist/locale/zh-CN.json +10 -10
  13. package/dist/server/collections/ai-api-config.js +9 -0
  14. package/dist/server/collections/ai-api-role-permissions.js +9 -0
  15. package/dist/server/index.js +9 -0
  16. package/dist/server/middleware/rate-limit.js +9 -0
  17. package/dist/server/middleware/role-permission.js +9 -0
  18. package/dist/server/plugin.js +14 -0
  19. package/dist/server/resource/ai-api-config.js +9 -0
  20. package/dist/server/routes/agent-completions.js +21 -4
  21. package/dist/server/routes/auth.js +11 -12
  22. package/dist/server/routes/chat-completions.js +16 -4
  23. package/dist/server/routes/completions.js +16 -4
  24. package/dist/server/routes/embeddings.js +10 -3
  25. package/dist/server/routes/models.js +11 -8
  26. package/dist/server/routes/router.js +20 -4
  27. package/dist/server/utils/openai-format.js +9 -0
  28. package/dist/server/utils/rate-limiter.js +9 -0
  29. package/dist/server/utils/resolve-service.js +9 -0
  30. package/dist/swagger.js +10 -4
  31. package/package.json +21 -3
  32. package/src/client/AiApiConfigPage.tsx +309 -0
  33. package/src/client/client.d.ts +258 -0
  34. package/src/client/components/AiApiRolePermissions.tsx +169 -0
  35. package/{dist/client/models/index.d.ts → src/client/index.tsx} +10 -10
  36. package/src/client/locale.ts +21 -0
  37. package/src/client/models/index.ts +12 -0
  38. package/src/client/plugin.tsx +48 -0
  39. package/src/client-v2/plugin.tsx +24 -0
  40. package/{dist/client/locale.d.ts → src/index.ts} +11 -10
  41. package/src/locale/en-US.json +10 -0
  42. package/src/locale/zh-CN.json +10 -0
  43. package/src/server/collections/.gitkeep +0 -0
  44. package/src/server/collections/ai-api-config.ts +51 -0
  45. package/src/server/collections/ai-api-role-permissions.ts +41 -0
  46. package/src/server/index.ts +10 -0
  47. package/src/server/middleware/rate-limit.ts +70 -0
  48. package/src/server/middleware/role-permission.ts +66 -0
  49. package/src/server/plugin.ts +89 -0
  50. package/src/server/resource/ai-api-config.ts +74 -0
  51. package/src/server/routes/agent-completions.ts +428 -0
  52. package/src/server/routes/auth.ts +111 -0
  53. package/src/server/routes/chat-completions.ts +318 -0
  54. package/src/server/routes/completions.ts +299 -0
  55. package/src/server/routes/embeddings.ts +191 -0
  56. package/src/server/routes/models.ts +195 -0
  57. package/src/server/routes/router.ts +283 -0
  58. package/src/server/utils/openai-format.ts +142 -0
  59. package/src/server/utils/rate-limiter.ts +83 -0
  60. package/src/server/utils/resolve-service.ts +82 -0
  61. package/src/swagger.ts +325 -0
  62. package/dist/client/2c126424a58bcb27.js +0 -1
  63. package/dist/client/824c2f7487ee05fd.js +0 -1
  64. package/dist/client/AiApiConfigPage.d.ts +0 -3
  65. package/dist/client/components/AiApiRolePermissions.d.ts +0 -8
  66. package/dist/client/plugin.d.ts +0 -6
  67. package/dist/index.d.ts +0 -2
  68. package/dist/server/collections/ai-api-config.d.ts +0 -2
  69. package/dist/server/collections/ai-api-role-permissions.d.ts +0 -2
  70. package/dist/server/index.d.ts +0 -1
  71. package/dist/server/middleware/rate-limit.d.ts +0 -18
  72. package/dist/server/middleware/role-permission.d.ts +0 -21
  73. package/dist/server/plugin.d.ts +0 -18
  74. package/dist/server/resource/ai-api-config.d.ts +0 -10
  75. package/dist/server/routes/agent-completions.d.ts +0 -34
  76. package/dist/server/routes/auth.d.ts +0 -8
  77. package/dist/server/routes/chat-completions.d.ts +0 -9
  78. package/dist/server/routes/completions.d.ts +0 -10
  79. package/dist/server/routes/embeddings.d.ts +0 -22
  80. package/dist/server/routes/models.d.ts +0 -21
  81. package/dist/server/routes/router.d.ts +0 -25
  82. package/dist/server/utils/openai-format.d.ts +0 -99
  83. package/dist/server/utils/rate-limiter.d.ts +0 -35
  84. package/dist/server/utils/resolve-service.d.ts +0 -19
  85. package/dist/swagger.d.ts +0 -472
  86. package/plugin-ai-api-1.0.1.tgz +0 -0
  87. package/plugin-ai-api-1.0.2.tgz +0 -0
  88. /package/{dist/client/index.d.ts → src/client-v2/index.tsx} +0 -0
@@ -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,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>;
@@ -1,21 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- /**
3
- * Check whether the authenticated role is allowed to use the AI API.
4
- * Loads the permission record and stores it in ctx.state.aiApiRolePermission.
5
- *
6
- * Returns true if access is allowed (caller may proceed).
7
- * Returns false if access is denied (403 already written to ctx, caller must return).
8
- *
9
- * The 'root' and 'admin' roles always bypass the check.
10
- */
11
- export declare function checkRolePermission(ctx: Context): Promise<boolean>;
12
- /**
13
- * Check whether the current role is allowed to use a specific AI Employee.
14
- * Must be called after checkRolePermission (so ctx.state.aiApiRolePermission is set).
15
- *
16
- * Returns true when:
17
- * - Role is admin/root (no permission record stored)
18
- * - allowAllEmployees is true
19
- * - The employeeUsername is in the allowedEmployees list
20
- */
21
- export declare function checkEmployeeAccess(ctx: Context, employeeUsername: string): boolean;
@@ -1,18 +0,0 @@
1
- import { Plugin } from '@nocobase/server';
2
- import { RateLimiter } from './utils/rate-limiter';
3
- export declare class PluginAiApiServer extends Plugin {
4
- /**
5
- * Singleton rate limiter — lives for the entire plugin lifetime, shared across all requests.
6
- * Uses a 1-minute sliding window to enforce rateLimitPerMinute from aiApiConfig.
7
- */
8
- rateLimiter: RateLimiter;
9
- private gcInterval;
10
- afterAdd(): Promise<void>;
11
- beforeLoad(): Promise<void>;
12
- load(): Promise<void>;
13
- install(): Promise<void>;
14
- afterEnable(): Promise<void>;
15
- afterDisable(): Promise<void>;
16
- remove(): Promise<void>;
17
- }
18
- export default PluginAiApiServer;
@@ -1,10 +0,0 @@
1
- import { ResourceOptions } from '@nocobase/resourcer';
2
- /**
3
- * Resource for managing AI API configuration via NocoBase admin UI.
4
- * Singleton config pattern (same as aiSettings in plugin-ai).
5
- *
6
- * Uses custom action name 'save' instead of 'update' because NocoBase's
7
- * built-in middleware requires filter/filterByTk for the standard 'update' action.
8
- */
9
- declare const aiApiConfigResource: ResourceOptions;
10
- export default aiApiConfigResource;
@@ -1,34 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- import type PluginAiApiServer from '../plugin';
3
- /**
4
- * POST /api/ai-llm/v1/chat/completions (agent mode)
5
- *
6
- * Runs the full AI Employee pipeline by directly instantiating AIEmployee
7
- * from plugin-ai. This provides TRUE real-time streaming — no buffering.
8
- *
9
- * ## Architecture (vs old approach)
10
- *
11
- * OLD (fake streaming, HTTP loopback):
12
- * Client → ai-api → HTTP to localhost → aiConversations:sendMessages
13
- * ← buffers entire response ←
14
- * Client ← 20-char fake chunks ← re-emit
15
- *
16
- * NEW (true streaming, direct instantiation):
17
- * Client → ai-api → AIEmployee(ctx) ← writes directly to ctx.res
18
- * We intercept ctx.res.write to translate NocoBase SSE → OpenAI SSE
19
- * Client ← real-time OpenAI SSE chunks ← write() intercept
20
- *
21
- * ## NocoBase → OpenAI SSE translation
22
- *
23
- * NocoBase emits: `data: {"type":"content","body":"chunk text"}\n\n`
24
- * We emit: `data: {"choices":[{"delta":{"content":"chunk text"},...}]}\n\n`
25
- *
26
- * Other NocoBase event types (tool_calls, stream_start, etc.) are silently
27
- * ignored — they are NocoBase-internal events not part of the OpenAI protocol.
28
- *
29
- * ## Known limitation
30
- * Token usage is always { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
31
- * in agent mode. ResponseMetadataCollector is private inside AIEmployee with no
32
- * public accessor. Modifying plugin-ai is out of scope for this plugin.
33
- */
34
- export declare function handleAgentCompletions(ctx: Context, plugin: PluginAiApiServer): Promise<void>;
@@ -1,8 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- /**
3
- * Authentication middleware for /api/ai-llm/v1/* routes.
4
- *
5
- * Validates Bearer token against NocoBase's apiKeys collection.
6
- * Sets ctx.state.currentUser for downstream handlers.
7
- */
8
- export declare function authenticateBearer(ctx: Context): Promise<boolean>;
@@ -1,9 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- import type PluginAiApiServer from '../plugin';
3
- /**
4
- * POST /api/ai-llm/v1/chat/completions
5
- *
6
- * Handles OpenAI-compatible chat completion requests.
7
- * Supports both streaming (SSE) and non-streaming modes.
8
- */
9
- export declare function handleChatCompletions(ctx: Context, plugin: PluginAiApiServer): Promise<void>;
@@ -1,10 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- import type PluginAiApiServer from '../plugin';
3
- /**
4
- * POST /api/ai-llm/v1/completions
5
- *
6
- * Handles legacy OpenAI text completions API.
7
- * Converts prompt to messages and delegates to the chat model.
8
- * Required by LiteLLM and other tools that test via legacy completions endpoint.
9
- */
10
- export declare function handleCompletions(ctx: Context, plugin: PluginAiApiServer): Promise<void>;
@@ -1,22 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- import type PluginAiApiServer from '../plugin';
3
- /**
4
- * POST /api/ai-llm/v1/embeddings
5
- *
6
- * OpenAI-compatible embeddings endpoint.
7
- *
8
- * Supported providers (those with an `embedding` field in LLMProviderMeta):
9
- * - openai → OpenAiEmbeddingProvider
10
- * - openai-completions → OpenAiEmbeddingProvider
11
- * - dashscope → DashscopeEmbeddingProvider
12
- * - google-genai → GoogleGenAIEmbeddingProvider
13
- * - ollama → OllamaEmbeddingProvider
14
- *
15
- * Not supported: anthropic, deepseek, kimi (no embedding provider registered).
16
- *
17
- * Limitations:
18
- * - encoding_format 'base64' is not supported (always returns float arrays)
19
- * - Token counts always return 0 (LangChain embeddings API doesn't expose this)
20
- * - Token array input (integer[]) is not supported, only string input
21
- */
22
- export declare function handleEmbeddings(ctx: Context, plugin: PluginAiApiServer): Promise<void>;
@@ -1,21 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- import type PluginAiApiServer from '../plugin';
3
- /**
4
- * GET /api/ai-llm/v1/models
5
- *
6
- * Lists all available models from enabled LLM services.
7
- * Model IDs use the "serviceName/modelId" format (e.g. "my-openai/gpt-4o")
8
- * so clients can copy-paste the ID directly into POST /v1/chat/completions
9
- * without needing to configure a defaultLlmService.
10
- *
11
- * Backward compatibility: resolveModelString() in resolve-service.ts still
12
- * accepts bare model IDs via its 3-tier fallback (defaultLlmService / single service).
13
- */
14
- export declare function handleListModels(ctx: Context, plugin: PluginAiApiServer): Promise<void>;
15
- /**
16
- * GET /api/ai-llm/v1/models/:model
17
- *
18
- * Retrieve a single model by ID.
19
- * Accepts both "serviceName/modelId" format (new) and bare "modelId" (backward compat).
20
- */
21
- export declare function handleGetModel(ctx: Context, modelId: string, plugin: PluginAiApiServer): Promise<void>;
@@ -1,25 +0,0 @@
1
- import type PluginAiApiServer from '../plugin';
2
- /**
3
- * Main Koa middleware router for OpenAI-compatible endpoints.
4
- *
5
- * Intercepts all requests to /api/ai-llm/v1/* and routes them
6
- * to the appropriate handler. Runs before NocoBase's resourcer
7
- * so the URL paths follow OpenAI convention.
8
- *
9
- * Features:
10
- * - CORS support (Access-Control-Allow-Origin: *)
11
- * - OPTIONS preflight handling (204)
12
- * - X-Request-Id on every response
13
- * - Bearer token authentication
14
- * - Sliding window rate limiting (enforces rateLimitPerMinute from config)
15
- * - Structured request logging via app.logger
16
- *
17
- * Supported endpoints:
18
- * POST /v1/chat/completions — OpenAI chat completions (LLM or agent mode)
19
- * POST /v1/completions — Legacy text completions (LiteLLM compat)
20
- * POST /v1/embeddings — OpenAI embeddings
21
- * GET /v1/models — List available models
22
- * GET /v1/models/:id — Get a single model
23
- * DELETE /v1/models/:id — Not implemented (501 stub)
24
- */
25
- export declare function createAiLlmRouter(plugin: PluginAiApiServer): (ctx: Context, next: Next) => Promise<any>;
@@ -1,99 +0,0 @@
1
- export interface ParsedModel {
2
- llmService: string;
3
- modelId: string;
4
- }
5
- /**
6
- * Parse OpenAI-style model string into NocoBase llmService + modelId.
7
- * Format: "llmServiceName/modelId" (e.g. "my-openai/gpt-4o")
8
- * If no "/" is present, the entire string is treated as modelId and llmService is empty.
9
- */
10
- export declare function parseModelString(model: string): ParsedModel;
11
- export declare function generateCompletionId(): string;
12
- export declare function toOpenAIError(statusCode: number, message: string, type?: string, code?: string): {
13
- error: {
14
- message: string;
15
- type: string;
16
- param: any;
17
- code: string;
18
- };
19
- };
20
- export declare function toOpenAIResponse(options: {
21
- id: string;
22
- model: string;
23
- content: string;
24
- finishReason?: string;
25
- usage?: {
26
- prompt_tokens?: number;
27
- completion_tokens?: number;
28
- total_tokens?: number;
29
- };
30
- }): {
31
- id: string;
32
- object: string;
33
- created: number;
34
- model: string;
35
- system_fingerprint: any;
36
- choices: {
37
- index: number;
38
- message: {
39
- role: string;
40
- content: string;
41
- };
42
- logprobs: any;
43
- finish_reason: string;
44
- }[];
45
- usage: {
46
- prompt_tokens?: number;
47
- completion_tokens?: number;
48
- total_tokens?: number;
49
- };
50
- };
51
- export declare function toOpenAIStreamChunk(options: {
52
- id: string;
53
- model: string;
54
- delta: {
55
- role?: string;
56
- content?: string;
57
- };
58
- finishReason?: string | null;
59
- }): {
60
- id: string;
61
- object: string;
62
- created: number;
63
- model: string;
64
- system_fingerprint: any;
65
- choices: {
66
- index: number;
67
- delta: {
68
- role?: string;
69
- content?: string;
70
- };
71
- logprobs: any;
72
- finish_reason: string;
73
- }[];
74
- };
75
- export declare function toOpenAIEmbeddingsResponse(options: {
76
- model: string;
77
- embeddings: number[][];
78
- promptTokens?: number;
79
- }): {
80
- object: "list";
81
- data: {
82
- object: "embedding";
83
- embedding: number[];
84
- index: number;
85
- }[];
86
- model: string;
87
- usage: {
88
- prompt_tokens: number;
89
- total_tokens: number;
90
- };
91
- };
92
- /**
93
- * Format a streaming chunk as an SSE data line.
94
- */
95
- export declare function formatSSE(data: any): string;
96
- /**
97
- * Format the terminal SSE [DONE] signal.
98
- */
99
- export declare function formatSSEDone(): string;
@@ -1,35 +0,0 @@
1
- /**
2
- * In-memory sliding window rate limiter.
3
- *
4
- * Stores per-user request timestamps. On each check: prunes timestamps
5
- * older than the window, counts the remainder, and accepts/rejects.
6
- *
7
- * Single-process safe (Node.js event loop). Not distributed.
8
- * For multi-process deployments, replace with a Redis-backed implementation.
9
- */
10
- export declare class RateLimiter {
11
- private readonly windowMs;
12
- /** Map<userId, sorted array of request timestamps in ms> */
13
- private readonly store;
14
- constructor(windowMs?: number);
15
- /**
16
- * Check and record a request for a user.
17
- *
18
- * @param userId The user ID (string or numeric)
19
- * @param limit Max allowed requests per window (from aiApiConfig.rateLimitPerMinute)
20
- * @returns { allowed: true } or { allowed: false, retryAfterMs: number }
21
- */
22
- check(userId: string | number, limit: number): {
23
- allowed: true;
24
- } | {
25
- allowed: false;
26
- retryAfterMs: number;
27
- };
28
- /**
29
- * Garbage collect entries for inactive users.
30
- * Call every ~5 minutes to prevent unbounded memory growth in long-running servers.
31
- */
32
- gc(): void;
33
- /** Clear all state (useful in tests). */
34
- clear(): void;
35
- }
@@ -1,19 +0,0 @@
1
- import { Context } from '@nocobase/actions';
2
- /**
3
- * Resolve an LLM service by name or title.
4
- */
5
- export declare function resolveLlmService(ctx: Context, serviceKey: string): Promise<any>;
6
- /**
7
- * Resolve a model string to a service + modelId.
8
- *
9
- * Strategy (priority order):
10
- * 1. Try splitting at each "/" position and match the left part against DB (name or title).
11
- * This handles cases like "Custom LLM (OpenAI Compatible)/qwen/qwen3.6-plus-preview:free"
12
- * 2. If no service match, use the defaultLlmService from config and treat the ENTIRE
13
- * model string as the modelId. This allows clients to send just "qwen/qwen3.6-plus-preview:free"
14
- * or "gpt-4o" without knowing the service name.
15
- */
16
- export declare function resolveModelString(ctx: Context, modelString: string): Promise<{
17
- service: any;
18
- modelId: string;
19
- } | null>;