From e7d169b73792823d1e90839cbec089d16071ead7 Mon Sep 17 00:00:00 2001 From: summerscar Date: Thu, 31 Oct 2024 17:04:07 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(ai):=20add=20gemini=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 21 +++++++++++++++++++ app/components/home-status/index.tsx | 18 +++++++++++++--- app/styles/_variables.css | 2 +- biome.json | 2 +- package-lock.json | 10 +++++++++ package.json | 5 ++++- scripts/open-ai.ts | 31 ++++++++++++++++++++++------ 7 files changed, 77 insertions(+), 12 deletions(-) diff --git a/.env.example b/.env.example index 9d8bfd5..2087419 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,24 @@ +# Authjs AUTH_SECRET="" +# Google Analytics NEXT_PUBLIC_GA_ID="" + +# NextAuth +NEXTAUTH_URL="" + +# Database +POSTGRES_DATABASE="verceldb" +POSTGRES_HOST="" +POSTGRES_PASSWORD="" +POSTGRES_PRISMA_URL="" +POSTGRES_URL="" +POSTGRES_URL_NON_POOLING="" +POSTGRES_URL_NO_SSL="" +POSTGRES_USER="" + +# AI +AI="" +GPT_KEY="" +GEMINI_KEY="" +GPT_URL="" diff --git a/app/components/home-status/index.tsx b/app/components/home-status/index.tsx index b6b0783..8cf00e8 100644 --- a/app/components/home-status/index.tsx +++ b/app/components/home-status/index.tsx @@ -145,7 +145,14 @@ const HomeStatus = ({ focusInput(); return; } - + if (e.code === "Quote") { + onSettingChange({ showMeaning: !setting.showMeaning }); + return; + } + if (e.code === "Semicolon") { + playWord(); + return; + } /** 单词导航 */ if ([NextKeyShortcut, PrevKeyShortcut].includes(e.code)) { if (isComplete || (curWordIndex === 0 && e.code === PrevKeyShortcut)) @@ -336,7 +343,8 @@ const HomeStatus = ({ clsx({ "font-bold text-[color:var(--font-color-error)]": isInputError && curInputIndex === strIndex, - "text-[color:var(--font-color-active)]": curInputIndex > strIndex, + "text-[color:var(--font-color-active)] font-bold": + curInputIndex > strIndex, }); const inlineStyle = useMemo(() => { @@ -484,7 +492,11 @@ const HomeStatus = ({
tips: Try [ {" / "} - ]. + ] + {" / "} + ; + {" / "} + '.
diff --git a/app/styles/_variables.css b/app/styles/_variables.css index 448a18f..834b6b7 100644 --- a/app/styles/_variables.css +++ b/app/styles/_variables.css @@ -1,6 +1,6 @@ :root { - --font-color-inactive: theme(textColor.gray.500); + --font-color-inactive: theme(textColor.gray.500/0.8); --font-color-active: theme(textColor.base-content); --font-color-error: theme(textColor.rose.500); --header-height: 64px; diff --git a/biome.json b/biome.json index b2a4e2f..b3bf6e2 100644 --- a/biome.json +++ b/biome.json @@ -39,7 +39,7 @@ "allowComments": true }, "formatter": { - "lineWidth": 75 + "lineWidth": 48 } }, "vcs": { diff --git a/package-lock.json b/package-lock.json index 1e108cf..cfefe83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "hasInstallScript": true, "dependencies": { + "@google/generative-ai": "^0.21.0", "@keystone-6/auth": "^8.0.0", "@keystone-6/core": "^6.2.0", "@keystone-6/document-renderer": "^1.1.2", @@ -3940,6 +3941,15 @@ "tslib": "^2.7.0" } }, + "node_modules/@google/generative-ai": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.21.0.tgz", + "integrity": "sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@graphql-tools/executor": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.2.8.tgz", diff --git a/package.json b/package.json index 3bcef14..1871b11 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "script:docs-modified": "tsx scripts/generate-doc-last-modified.ts" }, "dependencies": { + "@google/generative-ai": "^0.21.0", "@keystone-6/auth": "^8.0.0", "@keystone-6/core": "^6.2.0", "@keystone-6/document-renderer": "^1.1.2", @@ -120,7 +121,9 @@ "*": [ "biome check --no-errors-on-unmatched --files-ignore-unknown=true" ], - "*.{mdx,md}": ["npm run script:docs-modified"] + "*.{mdx,md}": [ + "npm run script:docs-modified" + ] }, "volta": { "node": "20.17.0" diff --git a/scripts/open-ai.ts b/scripts/open-ai.ts index 1bf6327..986b3e9 100644 --- a/scripts/open-ai.ts +++ b/scripts/open-ai.ts @@ -1,6 +1,8 @@ +import { GoogleGenerativeAI } from "@google/generative-ai"; import { config as envConfig } from "dotenv"; import OpenAI from "openai"; import type { ChatCompletionMessageParam } from "openai/resources/index.mjs"; + envConfig({ path: ["./.env", "./.env.local"] }); const openai = new OpenAI({ @@ -8,13 +10,30 @@ const openai = new OpenAI({ baseURL: process.env.GPT_URL, }); +const gemini_AI = new GoogleGenerativeAI(process.env.GEMINI_KEY || ""); + async function fetchChatCompletion(messages: ChatCompletionMessageParam[]) { - const result = await openai.chat.completions.create({ - model: "gpt-3.5-turbo", - messages, - }); - console.log(`[chatGPT]: use ${result.usage?.total_tokens} tokens.`); - return result.choices[0].message.content; + if (!process.env.AI || process.env.AI === "openai") { + const result = await openai.chat.completions.create({ + model: "gpt-3.5-turbo", + messages, + }); + console.log(`[AI][OpenAI]: use ${result.usage?.total_tokens} tokens.`); + return result.choices[0].message.content; + } + + if (process.env.AI === "gemini") { + const geminiModel = gemini_AI.getGenerativeModel({ + model: "gemini-1.5-flash", + }); + const result = await geminiModel.generateContent( + messages[0].content as string, + ); + console.log( + `[AI][Gemini]: use ${result.response.usageMetadata?.totalTokenCount} tokens.`, + ); + return result.response.text(); + } } export { fetchChatCompletion };