From 4f034ddacf41635247e4ebd4cb115724770324cd Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Wed, 16 Aug 2023 23:55:39 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20=20Refactor:=20=E5=89=A5?= =?UTF-8?q?=E7=A6=BB=E6=8F=92=E4=BB=B6=E6=9C=8D=E5=8A=A1=E7=AB=AF=E8=B7=AF?= =?UTF-8?q?=E7=94=B1=EF=BC=8C=E4=B8=BA=E5=AE=9E=E7=8E=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=9A=84=E6=8F=92=E4=BB=B6=E8=B0=83=E7=94=A8=E5=81=9A=E5=87=86?= =?UTF-8?q?=E5=A4=87=20(#82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 refactor: remove /api/plugins function * ♻️ refactor: refactor the plugin implement to separate the server runner --- next.config.mjs | 9 ++++-- package.json | 2 -- src/pages/api/plugins.api.ts | 22 -------------- src/plugins/searchEngine/index.ts | 6 +--- src/plugins/searchEngine/runner.ts | 41 -------------------------- src/plugins/type.ts | 18 ++---------- src/plugins/weather/index.ts | 6 +--- src/plugins/weather/runner.ts | 35 ----------------------- src/plugins/webCrawler/index.ts | 5 +--- src/plugins/webCrawler/runner.ts | 46 ------------------------------ src/services/url.ts | 2 +- 11 files changed, 13 insertions(+), 179 deletions(-) delete mode 100644 src/pages/api/plugins.api.ts delete mode 100644 src/plugins/searchEngine/runner.ts delete mode 100644 src/plugins/weather/runner.ts delete mode 100644 src/plugins/webCrawler/runner.ts diff --git a/next.config.mjs b/next.config.mjs index 6aece5e24ba7..2e33e837942b 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,6 +3,10 @@ import nextPWA from 'next-pwa'; const isProd = process.env.NODE_ENV === 'production'; const API_END_PORT_URL = process.env.API_END_PORT_URL || ''; +// chat plugin market +const PLUGIN_RUNNER_BASE_URL = + process.env.PLUGIN_RUNNER_BASE_URL || 'https://lobe-chat-plugin-market.vercel.app'; + const withPWA = nextPWA({ dest: 'public', register: true, @@ -31,8 +35,9 @@ const nextConfig = { destination: `${API_END_PORT_URL}/api/openai`, }, { - source: '/api/plugins-dev', - destination: `${API_END_PORT_URL}/api/plugins`, + source: '/api/plugins', + // refs to: https://github.com/lobehub/chat-plugin-market + destination: `${PLUGIN_RUNNER_BASE_URL}/api/v1/runner`, }, ]; }, diff --git a/package.json b/package.json index 957ce4c3767d..399e4114d89b 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "dependencies": { "@ant-design/colors": "^7", "@ant-design/icons": "^5", - "@commitlint/cli": "^17", "@emoji-mart/data": "^1", "@emoji-mart/react": "^1", "@icons-pack/react-simple-icons": "^8", @@ -86,7 +85,6 @@ "next": "13.4.7", "openai-edge": "^1", "polished": "^4", - "query-string": "^8", "react": "^18", "react-dom": "^18", "react-hotkeys-hook": "^4", diff --git a/src/pages/api/plugins.api.ts b/src/pages/api/plugins.api.ts deleted file mode 100644 index d10fa4d8b1fe..000000000000 --- a/src/pages/api/plugins.api.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { OpenAIPluginPayload } from '@/types/plugin'; - -import { PluginsMap } from '../../plugins'; - -export const runtime = 'edge'; - -export default async function handler(req: Request) { - const { name, arguments: args } = (await req.json()) as OpenAIPluginPayload; - - console.log(`检测到 functionCall: ${name}`); - - const func = PluginsMap[name]; - - if (func) { - const data = JSON.parse(args); - const result = await func.runner(data); - - console.log(`[${name}]`, args, `result:`, JSON.stringify(result, null, 2).slice(0, 3600)); - - return new Response(JSON.stringify(result)); - } -} diff --git a/src/plugins/searchEngine/index.ts b/src/plugins/searchEngine/index.ts index 5a7f2979a959..87d8843fa57c 100644 --- a/src/plugins/searchEngine/index.ts +++ b/src/plugins/searchEngine/index.ts @@ -2,9 +2,6 @@ import { ChatCompletionFunctions } from 'openai-edge/types/api'; import { PluginItem } from '@/plugins/type'; -import runner from './runner'; -import { Result } from './type'; - const schema: ChatCompletionFunctions = { description: '查询搜索引擎获取信息', name: 'searchEngine', @@ -20,10 +17,9 @@ const schema: ChatCompletionFunctions = { }, }; -const searchEngine: PluginItem = { +const searchEngine: PluginItem = { avatar: '🔍', name: 'searchEngine', - runner, schema, }; diff --git a/src/plugins/searchEngine/runner.ts b/src/plugins/searchEngine/runner.ts deleted file mode 100644 index eb2a23105787..000000000000 --- a/src/plugins/searchEngine/runner.ts +++ /dev/null @@ -1,41 +0,0 @@ -import querystring from 'query-string'; - -import { PluginRunner } from '@/plugins/type'; - -import { OrganicResults, Result } from './type'; - -const BASE_URL = 'https://serpapi.com/search'; - -const API_KEY = process.env.SERPAI_API_KEY; - -const fetchResult: PluginRunner<{ keywords: string }, Result> = async ({ keywords }) => { - const params = { - api_key: API_KEY, - engine: 'google', - gl: 'cn', - google_domain: 'google.com', - hl: 'zh-cn', - location: 'China', - q: keywords, - }; - - const query = querystring.stringify(params); - - const res = await fetch(`${BASE_URL}?${query}`); - - const data = await res.json(); - - const results = data.organic_results as OrganicResults; - - return results.map((r) => ({ - content: r.snippet, - date: r.date, - displayed_link: r.displayed_link, - favicon: r.favicon, - link: r.link, - source: r.source, - title: r.title, - })); -}; - -export default fetchResult; diff --git a/src/plugins/type.ts b/src/plugins/type.ts index 375b0f86cce5..f96747d561bc 100644 --- a/src/plugins/type.ts +++ b/src/plugins/type.ts @@ -6,7 +6,7 @@ import { ReactNode } from 'react'; * @template Result - 结果类型,默认为 any * @template RunnerParams - 运行参数类型,默认为 any */ -export interface PluginItem { +export interface PluginItem { /** * 头像 */ @@ -15,12 +15,7 @@ export interface PluginItem { * 名称 */ name: string; - /** - * 运行器 - * @param params - 运行参数 - * @returns 运行结果的 Promise - */ - runner: PluginRunner; + render?: PluginRender; /** * 聊天完成函数的模式 */ @@ -34,15 +29,6 @@ export interface PluginItem { */ export type PluginRender = (props: PluginRenderProps) => ReactNode; -/** - * 插件运行器 - * @template Params - 参数类型,默认为 object - * @template Result - 结果类型,默认为 any - * @param params - 运行参数 - * @returns 运行结果的 Promise - */ -export type PluginRunner = (params: Params) => Promise; - /** * 插件渲染属性 * @template Result - 结果类型,默认为 any diff --git a/src/plugins/weather/index.ts b/src/plugins/weather/index.ts index 1098725eceef..a8164f9ca63d 100644 --- a/src/plugins/weather/index.ts +++ b/src/plugins/weather/index.ts @@ -1,7 +1,4 @@ import { PluginItem } from '@/plugins/type'; -import { WeatherResult } from '@/plugins/weather/type'; - -import runner from './runner'; const schema = { description: '获取当前天气情况', @@ -18,10 +15,9 @@ const schema = { }, }; -const getWeather: PluginItem = { +const getWeather: PluginItem = { avatar: '☂️', name: 'realtimeWeather', - runner, schema, }; diff --git a/src/plugins/weather/runner.ts b/src/plugins/weather/runner.ts deleted file mode 100644 index eea9c82993f2..000000000000 --- a/src/plugins/weather/runner.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { PluginRunner } from '@/plugins/type'; - -import { Response, WeatherParams, WeatherResult } from './type'; - -const weatherBaseURL = 'https://restapi.amap.com/v3/weather/weatherInfo'; - -const citySearchURL = 'https://restapi.amap.com/v3/config/district'; - -const KEY = process.env.GAODE_WEATHER_KEY; - -const fetchCityCode = async (keywords: string): Promise => { - const URL = `${citySearchURL}?keywords=${keywords}&subdistrict=0&extensions=base&key=${KEY}`; - const res = await fetch(URL); - - const data = await res.json(); - console.log(data); - - return data.districts[0].adcode; -}; - -const fetchWeather: PluginRunner = async ({ - city, - extensions = 'all', -}) => { - const cityCode = await fetchCityCode(city); - - const URL = `${weatherBaseURL}?city=${cityCode}&extensions=${extensions}&key=${KEY}`; - const res = await fetch(URL); - - const data: Response = await res.json(); - - return data.forecasts; -}; - -export default fetchWeather; diff --git a/src/plugins/webCrawler/index.ts b/src/plugins/webCrawler/index.ts index 02024f68619a..df0bd0f031de 100644 --- a/src/plugins/webCrawler/index.ts +++ b/src/plugins/webCrawler/index.ts @@ -1,8 +1,5 @@ import { PluginItem } from '@/plugins/type'; -import runner from './runner'; -import { Result } from './type'; - const schema = { description: '提取网页内容并总结', name: 'websiteCrawler', @@ -18,6 +15,6 @@ const schema = { }, }; -const getWeather: PluginItem = { avatar: '🕸', name: 'websiteCrawler', runner, schema }; +const getWeather: PluginItem = { avatar: '🕸', name: 'websiteCrawler', schema }; export default getWeather; diff --git a/src/plugins/webCrawler/runner.ts b/src/plugins/webCrawler/runner.ts deleted file mode 100644 index 25bbbabaf995..000000000000 --- a/src/plugins/webCrawler/runner.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { PluginRunner } from '@/plugins/type'; - -import { ParserResponse, Result } from './type'; - -const BASE_URL = process.env.BROWSERLESS_URL ?? 'https://chrome.browserless.io'; -const BROWSERLESS_TOKEN = process.env.BROWSERLESS_TOKEN; - -// service from: https://github.com/lobehub/html-parser/tree/master -const HTML_PARSER_URL = process.env.HTML_PARSER_URL; - -const runner: PluginRunner<{ url: string }, Result> = async ({ url }) => { - const input = { - gotoOptions: { waitUntil: 'networkidle2' }, - url, - }; - - try { - const res = await fetch(`${BASE_URL}/content?token=${BROWSERLESS_TOKEN}`, { - body: JSON.stringify(input), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }); - const html = await res.text(); - - const parserBody = { html, url }; - - const parseRes = await fetch(`${HTML_PARSER_URL}`, { - body: JSON.stringify(parserBody), - headers: { - 'Content-Type': 'application/json', - }, - method: 'POST', - }); - - const { title, textContent, siteName } = (await parseRes.json()) as ParserResponse; - - return { content: textContent, title, url, website: siteName }; - } catch (error) { - console.error(error); - return { content: '抓取失败', errorMessage: (error as any).message, url }; - } -}; - -export default runner; diff --git a/src/services/url.ts b/src/services/url.ts index f18b774bd92c..b813c1359882 100644 --- a/src/services/url.ts +++ b/src/services/url.ts @@ -4,5 +4,5 @@ const prefix = isDev ? '-dev' : ''; export const URLS = { openai: '/api/openai' + prefix, - plugins: '/api/plugins' + prefix, + plugins: '/api/plugins', };