From 3415df37158c031f8bc13eaefbb3392ae86a9a48 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Fri, 3 Apr 2026 00:46:19 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20remove=20chat-?= =?UTF-8?q?plugin-sdk=20(#13512)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ♻️ refactor: remove @lobehub/chat-plugin-sdk dependency Plugins have been deprecated. This removes the SDK entirely: - Define built-in ToolManifest, ToolManifestSettings, ToolErrorType types - Delete src/features/PluginsUI/ (plugin iframe rendering) - Delete src/store/tool/slices/oldStore/ (deprecated plugin store) - Delete src/server/services/pluginGateway/ (plugin gateway) - Delete src/app/(backend)/webapi/plugin/gateway/ (plugin API route) - Migrate all ~50 files from SDK imports to @lobechat/types - Remove @lobehub/chat-plugin-sdk, @lobehub/chat-plugins-gateway deps - Remove @swagger-api/apidom-reference override and patch Fixes LOBE-6655 Co-Authored-By: Claude Opus 4.6 (1M context) * 🐛 fix: add missing getInstalledPlugins mock in customPlugin test Co-Authored-By: Claude Opus 4.6 (1M context) * 🔧 chore: increase Vercel build memory limit to 8192MB The 6144MB limit was causing OOM during Vite SPA chunk rendering. Aligned with other build commands that already use 8192MB. Co-Authored-By: Claude Opus 4.6 (1M context) * ♻️ refactor: unify default tool type to builtin and fix CustomRender - Remove `invokeDefaultTypePlugin` — default type now falls through to builtin in both server and client execution paths - Fix `CustomRender` to actually render builtin tool components via `getBuiltinRender` instead of always returning null - Increase SPA build memory limit from 7168MB to 8192MB to fix OOM Co-Authored-By: Claude Opus 4.6 (1M context) * ♻️ refactor: remove legacy plugin gateway and type-specific invocations - Delete `runPluginApi`, `internal_callPluginApi`, `invokeMarkdownTypePlugin`, `invokeStandaloneTypePlugin` - Remove plugin gateway endpoint (`/webapi/plugin/gateway`) from URL config - Remove special `builtin → default` runtimeType mapping in plugin model - Clean up unused imports and related tests Co-Authored-By: Claude Opus 4.6 (1M context) * 🐛 fix: add 'builtin' to runtimeType union to fix type error Use ToolManifestType instead of inline union for runtimeType fields so that 'builtin' is included as a valid type. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- docs/development/basic/chat-api.mdx | 2 +- docs/development/basic/chat-api.zh-CN.mdx | 2 +- package.json | 6 +- packages/database/src/schemas/user.ts | 10 +- packages/types/package.json | 1 - packages/types/src/discover/plugins.ts | 24 +- packages/types/src/message/common/base.ts | 4 +- packages/types/src/message/common/tools.ts | 3 +- packages/types/src/tool/builtin.ts | 4 +- packages/types/src/tool/error.ts | 5 + packages/types/src/tool/index.ts | 11 +- packages/types/src/tool/manifest.ts | 58 ++++ packages/types/src/tool/plugin.ts | 19 +- packages/utils/package.json | 5 +- packages/utils/src/toolManifest.ts | 62 +---- patches/@swagger-api__apidom-reference.patch | 26 -- pnpm-workspace.yaml | 3 - .../(backend)/webapi/plugin/gateway/route.ts | 52 ---- .../AgentPlugin/LocalPluginItem.tsx | 23 +- .../ChatInput/ActionBar/Tools/useControls.tsx | 3 - src/features/Conversation/Error/index.tsx | 5 +- .../Tool/Detail/Render/CustomRender.tsx | 22 +- .../PluginDevModal/MCPManifestForm/utils.ts | 4 +- .../PluginDevModal/PluginPreview/index.tsx | 4 +- .../PluginDevModal/UrlManifestForm.tsx | 4 +- src/features/PluginSettings/index.tsx | 6 +- .../Render/BuiltinType/index.test.tsx | 69 ----- .../PluginsUI/Render/BuiltinType/index.tsx | 64 ----- .../Render/DefaultType/IFrameRender/index.tsx | 61 ----- .../PluginsUI/Render/DefaultType/index.tsx | 42 --- src/features/PluginsUI/Render/Loading.tsx | 58 ---- .../PluginsUI/Render/MCPType/index.tsx | 74 ------ .../PluginsUI/Render/MarkdownType/index.tsx | 25 -- .../Render/StandaloneType/Iframe.tsx | 163 ------------ .../PluginsUI/Render/StandaloneType/index.tsx | 37 --- src/features/PluginsUI/Render/index.tsx | 115 -------- .../PluginsUI/Render/useParseContent.ts | 15 -- .../Render/utils/iframeOnReady.test.ts | 76 ------ .../PluginsUI/Render/utils/iframeOnReady.ts | 25 -- .../Render/utils/listenToPlugin.test.ts | 164 ------------ .../PluginsUI/Render/utils/listenToPlugin.ts | 98 ------- .../Render/utils/pluginSettings.test.ts | 54 ---- .../PluginsUI/Render/utils/pluginSettings.ts | 17 -- .../Render/utils/pluginState.test.ts | 44 ---- .../PluginsUI/Render/utils/pluginState.ts | 20 -- .../Render/utils/postMessage.test.ts | 64 ----- .../PluginsUI/Render/utils/postMessage.ts | 28 -- .../Portal/Plugins/Body/ToolRender.tsx | 15 +- src/features/ProfileEditor/AgentTool.tsx | 3 - .../SkillStore/SkillList/Community/Item.tsx | 2 +- .../SkillStore/SkillList/Custom/Item.tsx | 2 +- src/helpers/toolEngineering/index.test.ts | 14 +- src/helpers/toolEngineering/index.ts | 15 +- .../MentionList/useMentionItems.tsx | 10 +- .../MentionList/useMentionItems.tsx | 10 +- .../settings/skill/features/Actions.tsx | 20 +- src/server/routers/lambda/klavis.ts | 6 +- .../agentRuntime/AgentRuntimeService.test.ts | 8 - .../agentRuntime/AgentRuntimeService.ts | 3 - .../__tests__/completionWebhook.test.ts | 8 - .../__tests__/executeStep.test.ts | 3 - .../__tests__/executeSync.test.ts | 8 - .../__tests__/stepLifecycleCallbacks.test.ts | 8 - src/server/services/mcp/index.ts | 21 +- src/server/services/pluginGateway/index.ts | 61 ----- .../services/pluginGateway/settings.test.ts | 103 -------- src/server/services/pluginGateway/settings.ts | 29 -- src/server/services/toolExecution/index.ts | 19 +- .../__tests__/__snapshots__/tool.test.ts.snap | 79 ------ src/services/__tests__/_url.test.ts | 1 - src/services/__tests__/tool.test.ts | 8 - src/services/_url.ts | 3 - src/services/chat/chat.test.ts | 17 -- src/services/chat/index.ts | 41 +-- src/services/mcp.test.ts | 17 +- src/services/plugin/index.ts | 7 +- src/services/tool.ts | 3 +- src/store/chat/slices/plugin/action.test.ts | 236 ----------------- .../chat/slices/plugin/actions/internals.ts | 13 +- .../chat/slices/plugin/actions/pluginTypes.ts | 122 --------- .../chat/slices/plugin/actions/publicApi.ts | 17 +- src/store/tool/helpers.ts | 5 +- src/store/tool/initialState.ts | 3 - src/store/tool/selectors/index.ts | 1 - src/store/tool/selectors/tool.test.ts | 8 +- src/store/tool/selectors/tool.ts | 7 +- .../tool/slices/customPlugin/action.test.ts | 1 + src/store/tool/slices/customPlugin/action.ts | 4 +- .../slices/customPlugin/selectors.test.ts | 6 +- src/store/tool/slices/mcpStore/action.test.ts | 8 +- src/store/tool/slices/mcpStore/action.ts | 14 +- .../tool/slices/mcpStore/initialState.ts | 4 + .../tool/slices/mcpStore/selectors.test.ts | 13 +- src/store/tool/slices/oldStore/action.test.ts | 249 ------------------ src/store/tool/slices/oldStore/action.ts | 197 -------------- src/store/tool/slices/oldStore/index.ts | 2 - .../tool/slices/oldStore/initialState.ts | 51 ---- .../tool/slices/oldStore/selectors.test.ts | 54 ---- src/store/tool/slices/oldStore/selectors.ts | 45 ---- src/store/tool/slices/plugin/action.test.ts | 10 +- src/store/tool/slices/plugin/action.ts | 33 +++ src/store/tool/slices/plugin/initialState.ts | 2 + .../tool/slices/plugin/reducers/manifest.ts | 4 +- .../tool/slices/plugin/selectors.test.ts | 42 +-- src/store/tool/slices/plugin/selectors.ts | 20 +- src/store/tool/store.ts | 4 - 106 files changed, 310 insertions(+), 3090 deletions(-) create mode 100644 packages/types/src/tool/error.ts create mode 100644 packages/types/src/tool/manifest.ts delete mode 100644 patches/@swagger-api__apidom-reference.patch delete mode 100644 src/app/(backend)/webapi/plugin/gateway/route.ts delete mode 100644 src/features/PluginsUI/Render/BuiltinType/index.test.tsx delete mode 100644 src/features/PluginsUI/Render/BuiltinType/index.tsx delete mode 100644 src/features/PluginsUI/Render/DefaultType/IFrameRender/index.tsx delete mode 100644 src/features/PluginsUI/Render/DefaultType/index.tsx delete mode 100644 src/features/PluginsUI/Render/Loading.tsx delete mode 100644 src/features/PluginsUI/Render/MCPType/index.tsx delete mode 100644 src/features/PluginsUI/Render/MarkdownType/index.tsx delete mode 100644 src/features/PluginsUI/Render/StandaloneType/Iframe.tsx delete mode 100644 src/features/PluginsUI/Render/StandaloneType/index.tsx delete mode 100644 src/features/PluginsUI/Render/index.tsx delete mode 100644 src/features/PluginsUI/Render/useParseContent.ts delete mode 100644 src/features/PluginsUI/Render/utils/iframeOnReady.test.ts delete mode 100644 src/features/PluginsUI/Render/utils/iframeOnReady.ts delete mode 100644 src/features/PluginsUI/Render/utils/listenToPlugin.test.ts delete mode 100644 src/features/PluginsUI/Render/utils/listenToPlugin.ts delete mode 100644 src/features/PluginsUI/Render/utils/pluginSettings.test.ts delete mode 100644 src/features/PluginsUI/Render/utils/pluginSettings.ts delete mode 100644 src/features/PluginsUI/Render/utils/pluginState.test.ts delete mode 100644 src/features/PluginsUI/Render/utils/pluginState.ts delete mode 100644 src/features/PluginsUI/Render/utils/postMessage.test.ts delete mode 100644 src/features/PluginsUI/Render/utils/postMessage.ts delete mode 100644 src/server/services/pluginGateway/index.ts delete mode 100644 src/server/services/pluginGateway/settings.test.ts delete mode 100644 src/server/services/pluginGateway/settings.ts delete mode 100644 src/services/__tests__/__snapshots__/tool.test.ts.snap delete mode 100644 src/store/tool/slices/oldStore/action.test.ts delete mode 100644 src/store/tool/slices/oldStore/action.ts delete mode 100644 src/store/tool/slices/oldStore/index.ts delete mode 100644 src/store/tool/slices/oldStore/initialState.ts delete mode 100644 src/store/tool/slices/oldStore/selectors.test.ts delete mode 100644 src/store/tool/slices/oldStore/selectors.ts diff --git a/docs/development/basic/chat-api.mdx b/docs/development/basic/chat-api.mdx index bcbe600fbe..614f1d3d9c 100644 --- a/docs/development/basic/chat-api.mdx +++ b/docs/development/basic/chat-api.mdx @@ -179,7 +179,7 @@ This system is expected to be gradually deprecated in favor of the MCP tool system. - Frontend calls them via the - `invokeDefaultTypePlugin` method + `invokeBuiltinTool` method - Retrieves plugin settings and manifest, creates authentication headers, and sends requests to the plugin gateway diff --git a/docs/development/basic/chat-api.zh-CN.mdx b/docs/development/basic/chat-api.zh-CN.mdx index 98ed943cc9..9c252a438f 100644 --- a/docs/development/basic/chat-api.zh-CN.mdx +++ b/docs/development/basic/chat-api.zh-CN.mdx @@ -159,7 +159,7 @@ while (state.status !== 'done' && state.status !== 'error') { **Plugin 工具**:传统插件体系,通过 API 网关调用。 该体系预期将逐步废弃,由 MCP 工具体系替代。 -- 前端通过 `invokeDefaultTypePlugin` 方法调用 +- 前端通过 `invokeBuiltinTool` 方法调用 - 获取插件设置和清单、创建认证请求头、 发送请求到插件网关 diff --git a/package.json b/package.json index 8c0bf1cba7..e1baa81cbd 100644 --- a/package.json +++ b/package.json @@ -40,11 +40,11 @@ "build:next": "cross-env NODE_OPTIONS=--max-old-space-size=7168 bun run build:next:raw", "build:next:raw": "next build", "build:raw": "bun run build:spa:raw && bun run build:spa:copy && bun run build:next:raw", - "build:spa": "cross-env NODE_OPTIONS=--max-old-space-size=7168 pnpm run build:spa:raw", + "build:spa": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm run build:spa:raw", "build:spa:copy": "tsx scripts/copySpaBuild.mts && tsx scripts/generateSpaTemplates.mts", "build:spa:mobile": "cross-env NODE_OPTIONS=--max-old-space-size=8192 MOBILE=true vite build", "build:spa:raw": "rm -rf public/_spa && vite build", - "build:vercel": "cross-env-shell NODE_OPTIONS=--max-old-space-size=6144 \"bun run build:raw && bun run db:migrate\"", + "build:vercel": "cross-env-shell NODE_OPTIONS=--max-old-space-size=8192 \"bun run build:raw && bun run db:migrate\"", "build-migrate-db": "bun run db:migrate", "build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts", "clean:node_modules": "bash -lc 'set -e; echo \"Removing all node_modules...\"; rm -rf node_modules; pnpm -r exec rm -rf node_modules; rm -rf apps/desktop/node_modules; echo \"All node_modules removed.\"'", @@ -262,8 +262,6 @@ "@lobechat/web-crawler": "workspace:*", "@lobehub/analytics": "^1.6.0", "@lobehub/charts": "^5.0.0", - "@lobehub/chat-plugin-sdk": "^1.32.4", - "@lobehub/chat-plugins-gateway": "^1.9.0", "@lobehub/desktop-ipc-typings": "workspace:*", "@lobehub/editor": "^4.5.0", "@lobehub/icons": "^5.0.0", diff --git a/packages/database/src/schemas/user.ts b/packages/database/src/schemas/user.ts index f9e85d3d05..37f53f208a 100644 --- a/packages/database/src/schemas/user.ts +++ b/packages/database/src/schemas/user.ts @@ -1,6 +1,10 @@ import { DEFAULT_PREFERENCE } from '@lobechat/const'; -import type { CustomPluginParams, UserAgentOnboarding, UserOnboarding } from '@lobechat/types'; -import type { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; +import type { + CustomPluginParams, + ToolManifest, + UserAgentOnboarding, + UserOnboarding, +} from '@lobechat/types'; import { sql } from 'drizzle-orm'; import { boolean, index, jsonb, pgTable, primaryKey, text, varchar } from 'drizzle-orm/pg-core'; @@ -95,7 +99,7 @@ export const userInstalledPlugins = pgTable( identifier: text('identifier').notNull(), type: text('type', { enum: ['plugin', 'customPlugin'] }).notNull(), - manifest: jsonb('manifest').$type(), + manifest: jsonb('manifest').$type(), settings: jsonb('settings'), customParams: jsonb('custom_params').$type(), source: varchar255('source'), diff --git a/packages/types/package.json b/packages/types/package.json index b57d447ee6..5ecf9d3956 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -7,7 +7,6 @@ "dependencies": { "@lobechat/python-interpreter": "workspace:*", "@lobechat/web-crawler": "workspace:*", - "@lobehub/chat-plugin-sdk": "^1.32.4", "@lobehub/market-sdk": "0.32.2", "@lobehub/market-types": "^1.12.3", "model-bank": "workspace:*", diff --git a/packages/types/src/discover/plugins.ts b/packages/types/src/discover/plugins.ts index 55848ceb7e..00b89064d4 100644 --- a/packages/types/src/discover/plugins.ts +++ b/packages/types/src/discover/plugins.ts @@ -1,5 +1,4 @@ -import type { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; -import type { LobeChatPluginMeta, Meta } from '@lobehub/chat-plugin-sdk/lib/types/market'; +import type { ToolManifest } from '../tool/manifest'; export enum PluginCategory { All = 'all', @@ -24,7 +23,24 @@ export enum PluginSorts { Title = 'title', } -export interface DiscoverPluginItem extends Omit, Meta { +interface PluginMeta { + avatar: string; + description?: string; + tags?: string[]; + title: string; +} + +interface DiscoverPluginMeta { + author: string; + createdAt: string; + homepage: string; + identifier: string; + manifest: string; + meta: PluginMeta; + schemaVersion: number; +} + +export interface DiscoverPluginItem extends Omit, PluginMeta { category?: PluginCategory; } @@ -55,7 +71,7 @@ export interface PluginListResponse { export type PluginSource = 'legacy' | 'market' | 'builtin'; export interface DiscoverPluginDetail extends Omit { - manifest?: LobeChatPluginManifest | string; + manifest?: ToolManifest | string; related: DiscoverPluginItem[]; /** * Plugin source type diff --git a/packages/types/src/message/common/base.ts b/packages/types/src/message/common/base.ts index a46ac7f7da..bf33532daa 100644 --- a/packages/types/src/message/common/base.ts +++ b/packages/types/src/message/common/base.ts @@ -1,8 +1,8 @@ -import type { IPluginErrorType } from '@lobehub/chat-plugin-sdk'; import { z } from 'zod'; import type { ILobeAgentRuntimeErrorType } from '../../agentRuntime'; import type { ErrorType } from '../../fetch'; +import type { IToolErrorType } from '../../tool/error'; /** * Chat message error object @@ -10,7 +10,7 @@ import type { ErrorType } from '../../fetch'; export interface ChatMessageError { body?: any; message?: string; - type: ErrorType | IPluginErrorType | ILobeAgentRuntimeErrorType; + type: ErrorType | IToolErrorType | ILobeAgentRuntimeErrorType; } export const ChatMessageErrorSchema = z.object({ diff --git a/packages/types/src/message/common/tools.ts b/packages/types/src/message/common/tools.ts index e2a19224f7..ff788dcf3e 100644 --- a/packages/types/src/message/common/tools.ts +++ b/packages/types/src/message/common/tools.ts @@ -1,4 +1,3 @@ -import type { IPluginErrorType } from '@lobehub/chat-plugin-sdk'; import type { PartialDeep } from 'type-fest'; import { z } from 'zod'; @@ -129,5 +128,5 @@ export const ChatToolPayloadSchema = z.object({ export interface ChatMessagePluginError { body?: any; message: string; - type: IPluginErrorType; + type: string; } diff --git a/packages/types/src/tool/builtin.ts b/packages/types/src/tool/builtin.ts index 2b012b2420..9d035f71cf 100644 --- a/packages/types/src/tool/builtin.ts +++ b/packages/types/src/tool/builtin.ts @@ -5,7 +5,7 @@ import { type RuntimeStepContext } from '../stepContext'; import { type HumanInterventionConfig, type HumanInterventionPolicy } from './intervention'; import { HumanInterventionConfigSchema, HumanInterventionPolicySchema } from './intervention'; -interface Meta { +export interface Meta { /** * avatar * @desc Avatar of the plugin @@ -35,7 +35,7 @@ interface Meta { title: string; } -const MetaSchema = z.object({ +export const MetaSchema = z.object({ avatar: z.string().optional(), description: z.string().optional(), readme: z.string().optional(), diff --git a/packages/types/src/tool/error.ts b/packages/types/src/tool/error.ts new file mode 100644 index 0000000000..70eb7e4d20 --- /dev/null +++ b/packages/types/src/tool/error.ts @@ -0,0 +1,5 @@ +export const ToolErrorType = { + PluginSettingsInvalid: 'PluginSettingsInvalid', +} as const; + +export type IToolErrorType = (typeof ToolErrorType)[keyof typeof ToolErrorType]; diff --git a/packages/types/src/tool/index.ts b/packages/types/src/tool/index.ts index 579bc6b357..b65531765f 100644 --- a/packages/types/src/tool/index.ts +++ b/packages/types/src/tool/index.ts @@ -1,16 +1,15 @@ -import type { LobeChatPluginManifest, LobePluginType } from '@lobehub/chat-plugin-sdk'; - +import type { ToolManifest, ToolManifestType } from './manifest'; import type { CustomPluginParams } from './plugin'; import type { LobeToolType } from './tool'; export interface LobeTool { customParams?: CustomPluginParams | null; identifier: string; - manifest?: LobeChatPluginManifest | null; + manifest?: ToolManifest | null; /** * use for runtime */ - runtimeType?: 'mcp' | 'default' | 'markdown' | 'standalone'; + runtimeType?: ToolManifestType; settings?: any; // TODO: remove type and then make it required source?: LobeToolType; @@ -21,12 +20,14 @@ export interface LobeTool { type: LobeToolType; } -export type LobeToolRenderType = LobePluginType | 'builtin'; +export type LobeToolRenderType = ToolManifestType; export * from './builtin'; export * from './crawler'; +export * from './error'; export * from './interpreter'; export * from './intervention'; +export * from './manifest'; export * from './plugin'; export * from './search'; export * from './tool'; diff --git a/packages/types/src/tool/manifest.ts b/packages/types/src/tool/manifest.ts new file mode 100644 index 0000000000..35797db0e0 --- /dev/null +++ b/packages/types/src/tool/manifest.ts @@ -0,0 +1,58 @@ +import { z } from 'zod'; + +import { type LobeChatPluginApi, LobeChatPluginApiSchema, type Meta, MetaSchema } from './builtin'; + +export type ToolManifestType = 'builtin' | 'default' | 'markdown' | 'mcp' | 'standalone'; + +export interface ToolManifestSettings { + properties: Record; + required?: string[]; + type: 'object'; +} + +export const ToolManifestSettingsSchema = z.object({ + properties: z.record(z.string(), z.any()), + required: z.array(z.string()).optional(), + type: z.literal('object'), +}); + +export interface ToolManifest { + $schema?: string; + api: LobeChatPluginApi[]; + author?: string; + createdAt?: string; + gateway?: string; + homepage?: string; + identifier: string; + meta: Meta; + openapi?: string; + settings?: ToolManifestSettings; + systemRole?: string; + type?: ToolManifestType; + ui?: { height?: number; mode?: 'iframe' | 'module'; url: string; width?: number }; + version?: string; +} + +export const ToolManifestSchema = z.object({ + $schema: z.string().optional(), + api: z.array(LobeChatPluginApiSchema), + author: z.string().optional(), + createdAt: z.string().optional(), + gateway: z.string().optional(), + homepage: z.string().optional(), + identifier: z.string(), + meta: MetaSchema, + openapi: z.string().optional(), + settings: ToolManifestSettingsSchema.optional(), + systemRole: z.string().optional(), + type: z.enum(['default', 'standalone', 'markdown', 'builtin', 'mcp']).optional(), + ui: z + .object({ + height: z.number().optional(), + mode: z.enum(['iframe', 'module']).optional(), + url: z.string(), + width: z.number().optional(), + }) + .optional(), + version: z.string().optional(), +}); diff --git a/packages/types/src/tool/plugin.ts b/packages/types/src/tool/plugin.ts index 25309feaf9..15446e53db 100644 --- a/packages/types/src/tool/plugin.ts +++ b/packages/types/src/tool/plugin.ts @@ -1,8 +1,8 @@ -import type { LobeChatPluginManifest, Meta } from '@lobehub/chat-plugin-sdk'; - +import type { Meta } from './builtin'; +import type { ToolManifest, ToolManifestType } from './manifest'; import type { LobeToolType } from './tool'; -export type PluginManifestMap = Record; +export type PluginManifestMap = Record; export interface CustomPluginMetadata { avatar?: string; @@ -53,7 +53,7 @@ export interface CustomPluginParams { export interface LobeToolCustomPlugin { customParams?: CustomPluginParams; identifier: string; - manifest?: LobeChatPluginManifest; + manifest?: ToolManifest; settings?: any; type: 'customPlugin'; } @@ -63,7 +63,7 @@ export interface InstallPluginMeta extends Partial { createdAt?: string; homepage?: string; identifier: string; - runtimeType?: 'mcp' | 'default' | 'markdown' | 'standalone' | undefined; + runtimeType?: ToolManifestType; type: LobeToolType; } @@ -71,3 +71,12 @@ export interface PluginInstallError { cause?: string; message: 'noManifest' | 'fetchError' | 'manifestInvalid' | 'urlError'; } + +export interface PluginRequestPayload { + apiName: string; + arguments?: string; + identifier: string; + indexUrl?: string; + manifest?: ToolManifest; + type?: string; +} diff --git a/packages/utils/package.json b/packages/utils/package.json index eb8d3ca0d4..ee5824c55a 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -15,8 +15,8 @@ }, "dependencies": { "@lobechat/const": "workspace:*", + "@lobechat/ssrf-safe-fetch": "workspace:*", "@lobechat/types": "workspace:*", - "@lobehub/chat-plugin-sdk": "^1.32.4", "@vercel/functions": "^3.3.0", "brotli-wasm": "^3.0.1", "chroma-js": "^3.1.2", @@ -33,7 +33,6 @@ "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-html": "^16.0.1", - "@lobechat/ssrf-safe-fetch": "workspace:*", "tokenx": "^1.2.1", "ua-parser-js": "^1.0.41", "uuid": "^11.1.0", @@ -42,4 +41,4 @@ "devDependencies": { "vitest-canvas-mock": "^1.1.3" } -} \ No newline at end of file +} diff --git a/packages/utils/src/toolManifest.ts b/packages/utils/src/toolManifest.ts index 7b471092c0..114509aca4 100644 --- a/packages/utils/src/toolManifest.ts +++ b/packages/utils/src/toolManifest.ts @@ -1,11 +1,9 @@ -import type { OpenAIPluginManifest } from '@lobechat/types'; -import type { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; -import { pluginManifestSchema } from '@lobehub/chat-plugin-sdk'; +import type { ToolManifest } from '@lobechat/types'; +import { ToolManifestSchema } from '@lobechat/types'; import { API_ENDPOINTS } from '@/services/_url'; const fetchJSON = async (url: string, proxy = false): Promise => { - // 2. Send request let res: Response; try { res = await (proxy ? fetch(API_ENDPOINTS.proxy, { body: url, method: 'POST' }) : fetch(url)); @@ -36,67 +34,17 @@ const fetchJSON = async (url: string, proxy = false): Promise => { return data; }; -export const convertOpenAIManifestToLobeManifest = ( - data: OpenAIPluginManifest, -): LobeChatPluginManifest => { - const manifest: LobeChatPluginManifest = { - api: [], - homepage: data.legal_info_url, - identifier: data.name_for_model, - meta: { - avatar: data.logo_url, - description: data.description_for_human, - title: data.name_for_human, - }, - openapi: data.api.url, - systemRole: data.description_for_model, - type: 'default', - version: '1', - }; - switch (data.auth.type) { - case 'none': { - break; - } - case 'service_http': { - manifest.settings = { - properties: { - apiAuthKey: { - default: data.auth.verification_tokens['openai'], - description: 'API Key', - format: 'password', - title: 'API Key', - type: 'string', - }, - }, - type: 'object', - }; - break; - } - } - - return manifest; -}; - export const getToolManifest = async ( url?: string, useProxy: boolean = false, -): Promise => { - // 1. Validate plugin +): Promise => { if (!url) { throw new TypeError('noManifest'); } - // 2. Send request - let data = await fetchJSON(url, useProxy); + const data = await fetchJSON(url, useProxy); - // @ts-ignore - // if there is a description_for_model, it is an OpenAI plugin - // we need convert to lobe plugin - if (data['description_for_model']) { - data = convertOpenAIManifestToLobeManifest(data as any); - } - // 3. Validate plugin file format specification - const parser = pluginManifestSchema.safeParse(data); + const parser = ToolManifestSchema.safeParse(data); if (!parser.success) { throw new TypeError('manifestInvalid', { cause: parser.error }); diff --git a/patches/@swagger-api__apidom-reference.patch b/patches/@swagger-api__apidom-reference.patch deleted file mode 100644 index ed6d2c95e9..0000000000 --- a/patches/@swagger-api__apidom-reference.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/node_modules/.cache/logger/umi.log b/node_modules/.cache/logger/umi.log -new file mode 100644 -index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 -diff --git a/src/parse/parsers/binary/index-node.cjs b/src/parse/parsers/binary/index-node.cjs -index a88ba4d8a0ddff6f4b545ed9368739d94f893eb2..b9b78a29e3c228e7d78e0861b38d734bdef2c854 100644 ---- a/src/parse/parsers/binary/index-node.cjs -+++ b/src/parse/parsers/binary/index-node.cjs -@@ -3,7 +3,7 @@ - var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default; - exports.__esModule = true; - exports.default = void 0; --var _buffer = require("#buffer"); -+var _buffer = require("buffer"); - var _apidomCore = require("@swagger-api/apidom-core"); - var _ParserError = _interopRequireDefault(require("../../../errors/ParserError.cjs")); - var _Parser = _interopRequireDefault(require("../Parser.cjs")); -diff --git a/src/parse/parsers/binary/index-node.mjs b/src/parse/parsers/binary/index-node.mjs -index 3abce8c52fd5eb7b92f33b66fccf6896ccbc89fe..4a3f957cf127a71c7f2b4dfa56512271b6f785ab 100644 ---- a/src/parse/parsers/binary/index-node.mjs -+++ b/src/parse/parsers/binary/index-node.mjs -@@ -1,4 +1,4 @@ --import { Buffer } from '#buffer'; // eslint-disable-line import/order -+import { Buffer } from 'buffer'; // eslint-disable-line import/order - import { ParseResultElement, StringElement } from '@swagger-api/apidom-core'; - import ParserError from "../../../errors/ParserError.mjs"; - import Parser from "../Parser.mjs"; diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 7a431fff1d..e09b866f3d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,8 +9,6 @@ onlyBuiltDependencies: - '@lobehub/editor' overrides: - '@lobehub/chat-plugin-sdk>swagger-client': 3.36.0 - '@swagger-api/apidom-reference': 1.1.0 jose: ^6.1.3 stylelint-config-clean-order: 7.0.0 pdfjs-dist: 5.4.530 @@ -18,5 +16,4 @@ overrides: react-dom: 19.2.4 patchedDependencies: - '@swagger-api/apidom-reference': patches/@swagger-api__apidom-reference.patch '@upstash/qstash': patches/@upstash__qstash.patch diff --git a/src/app/(backend)/webapi/plugin/gateway/route.ts b/src/app/(backend)/webapi/plugin/gateway/route.ts deleted file mode 100644 index a9f5fb8497..0000000000 --- a/src/app/(backend)/webapi/plugin/gateway/route.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { AgentRuntimeError } from '@lobechat/model-runtime'; -import { ChatErrorType, TraceNameMap } from '@lobechat/types'; -import { type PluginRequestPayload } from '@lobehub/chat-plugin-sdk'; -import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway'; - -import { LOBE_CHAT_TRACE_ID } from '@/const/trace'; -import { getAppConfig } from '@/envs/app'; -import { LOBE_CHAT_AUTH_HEADER } from '@/envs/auth'; -import { TraceClient } from '@/libs/traces'; -import { parserPluginSettings } from '@/server/services/pluginGateway/settings'; -import { getTracePayload } from '@/utils/trace'; - -const { PLUGINS_INDEX_URL: pluginsIndexUrl, PLUGIN_SETTINGS } = getAppConfig(); - -const defaultPluginSettings = parserPluginSettings(PLUGIN_SETTINGS); - -const handler = createGatewayOnEdgeRuntime({ defaultPluginSettings, pluginsIndexUrl }); - -export const POST = async (req: Request) => { - // get Authorization from header - const authorization = req.headers.get(LOBE_CHAT_AUTH_HEADER); - if (!authorization) throw AgentRuntimeError.createError(ChatErrorType.Unauthorized); - - // TODO: need to be replace by better telemetry system - // add trace - const tracePayload = getTracePayload(req); - const traceClient = new TraceClient(); - const trace = traceClient.createTrace({ - id: tracePayload?.traceId, - ...tracePayload, - }); - - const { manifest, indexUrl, ...input } = (await req.clone().json()) as PluginRequestPayload; - - const span = trace?.span({ - input, - metadata: { indexUrl, manifest }, - name: TraceNameMap.FetchPluginAPI, - }); - - span?.update({ parentObservationId: tracePayload?.observationId }); - - const res = await handler(req); - - span?.end({ output: await res.clone().text() }); - - if (trace?.id) { - res.headers.set(LOBE_CHAT_TRACE_ID, trace.id); - } - - return res; -}; diff --git a/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx b/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx index 970aeefbc0..ee7a0bcce8 100644 --- a/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx +++ b/src/features/AgentSetting/AgentPlugin/LocalPluginItem.tsx @@ -1,38 +1,19 @@ import { Flexbox } from '@lobehub/ui'; import { Switch } from 'antd'; -import isEqual from 'fast-deep-equal'; import { memo } from 'react'; -import { useToolStore } from '@/store/tool'; - import { useStore } from '../store'; const MarketList = memo<{ id: string }>(({ id }) => { const [toggleAgentPlugin, hasPlugin] = useStore((s) => [s.toggleAgentPlugin, !!s.config.plugins]); const plugins = useStore((s) => s.config.plugins || []); - const [useFetchPluginList, fetchPluginManifest] = useToolStore((s) => [ - s.useFetchPluginStore, - s.installPlugin, - ]); - - const pluginManifestLoading = useToolStore((s) => s.pluginInstallLoading, isEqual); - - useFetchPluginList(); - return ( { + checked={!hasPlugin ? false : plugins.includes(id)} + onChange={() => { toggleAgentPlugin(id); - if (checked) { - fetchPluginManifest(id); - } }} /> diff --git a/src/features/ChatInput/ActionBar/Tools/useControls.tsx b/src/features/ChatInput/ActionBar/Tools/useControls.tsx index 5823b39165..2fc6a4a35a 100644 --- a/src/features/ChatInput/ActionBar/Tools/useControls.tsx +++ b/src/features/ChatInput/ActionBar/Tools/useControls.tsx @@ -59,20 +59,17 @@ export const useControls = ({ setUpdating }: { setUpdating: (updating: boolean) const userAgentSkills = useToolStore(agentSkillsSelectors.getUserAgentSkills, isEqual); const [ - useFetchPluginStore, useFetchUserKlavisServers, useFetchLobehubSkillConnections, useFetchUninstalledBuiltinTools, useFetchAgentSkills, ] = useToolStore((s) => [ - s.useFetchPluginStore, s.useFetchUserKlavisServers, s.useFetchLobehubSkillConnections, s.useFetchUninstalledBuiltinTools, s.useFetchAgentSkills, ]); - useFetchPluginStore(); useFetchInstalledPlugins(); useFetchUninstalledBuiltinTools(true); useFetchAgentSkills(true); diff --git a/src/features/Conversation/Error/index.tsx b/src/features/Conversation/Error/index.tsx index ea0d1b1ab3..3f7cbc4d0c 100644 --- a/src/features/Conversation/Error/index.tsx +++ b/src/features/Conversation/Error/index.tsx @@ -1,9 +1,8 @@ import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const'; import { type ILobeAgentRuntimeErrorType } from '@lobechat/model-runtime'; import { AgentRuntimeErrorType } from '@lobechat/model-runtime'; -import { type ChatMessageError, type ErrorType } from '@lobechat/types'; +import { type ChatMessageError, type ErrorType, type IToolErrorType } from '@lobechat/types'; import { ChatErrorType } from '@lobechat/types'; -import { type IPluginErrorType } from '@lobehub/chat-plugin-sdk'; import { type AlertProps } from '@lobehub/ui'; import { Block, Highlighter, Skeleton } from '@lobehub/ui'; import { memo, useMemo } from 'react'; @@ -52,7 +51,7 @@ const OllamaSetupGuide = dynamic(() => import('./OllamaSetupGuide'), { // Config for the errorMessage display const getErrorAlertConfig = ( - errorType?: IPluginErrorType | ILobeAgentRuntimeErrorType | ErrorType, + errorType?: IToolErrorType | ILobeAgentRuntimeErrorType | ErrorType, ): AlertProps | undefined => { // OpenAIBizError / ZhipuBizError / GoogleBizError / ... if (typeof errorType === 'string' && (errorType.includes('Biz') || errorType.includes('Invalid'))) diff --git a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/CustomRender.tsx b/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/CustomRender.tsx index b0ec9127a2..8c3859400d 100644 --- a/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/CustomRender.tsx +++ b/src/features/Conversation/Messages/AssistantGroup/Tool/Detail/Render/CustomRender.tsx @@ -1,9 +1,9 @@ +import { getBuiltinRender } from '@lobechat/builtin-tools/renders'; +import { type ChatPluginPayload } from '@lobechat/types'; +import { safeParseJSON } from '@lobechat/utils'; import { Flexbox } from '@lobehub/ui'; import { memo } from 'react'; -import PluginRender from '@/features/PluginsUI/Render'; -import { type ChatPluginPayload } from '@/types/index'; - interface CustomRenderProps { content: string; /** @@ -19,19 +19,21 @@ interface CustomRenderProps { } const CustomRender = memo( - ({ toolCallId, messageId, content, pluginState, plugin }) => { + ({ content, messageId, plugin, pluginState, toolCallId }) => { + const Render = getBuiltinRender(plugin?.identifier, plugin?.apiName); + + if (!Render) return null; + return ( - ); diff --git a/src/features/PluginDevModal/MCPManifestForm/utils.ts b/src/features/PluginDevModal/MCPManifestForm/utils.ts index f6554db18e..1e25631568 100644 --- a/src/features/PluginDevModal/MCPManifestForm/utils.ts +++ b/src/features/PluginDevModal/MCPManifestForm/utils.ts @@ -1,4 +1,4 @@ -import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; +import { type ToolManifest } from '@lobechat/types'; import { safeParseJSON } from '@/utils/safeParseJSON'; @@ -14,7 +14,7 @@ interface McpServers { } interface ParsedMcpInput { - manifest?: LobeChatPluginManifest; + manifest?: ToolManifest; mcpServers?: McpServers; } diff --git a/src/features/PluginDevModal/PluginPreview/index.tsx b/src/features/PluginDevModal/PluginPreview/index.tsx index bfad7f15a4..25a80bc64b 100644 --- a/src/features/PluginDevModal/PluginPreview/index.tsx +++ b/src/features/PluginDevModal/PluginPreview/index.tsx @@ -1,4 +1,4 @@ -import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; +import { type ToolManifest } from '@lobechat/types'; import { Block, Button, Flexbox, Icon, Text } from '@lobehub/ui'; import { type FormInstance } from 'antd'; import { Form as AForm } from 'antd'; @@ -17,7 +17,7 @@ import PluginEmptyState from './EmptyState'; const PluginPreview = memo<{ form: FormInstance }>(({ form }) => { const { t } = useTranslation('plugin'); - const manifest: LobeChatPluginManifest = AForm.useWatch(['manifest'], form); + const manifest: ToolManifest = AForm.useWatch(['manifest'], form); const meta = manifest?.meta; if (!manifest) diff --git a/src/features/PluginDevModal/UrlManifestForm.tsx b/src/features/PluginDevModal/UrlManifestForm.tsx index 421b1ba08d..2395a66284 100644 --- a/src/features/PluginDevModal/UrlManifestForm.tsx +++ b/src/features/PluginDevModal/UrlManifestForm.tsx @@ -1,5 +1,5 @@ import { BRANDING_NAME } from '@lobechat/business-const'; -import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk'; +import { type ToolManifest } from '@lobechat/types'; import { ActionIcon, Checkbox, Flexbox, FormItem, Input } from '@lobehub/ui'; import { type FormInstance } from 'antd'; import { Form } from 'antd'; @@ -39,7 +39,7 @@ const UrlManifestForm = memo<{ form: FormInstance; isEditMode: boolean }>( ({ form, isEditMode }) => { const { t } = useTranslation('plugin'); - const [manifest, setManifest] = useState(); + const [manifest, setManifest] = useState(); const urlKey = ['customParams', 'manifestUrl']; const proxyKey = ['customParams', 'useProxy']; diff --git a/src/features/PluginSettings/index.tsx b/src/features/PluginSettings/index.tsx index bbb2975a52..d1b04278c4 100644 --- a/src/features/PluginSettings/index.tsx +++ b/src/features/PluginSettings/index.tsx @@ -1,4 +1,4 @@ -import { type PluginSchema } from '@lobehub/chat-plugin-sdk'; +import { type ToolManifestSettings } from '@lobechat/types'; import { Form, Markdown } from '@lobehub/ui'; import { Form as AForm } from 'antd'; import { createStaticStyles } from 'antd-style'; @@ -10,7 +10,7 @@ import { pluginSelectors } from '@/store/tool/selectors'; import ItemRender from '../../components/JSONSchemaConfig/ItemRender'; -export const transformPluginSettings = (pluginSettings: PluginSchema) => { +export const transformPluginSettings = (pluginSettings: ToolManifestSettings) => { if (!pluginSettings?.properties) return []; return Object.entries(pluginSettings.properties).map(([name, i]) => ({ @@ -28,7 +28,7 @@ export const transformPluginSettings = (pluginSettings: PluginSchema) => { interface PluginSettingsConfigProps { id: string; - schema: PluginSchema; + schema: ToolManifestSettings; } const styles = createStaticStyles(({ css, cssVar }) => ({ diff --git a/src/features/PluginsUI/Render/BuiltinType/index.test.tsx b/src/features/PluginsUI/Render/BuiltinType/index.test.tsx deleted file mode 100644 index ee82cf3d42..0000000000 --- a/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; - -import BuiltinType from './index'; - -// Mock renders module -const mockWebBrowsingRender = vi.fn(({ content }) =>
WebBrowsingRender: {content}
); -const mockCodeInterpreterRender = vi.fn(({ content }) => ( -
CodeInterpreterRender: {content}
-)); - -vi.mock('@lobechat/builtin-tools/renders', () => ({ - getBuiltinRender: vi.fn((identifier, apiName) => { - if (identifier === 'lobe-web-browsing') return mockWebBrowsingRender; - if (identifier === 'lobe-code-interpreter') return mockCodeInterpreterRender; - return undefined; - }), -})); - -// Mock useParseContent hook -vi.mock('../useParseContent', () => ({ - useParseContent: vi.fn((content) => ({ data: content })), -})); - -describe('BuiltinType', () => { - it('should not render anything if identifier is not provided', () => { - const { container } = render(); - expect(container).toBeEmptyDOMElement(); - }); - - it('should not render anything if identifier is unknown', () => { - const { container } = render(); - expect(container).toBeEmptyDOMElement(); - }); - - it('should render the correct renderer for web browsing', () => { - const content = '{"query":"test"}'; - render(); - expect(screen.getByText(`WebBrowsingRender: ${content}`)).toBeInTheDocument(); - }); - - it('should render the correct renderer for code interpreter', () => { - const content = '{"code":"print(1)"}'; - render(); - expect(screen.getByText(`CodeInterpreterRender: ${content}`)).toBeInTheDocument(); - }); - - it('should pass correct props to renderer', () => { - const content = '{"test":"data"}'; - const args = '{"arg":"value"}'; - const pluginState = { state: 'value' }; - const pluginError = { error: 'test' }; - - render( - , - ); - - expect(screen.getByText(`WebBrowsingRender: ${content}`)).toBeInTheDocument(); - }); -}); diff --git a/src/features/PluginsUI/Render/BuiltinType/index.tsx b/src/features/PluginsUI/Render/BuiltinType/index.tsx deleted file mode 100644 index 8c68919bc3..0000000000 --- a/src/features/PluginsUI/Render/BuiltinType/index.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { getBuiltinRender } from '@lobechat/builtin-tools/renders'; -import { ToolRenderProvider } from '@lobechat/shared-tool-ui'; -import { safeParseJSON } from '@lobechat/utils'; -import { memo } from 'react'; - -import { useParseContent } from '../useParseContent'; -import { useToolRenderCaps } from './useToolRenderCaps'; - -export interface BuiltinTypeProps { - apiName?: string; - arguments?: string; - content: string; - identifier?: string; - loading?: boolean; - /** - * The real message ID (tool message ID) - */ - messageId?: string; - pluginError?: any; - pluginState?: any; - /** - * The tool call ID from the assistant message - */ - toolCallId?: string; -} - -const BuiltinType = memo( - ({ - content, - arguments: argumentsStr = '', - pluginState, - toolCallId, - messageId, - identifier, - pluginError, - apiName, - }) => { - const { data } = useParseContent(content); - const caps = useToolRenderCaps(); - - const Render = getBuiltinRender(identifier, apiName); - - if (!Render) return; - - const args = safeParseJSON(argumentsStr); - - return ( - - - - ); - }, -); - -export default BuiltinType; diff --git a/src/features/PluginsUI/Render/DefaultType/IFrameRender/index.tsx b/src/features/PluginsUI/Render/DefaultType/IFrameRender/index.tsx deleted file mode 100644 index 17650e5ce5..0000000000 --- a/src/features/PluginsUI/Render/DefaultType/IFrameRender/index.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { type PluginRenderProps } from '@lobehub/chat-plugin-sdk/client'; -import { Skeleton } from '@lobehub/ui'; -import { memo, useRef, useState } from 'react'; - -import { useOnPluginReadyForInteraction } from '../../utils/iframeOnReady'; -import { useOnPluginFetchMessage } from '../../utils/listenToPlugin'; -import { sendMessageContentToPlugin } from '../../utils/postMessage'; - -interface IFrameRenderProps extends PluginRenderProps { - height?: number; - url: string; - width?: number; -} - -const IFrameRender = memo(({ url, width = 800, height = 300, ...props }) => { - const iframeRef = useRef(null); - const [loading, setLoading] = useState(true); - - // When props change, proactively send data to the iframe - useOnPluginReadyForInteraction(() => { - const iframeWin = iframeRef.current?.contentWindow; - - if (iframeWin) { - sendMessageContentToPlugin(iframeWin, props); - } - }, [props]); - - // when get iframe fetch message ,send message content - useOnPluginFetchMessage(() => { - const iframeWin = iframeRef.current?.contentWindow; - if (iframeWin) { - sendMessageContentToPlugin(iframeWin, props); - } - }, [props]); - - return ( - <> - {loading && } -