From cea4316b7f2ba1bec23f6ebd3ab55e5805a3e50a Mon Sep 17 00:00:00 2001 From: sunls24 Date: Tue, 7 Nov 2023 14:12:45 +0800 Subject: [PATCH] Add imageGeneration plugin --- app/api/chat/functions.ts | 47 +++++++++++++++++++++++++++++++--- app/api/chat/route.ts | 4 +-- components/dialog/settings.tsx | 36 +++++++++++++++++++------- lib/constants.ts | 6 ++--- lib/store/config.ts | 19 +++++++++++--- package.json | 4 +-- pnpm-lock.yaml | 8 +++--- 7 files changed, 97 insertions(+), 27 deletions(-) diff --git a/app/api/chat/functions.ts b/app/api/chat/functions.ts index b7bd0d5..9ec46c2 100644 --- a/app/api/chat/functions.ts +++ b/app/api/chat/functions.ts @@ -1,5 +1,8 @@ import { ChatCompletionCreateParams } from "openai/resources/chat/completions"; import { NodeHtmlMarkdown } from "node-html-markdown"; +import OpenAI from "openai"; + +export const openai = new OpenAI({ apiKey: "" }); interface FunctionCall { function: ChatCompletionCreateParams.Function; @@ -38,7 +41,7 @@ const functionMap: Record = { const result = await res.json(); return result.items ?? nothing; } catch (err: any) { - console.log(`- ${name} ${args.keyword} ${err.cause ?? err}`); + console.log(`- ${err.cause ?? err} [${name} ${args.keyword}]`); return nothing; } }, @@ -72,7 +75,7 @@ const functionMap: Record = { } return result; } catch (err: any) { - console.log(`- ${name} ${url} ${err.cause ?? err}`); + console.log(`- ${err.cause ?? err} [${name} ${url}]`); return "Unable to access this website"; } }, @@ -124,11 +127,49 @@ const functionMap: Record = { } return result; } catch (err: any) { - console.log(`- ${name} ${args.location} ${err.cause ?? err}`); + console.log(`- ${err.cause ?? err} [${name} ${args.location}]`); return nothing; } }, }, + imageGeneration: { + function: { + name: "imageGeneration", + description: + "Generate image by prompt. Returns the url of the image, please use md format to display image", + parameters: { + type: "object", + properties: { + prompt: { + type: "string", + }, + style: { + type: "string", + description: + "The style of the generated images. Must be one of vivid or natural", + }, + }, + required: ["prompt"], + }, + }, + call: async (name, args) => { + const prompt = args.prompt as string; + try { + const response = await openai.images.generate({ + prompt: prompt, + // @ts-ignore + style: args.style, + model: "dall-e-3", + size: "1024x1024", + n: 1, + }); + return response.data[0].url; + } catch (err: any) { + console.log(`- ${err.cause ?? err} [${name} ${prompt}]`); + return "Generate failure"; + } + }, + }, }; export const functions: ChatCompletionCreateParams.Function[] = Object.values( diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index 5bb8a67..3ecfb90 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -3,14 +3,13 @@ import { ChatCompletionCreateParams } from "openai/resources/chat/completions"; import { CreateMessage, OpenAIStream, StreamingTextResponse } from "ai"; import { NextResponse } from "next/server"; import { ApiConfig, Plugins } from "@/lib/store/config"; -import { functions, onFunctionCall } from "@/app/api/chat/functions"; +import { functions, onFunctionCall, openai } from "@/app/api/chat/functions"; import { getLocaleTime } from "@/lib/utils"; import { apiKeyPool } from "@/lib/pool"; export const runtime = "edge"; apiKeyPool.update(process.env.OPENAI_API_KEY ?? ""); -const openai = new OpenAI({ apiKey: "" }); export async function POST(req: Request) { const { messages, config, contextIndex } = await req.json(); @@ -32,6 +31,7 @@ export async function POST(req: Request) { openai.apiKey = await apiKeyPool.getNextEdge(apiConfig.apiKey); try { const response = await openai.chat.completions.create(body); + // @ts-ignore const stream = OpenAIStream(response, { experimental_onFunctionCall: async ( { name, arguments: args }, diff --git a/components/dialog/settings.tsx b/components/dialog/settings.tsx index 674db4b..2ce5f44 100644 --- a/components/dialog/settings.tsx +++ b/components/dialog/settings.tsx @@ -92,6 +92,17 @@ function Settings({ trigger }: { trigger: React.ReactNode }) { updateConfig((c) => (c.apiConfig.plugins.weatherInfo.amapKey = amapKey)); } + const [imageGeneration, setImageGeneration] = useState( + useConfig((state) => state.apiConfig.plugins.imageGeneration), + ); + + function onIGToggle(enabled: boolean) { + setImageGeneration({ ...imageGeneration, enabled }); + updateConfig( + (c) => (c.apiConfig.plugins.imageGeneration.enabled = enabled), + ); + } + return ( {trigger} @@ -102,7 +113,7 @@ function Settings({ trigger }: { trigger: React.ReactNode }) {
- +
- +
@@ -125,7 +136,7 @@ function Settings({ trigger }: { trigger: React.ReactNode }) { {searchEnabled && ( <>
- +
- + )} -
- + + +
+ +
+
- + } content="避免触发 Token 限制" @@ -179,7 +197,7 @@ function Settings({ trigger }: { trigger: React.ReactNode }) { )}
- +
- + ()( { name: Store.Config, version: StoreVersion, - // migrate(persistedState, version) { - // const state = persistedState as Config - // return state - // }, + migrate(persistedState, version) { + const state = persistedState as Config; + if (!state.apiConfig.plugins.imageGeneration) { + state.apiConfig.plugins.imageGeneration = + defaultConfig.apiConfig.plugins.imageGeneration; + } + return state; + }, }, ), ); diff --git a/package.json b/package.json index 86ab400..99314b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "chat-next", - "version": "2.0.2", + "version": "2.1.0", "private": true, "scripts": { "dev": "next dev", @@ -28,7 +28,7 @@ "next": "^14.0.1", "next-themes": "^0.2.1", "node-html-markdown": "^1.3.0", - "openai": "^4.15.4", + "openai": "^4.16.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6907a46..da4dfdb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -63,8 +63,8 @@ dependencies: specifier: ^1.3.0 version: 1.3.0 openai: - specifier: ^4.15.4 - version: 4.15.4 + specifier: ^4.16.1 + version: 4.16.1 react: specifier: ^18.2.0 version: 18.2.0 @@ -3835,8 +3835,8 @@ packages: wrappy: 1.0.2 dev: true - /openai@4.15.4: - resolution: {integrity: sha512-EnlSl1p8n7Q/HnBf4+VOEcYloBKKe23sKOFfH/WJcw+XVyWav4lwDK4wCmsUY1wS4RFOdbA2EwBUB2p5WEPmoQ==} + /openai@4.16.1: + resolution: {integrity: sha512-Gr+uqUN1ICSk6VhrX64E+zL7skjI1TgPr/XUN+ZQuNLLOvx15+XZulx/lSW4wFEAQzgjBDlMBbBeikguGIjiMg==} hasBin: true dependencies: '@types/node': 18.18.8