From 41d827e58a07c767c5fe6bb07583084068bb288f Mon Sep 17 00:00:00 2001 From: tianma-if Date: Fri, 25 Sep 2026 00:03:38 +0800 Subject: [PATCH] feat(plugins): raise AI output request ceiling to 32768 --- apps/api/src/plugin-capability-routes.test.mjs | 12 ++++++++++++ docs/plugin-development.md | 2 +- docs/plugin-development.zh-CN.md | 2 +- packages/shared/src/plugin-capabilities.ts | 2 +- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/apps/api/src/plugin-capability-routes.test.mjs b/apps/api/src/plugin-capability-routes.test.mjs index d5e48c4c..7d2ac38a 100644 --- a/apps/api/src/plugin-capability-routes.test.mjs +++ b/apps/api/src/plugin-capability-routes.test.mjs @@ -68,6 +68,18 @@ test('generic AI validates ordinary prompts, returns text, and redacts provider const response = await failed('ai/generate', { system: '', prompt: 'hello' }); expect(response.status).toBe(502); expect(await response.text()).not.toContain('dummy-provider-secret'); }); +test('generic AI accepts the larger output ceiling and rejects requests above it', async () => { + const call = fixture({ + generate: async input => ({ text: String(input.maxOutputTokens) }), + loadCredentials: async () => ({ provider: 'openai-compatible', baseUrl: 'https://api.example/v1', apiKey: 'plugin-key', modelId: 'model-a' }), + }); + const input = { system: '', prompt: 'hello', maxOutputTokens: 32768 }; + expect(await (await call('ai/generate', input)).json()).toEqual({ text: '32768' }); + expect((await call('ai/generate/prepare', input)).status).toBe(200); + expect((await call('ai/generate', { ...input, maxOutputTokens: 32769 })).status).toBe(400); + expect((await call('ai/generate/prepare', { ...input, maxOutputTokens: 32769 })).status).toBe(400); +}); + test('concurrent requests are bounded and released after completion', async () => { let started = 0; let release; const gate = new Promise(resolve => { release = resolve; }); const call = fixture({ fetch: async () => { started++; await gate; return new Response('ok'); } }); diff --git a/docs/plugin-development.md b/docs/plugin-development.md index e78b837f..d32d3ae4 100644 --- a/docs/plugin-development.md +++ b/docs/plugin-development.md @@ -598,7 +598,7 @@ const result = await context.ai.generate({ }); ``` -`system` is limited to 8,000 characters, `prompt` to 90,000, output to 5,000 tokens, and generation to 120 seconds. The backend requires an interactive user session, disables AI in public demo mode, and redacts provider errors. AI calls have a four-request per-workspace guard in each backend instance; this is not a distributed quota. Model charges follow the configured provider. Plugin deactivation aborts outstanding calls. +`system` is limited to 8,000 characters, `prompt` to 90,000, and requested output to 32,768 tokens; omitted `maxOutputTokens` defaults to 3,000. Generation is limited to 120 seconds. The model or provider may impose a lower limit or reject a request based on available credits. The backend requires an interactive user session, disables AI in public demo mode, and redacts provider errors. AI calls have a four-request per-workspace guard in each backend instance; this is not a distributed quota. Model charges follow the configured provider. Plugin deactivation aborts outstanding calls. The default `network.fetch(url, init)` transport is a trusted browser request. It accepts arbitrary HTTP/HTTPS destinations, methods, bodies, request headers such as `Authorization`, and the requested browser credential mode. It remains subject to the runtime browser's CORS and cookie policy. `networkHosts` is legacy descriptive metadata and is not a security boundary. To read a cross-origin public feed or API without credentials, explicitly select `transport: "public"`; listing `network` and `network:public` remains useful disclosure but is optional. diff --git a/docs/plugin-development.zh-CN.md b/docs/plugin-development.zh-CN.md index e2a92f0f..9811c5d8 100644 --- a/docs/plugin-development.zh-CN.md +++ b/docs/plugin-development.zh-CN.md @@ -598,7 +598,7 @@ const result = await context.ai.generate({ }); ``` -`system` 最多 8,000 字符,`prompt` 最多 90,000 字符,输出最多 5,000 token,生成最长 120 秒。后端要求交互式用户会话,公开演示模式禁用 AI,供应商错误脱敏。每个后端实例对每工作区的 AI 调用设置四路并发保护,不是分布式配额。模型费用沿用已配置供应商的计费;停用插件会中止其调用。 +`system` 最多 8,000 字符,`prompt` 最多 90,000 字符,请求的输出上限最多 32,768 token;省略 `maxOutputTokens` 时默认 3,000。生成最长 120 秒。模型或供应商可能有更低的限制,也可能因可用额度不足拒绝请求。后端要求交互式用户会话,公开演示模式禁用 AI,供应商错误脱敏。每个后端实例对每工作区的 AI 调用设置四路并发保护,不是分布式配额。模型费用沿用已配置供应商的计费;停用插件会中止其调用。 默认的 `network.fetch(url, init)` 是受信任的浏览器请求,可以访问任意 HTTP/HTTPS 地址,使用任意方法、正文、`Authorization` 等请求头以及调用方指定的浏览器凭据模式;它仍受所在运行时的 CORS 与 Cookie 策略约束。`networkHosts` 仅为兼容旧版保留,不是安全边界。需要无凭据读取跨域公开订阅或 API 时,显式选择 `transport: "public"` 即可;列出 `network` 和 `network:public` 仍有助于披露用途,但不是必需条件: diff --git a/packages/shared/src/plugin-capabilities.ts b/packages/shared/src/plugin-capabilities.ts index 83278f17..9123f06b 100644 --- a/packages/shared/src/plugin-capabilities.ts +++ b/packages/shared/src/plugin-capabilities.ts @@ -3,7 +3,7 @@ import { z } from 'zod'; export const PluginAiGenerateSchema = z.object({ system: z.string().max(8000), prompt: z.string().min(1).max(90000), - maxOutputTokens: z.number().int().min(1).max(5000).optional(), + maxOutputTokens: z.number().int().min(1).max(32768).optional(), }).strict(); export type PluginAiGenerateRequest = z.infer; export interface PluginAiStatus { configured: boolean; modelName?: string }