diff --git a/.github/workflows/auto-rebase.yml b/.github/workflows/auto-rebase.yml new file mode 100644 index 000000000000..5e8652ed4d37 --- /dev/null +++ b/.github/workflows/auto-rebase.yml @@ -0,0 +1,38 @@ +name: Auto Rebase + +on: + schedule: + - cron: '0 * * * *' # This will run every hour + push: + branches: + - main # Change this to the branch you want to watch for updates + workflow_dispatch: # Allows manual triggering of the action + +permissions: + id-token: write + contents: write + +jobs: + rebase: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 # Fetch all history for all branches and tags + + - name: Add upstream remote + run: git remote add upstream https://github.com/comfyanonymous/ComfyUI.git + + - name: Fetch upstream changes + run: git fetch upstream + + - name: Rebase branch + run: | + git config --global user.email jackalcooper@gmail.com + git config --global user.name tsai + git checkout master + git rebase upstream/master + + - name: Push changes + run: git push origin master --force diff --git a/README.md b/README.md index bf7c3059413b..c7b779d4a026 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** - I18n: English, 简体中文, 繁体中文, 日本語, Français, Español, Italiano, Türkçe, Deutsch, Tiếng Việt, Русский, Čeština, 한국어, Indonesia
- + ![主界面](./docs/images/cover.png)
@@ -101,7 +101,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** ## What's New - 🚀 v2.15.0 Now supports Plugins! Read this: [NextChat-Awesome-Plugins](https://github.com/ChatGPTNextWeb/NextChat-Awesome-Plugins) -- 🚀 v2.14.0 Now supports Artifacts & SD +- 🚀 v2.14.0 Now supports Artifacts & SD - 🚀 v2.10.1 support Google Gemini Pro model. - 🚀 v2.9.11 you can use azure endpoint now. - 🚀 v2.8 now we have a client that runs across all platforms! @@ -152,7 +152,7 @@ For enterprise inquiries, please contact: **business@nextchat.dev** 1. Get [OpenAI API Key](https://platform.openai.com/account/api-keys); 2. Click - [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web), remember that `CODE` is your page password; + [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fsiliconflow%2FChatGPT-Next-Web&env=NEXT_PUBLIC_SF_NEXT_CHAT_CLIENT_ID&env=SF_NEXT_CHAT_SECRET&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web), remember that `CODE` is your page password; 3. Enjoy :) ## FAQ @@ -346,7 +346,7 @@ Change default model ### `WHITE_WEBDAV_ENDPOINTS` (optional) You can use this option if you want to increase the number of webdav service addresses you are allowed to access, as required by the format: -- Each address must be a complete endpoint +- Each address must be a complete endpoint > `https://xxxx/yyy` - Multiple addresses are connected by ', ' diff --git a/app/api/[provider]/[...path]/route.ts b/app/api/[provider]/[...path]/route.ts index dffb3e9daa4b..a52b0487a4b4 100644 --- a/app/api/[provider]/[...path]/route.ts +++ b/app/api/[provider]/[...path]/route.ts @@ -1,3 +1,4 @@ +import { handle as oauthHandler } from "../../oauth_callback"; import { ApiPath } from "@/app/constant"; import { NextRequest } from "next/server"; import { handle as openaiHandler } from "../../openai"; @@ -19,6 +20,8 @@ async function handle( const apiPath = `/api/${params.provider}`; console.log(`[${params.provider} Route] params `, params); switch (apiPath) { + case ApiPath.OAuth: + return oauthHandler(req, { params }); case ApiPath.Azure: return azureHandler(req, { params }); case ApiPath.Google: diff --git a/app/api/oauth_callback.ts b/app/api/oauth_callback.ts new file mode 100644 index 000000000000..fb07dbf8e554 --- /dev/null +++ b/app/api/oauth_callback.ts @@ -0,0 +1,70 @@ +import { NextRequest, NextResponse } from "next/server"; +import { redirect } from "next/navigation"; +import { cookies } from "next/headers"; + +export async function handle( + req: NextRequest, + { params }: { params: { path: string[] } }, +) { + console.log("[SF] params ", params); + + if (req.method === "OPTIONS") { + return NextResponse.json({ body: "OK" }, { status: 200 }); + } + const url = new URL(req.url); + const queryParams = new URLSearchParams(url.search); + const code = queryParams.get("code"); + let sfak = ""; + console.log("[SF] code ", code); + try { + const tokenFetch = await fetch( + `${ + process.env.NEXT_PUBLIC_SF_NEXT_CHAT_SF_ACCOUNT_ENDPOINT || + "https://account.siliconflow.cn" + }/oauth?client_id=${ + process.env.NEXT_PUBLIC_SF_NEXT_CHAT_CLIENT_ID + }/api/open/oauth`, + { + method: "POST", + body: JSON.stringify({ + clientId: process.env.NEXT_PUBLIC_SF_NEXT_CHAT_CLIENT_ID, + secret: process.env.SF_NEXT_CHAT_SECRET, + code, + }), + }, + ); + if (!tokenFetch.ok) + return Response.json( + { status: false, message: "fetch error" }, + { status: 500 }, + ); + const tokenJson = await tokenFetch.json(); + const access_token = tokenJson.status ? tokenJson.data?.access_token : null; + console.log("access_token", access_token); + // uat https://cloud-uat.siliconflow.cn + // prod https://cloud.siliconflow.cn + const apiKey = await fetch( + `${ + process.env.NEXT_PUBLIC_SF_NEXT_CHAT_SF_CLOUD_ENDPOINT || + "https://cloud.siliconflow.cn" + }/api/oauth/apikeys`, + { + method: "POST", + headers: { + Authorization: `token ${access_token}`, + }, + }, + ); + const apiKeysData = await apiKey.json(); + console.log("apiKeysData", apiKeysData); + sfak = apiKeysData.data[0].secretKey; + } catch (error) { + console.log("error", error); + return Response.json( + { status: false, message: "fetch error" }, + { status: 500 }, + ); + } + cookies().set("sfak", sfak); + redirect(`/`); +} diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 3d519dee722b..c829b8af7389 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -59,6 +59,7 @@ import { DEFAULT_TOPIC, ModelType, usePluginStore, + DEFAULT_CONFIG, } from "../store"; import { @@ -114,6 +115,7 @@ import { ExportMessageModal } from "./exporter"; import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; +import { useCookies } from "react-cookie"; const localStorage = safeLocalStorage(); import { ClientApi } from "../client/api"; @@ -1244,6 +1246,99 @@ function _Chat() { return session.mask.hideContext ? [] : session.mask.context.slice(); }, [session.mask.context, session.mask.hideContext]); + const [cookies, setCookie, removeCookie] = useCookies(["sfak"], { + doNotParse: true, + }); + + useEffect(() => { + const sfakValue = cookies.sfak; + + const model = "Qwen/Qwen2-7B-Instruct"; + config.update((config) => { + const ids = [ + "stabilityai/stable-diffusion-xl-base-1.0", + "TencentARC/PhotoMaker", + "InstantX/InstantID", + "stabilityai/stable-diffusion-2-1", + "stabilityai/sd-turbo", + "stabilityai/sdxl-turbo", + "ByteDance/SDXL-Lightning", + "deepseek-ai/deepseek-llm-67b-chat", + "Qwen/Qwen1.5-14B-Chat", + "Qwen/Qwen1.5-7B-Chat", + "Qwen/Qwen1.5-110B-Chat", + "Qwen/Qwen1.5-32B-Chat", + "01-ai/Yi-1.5-6B-Chat", + "01-ai/Yi-1.5-9B-Chat-16K", + "01-ai/Yi-1.5-34B-Chat-16K", + "THUDM/chatglm3-6b", + "deepseek-ai/DeepSeek-V2-Chat", + "THUDM/glm-4-9b-chat", + "Qwen/Qwen2-72B-Instruct", + "Qwen/Qwen2-7B-Instruct", + "Qwen/Qwen2-57B-A14B-Instruct", + "stabilityai/stable-diffusion-3-medium", + "deepseek-ai/DeepSeek-Coder-V2-Instruct", + "Qwen/Qwen2-1.5B-Instruct", + "internlm/internlm2_5-7b-chat", + "BAAI/bge-large-en-v1.5", + "BAAI/bge-large-zh-v1.5", + "Pro/Qwen/Qwen2-7B-Instruct", + "Pro/Qwen/Qwen2-1.5B-Instruct", + "Pro/Qwen/Qwen1.5-7B-Chat", + "Pro/THUDM/glm-4-9b-chat", + "Pro/THUDM/chatglm3-6b", + "Pro/01-ai/Yi-1.5-9B-Chat-16K", + "Pro/01-ai/Yi-1.5-6B-Chat", + "Pro/internlm/internlm2_5-7b-chat", + "black-forest-labs/FLUX.1-schnell", + "FunAudioLLM/SenseVoiceSmall", + "netease-youdao/bce-embedding-base_v1", + "BAAI/bge-m3", + "internlm/internlm2_5-20b-chat", + "Qwen/Qwen2-Math-72B-Instruct", + "netease-youdao/bce-reranker-base_v1", + "BAAI/bge-reranker-v2-m3", + "deepseek-ai/DeepSeek-V2.5", + "Ascend/Qwen/Qwen2-72B-Instruct", + ]; + config.customModels = ids + .sort() + .filter( + (id) => + !id.toLowerCase().includes("voice") && + !id.toLowerCase().includes("flux") && + !id.toLowerCase().includes("stability") && + !id.toLowerCase().includes("sdxl") && + !id.toLowerCase().includes("photomaker") && + !id.toLowerCase().includes("instantid") && + !id.toLowerCase().includes("bge") && + !id.toLowerCase().includes("bce"), + ) + .join(","); // Filter out unwanted ids; + config.modelConfig.model = model as ModelType; + config.modelConfig.providerName = model as ServiceProvider; + }); + if (sfakValue) { + chatStore.updateCurrentSession((session) => { + if ( + session.mask.modelConfig.model == DEFAULT_CONFIG.modelConfig.model + ) { + session.mask.modelConfig.model = model as ModelType; + session.mask.modelConfig.providerName = model as ServiceProvider; + } + }); + accessStore.update((access) => { + console.log("update access store with SF API key"); + access.useCustomConfig = true; + access.openaiApiKey = sfakValue; + access.openaiUrl = + process.env.NEXT_PUBLIC_SF_NEXT_CHAT_SF_API_ENDPOINT!; + }); + removeCookie("sfak"); + } + }, []); + if ( context.length === 0 && session.messages.at(0)?.content !== BOT_HELLO.content diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx index 493b1103beb6..0bc722c01a28 100644 --- a/app/components/sidebar.tsx +++ b/app/components/sidebar.tsx @@ -5,6 +5,8 @@ import styles from "./home.module.scss"; import { IconButton } from "./button"; import SettingsIcon from "../icons/settings.svg"; import GithubIcon from "../icons/github.svg"; +import SiliconFlowIcon from "../icons/sf.svg"; +import SiliconFlowActiveIcon from "../icons/sf.active.svg"; import ChatGptIcon from "../icons/chatgpt.svg"; import AddIcon from "../icons/add.svg"; import DeleteIcon from "../icons/delete.svg"; @@ -14,7 +16,12 @@ import DiscoveryIcon from "../icons/discovery.svg"; import Locale from "../locales"; -import { useAppConfig, useChatStore } from "../store"; +import { + DEFAULT_CONFIG, + useAccessStore, + useAppConfig, + useChatStore, +} from "../store"; import { DEFAULT_SIDEBAR_WIDTH, @@ -30,6 +37,7 @@ import { Link, useNavigate } from "react-router-dom"; import { isIOS, useMobileScreen } from "../utils"; import dynamic from "next/dynamic"; import { showConfirm, Selector } from "./ui-lib"; +import { useCookies } from "react-cookie"; const ChatList = dynamic(async () => (await import("./chat-list")).ChatList, { loading: () => null, @@ -216,6 +224,10 @@ export function SideBar(props: { className?: string }) { const navigate = useNavigate(); const config = useAppConfig(); const chatStore = useChatStore(); + const accessStore = useAccessStore(); + const [cookies, setCookie, removeCookie] = useCookies(["sfak"], { + doNotParse: true, + }); return ( +
+ { + if (accessStore.useCustomConfig && accessStore.openaiApiKey) { + const confirmLogout = window.confirm( + "Are you sure you want to log out?", + ); + if (confirmLogout) { + chatStore.updateCurrentSession((session) => { + session.mask.modelConfig.model = + DEFAULT_CONFIG.modelConfig.model; + session.mask.modelConfig.providerName = + DEFAULT_CONFIG.modelConfig.providerName; + accessStore.update((access) => { + removeCookie("sfak"); + access.openaiApiKey = ""; + access.useCustomConfig = false; + window.location.href = "/"; + }); + }); + } + } else { + window.location.href = `${ + process.env + .NEXT_PUBLIC_SF_NEXT_CHAT_SF_ACCOUNT_ENDPOINT || + "https://account.siliconflow.cn" + }/oauth?client_id=${ + process.env.NEXT_PUBLIC_SF_NEXT_CHAT_CLIENT_ID + }`; + } + }} + > + + ) : ( + + ) + } + shadow + /> + +
} secondaryAction={ diff --git a/app/constant.ts b/app/constant.ts index a54a973daa6b..659c881df222 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -60,6 +60,7 @@ export enum ApiPath { Iflytek = "/api/iflytek", Stability = "/api/stability", Artifacts = "/api/artifacts", + OAuth = "/api/oauth_callback", } export enum SlotID { @@ -230,7 +231,7 @@ You are ChatGPT, a large language model trained by {{ServiceProvider}}. Knowledge cutoff: {{cutoff}} Current model: {{model}} Current time: {{time}} -Latex inline: \\(x^2\\) +Latex inline: \\(x^2\\) Latex block: $$e=mc^2$$ `; diff --git a/app/icons/sf.active.svg b/app/icons/sf.active.svg new file mode 100644 index 000000000000..ff9146216514 --- /dev/null +++ b/app/icons/sf.active.svg @@ -0,0 +1,3 @@ + + SF + diff --git a/app/icons/sf.svg b/app/icons/sf.svg new file mode 100644 index 000000000000..1e658fd5f732 --- /dev/null +++ b/app/icons/sf.svg @@ -0,0 +1,3 @@ + + SF + diff --git a/app/masks/cn.ts b/app/masks/cn.ts index ed507d7344e2..279ff74669de 100644 --- a/app/masks/cn.ts +++ b/app/masks/cn.ts @@ -1,6 +1,226 @@ import { BuiltinMask } from "./typing"; +const mandarinExpertPrompt = `# 角色: +你是新汉语老师,你年轻,批判现实,思考深刻,语言风趣"。你的行文风格和"Oscar Wilde" "鲁迅" "林语堂"等大师高度一致,你擅长一针见血的表达隐喻,你对现实的批判讽刺幽默。 + +- 作者:云中江树,李继刚 +- 模型:阿里通义 + +## 任务: +将一个汉语词汇进行全新角度的解释,你会用一个特殊视角来解释一个词汇: +用一句话表达你的词汇解释,抓住用户输入词汇的本质,使用辛辣的讽刺、一针见血的指出本质,使用包含隐喻的金句。 +例如:“委婉”: "刺向他人时, 决定在剑刃上撒上止痛药。" + +## 输出结果: +1. 词汇解释 +2. 输出词语卡片(Html 代码) + - 整体设计合理使用留白,整体排版要有呼吸感 + - 设计原则:干净 简洁 纯色 典雅 + - 配色:下面的色系中随机选择一个[ + "柔和粉彩系", + "深邃宝石系", + "清新自然系", + "高雅灰度系", + "复古怀旧系", + "明亮活力系", + "冷淡极简系", + "海洋湖泊系", + "秋季丰收系", + "莫兰迪色系" + ] + - 卡片样式: + (字体 . ("KaiTi, SimKai" "Arial, sans-serif")) + (颜色 . ((背景 "#FAFAFA") (标题 "#333") (副标题 "#555") (正文 "#333"))) + (尺寸 . ((卡片宽度 "auto") (卡片高度 "auto, >宽度") (内边距 "20px"))) + (布局 . (竖版 弹性布局 居中对齐)))) + - 卡片元素: + (标题 "汉语新解") + (分隔线) + (词语 用户输入) + (拼音) + (英文翻译) + (日文翻译) + (解释:(按现代诗排版)) + +## 结果示例: +\`\`\` + + + + + + 汉语新解 - 金融杠杆 + + + + +
+
+

汉语新解

+
+
+
+
金融杠杆
+
Jīn Róng Gàng Gǎn
+
Financial Leverage
+
金融レバレッジ
+
+
+
+
+

+ 借鸡生蛋,
+ 只不过这蛋要是金的,
+ 鸡得赶紧卖了还债。 +

+
+
+
+
杠杆
+
+ + +\`\`\` +## 注意: +1. 分隔线与上下元素垂直间距相同,具有分割美学。 +2. 卡片(.card)不需要 padding ,允许子元素“汉语新解”的色块完全填充到边缘,具有设计感。 + +## 初始行为: +输出"说吧, 他们又用哪个词来忽悠你了?"`; export const CN_MASKS: BuiltinMask[] = [ + { + avatar: "1f004", + name: "汉语新解", + context: [ + { + id: "expert-0", + role: "user", + content: mandarinExpertPrompt, + date: "", + }, + ], + modelConfig: { + model: "deepseek-ai/DeepSeek-Coder-V2-Instruct", + temperature: 1, + max_tokens: 2000, + presence_penalty: 0, + frequency_penalty: 0, + sendMemory: true, + historyMessageCount: 4, + compressMessageLengthThreshold: 1000, + }, + lang: "cn", + builtin: true, + createdAt: 1688899480511, + }, { avatar: "1f5bc-fe0f", name: "以文搜图", diff --git a/package.json b/package.json index 8696f83b558f..1b517e57f51c 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "node-fetch": "^3.3.1", "openapi-client-axios": "^7.5.5", "react": "^18.2.0", + "react-cookie": "^7.2.0", "react-dom": "^18.2.0", "react-markdown": "^8.0.7", "react-router-dom": "^6.15.0", diff --git a/yarn.lock b/yarn.lock index 7e7dd3484f1f..0f77eadf10d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1629,6 +1629,11 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@types/d3-scale-chromatic@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#103124777e8cdec85b20b51fd3397c682ee1e954" @@ -1689,6 +1694,14 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/hoist-non-react-statics@^3.3.5": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/js-yaml@4.0.9": version "4.0.9" resolved "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" @@ -2478,6 +2491,11 @@ convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +cookie@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + core-js-compat@^3.25.1: version "3.29.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.29.1.tgz#15c0fb812ea27c973c18d425099afa50b934b41b" @@ -5445,6 +5463,15 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +react-cookie@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.0.tgz#5770cd8d6b3c60c5d34ec4b7926f90281aee22ae" + integrity sha512-mqhPERUyfOljq5yJ4woDFI33bjEtigsl8JDJdPPeNhr0eSVZmBc/2Vdf8mFxOUktQxhxTR1T+uF0/FRTZyBEgw== + dependencies: + "@types/hoist-non-react-statics" "^3.3.5" + hoist-non-react-statics "^3.3.2" + universal-cookie "^7.0.0" + react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -6349,6 +6376,14 @@ unist-util-visit@^4.0.0: unist-util-is "^5.0.0" unist-util-visit-parents "^5.1.1" +universal-cookie@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.0.tgz#1f3fa9c575d863ac41b4e42272d240ae2d32c047" + integrity sha512-PvcyflJAYACJKr28HABxkGemML5vafHmiL4ICe3e+BEKXRMt0GaFLZhAwgv637kFFnnfiSJ8e6jknrKkMrU+PQ== + dependencies: + "@types/cookie" "^0.6.0" + cookie "^0.6.0" + update-browserslist-db@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"