From 5932999e9d9c91388b5fef3e085c09ed412e3447 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sat, 28 Oct 2023 22:34:28 +0700 Subject: [PATCH 01/21] Fix & Feat UI Page [Privacy Page] [+] fix(privacy.tsx): fix handleAgree function to use privacyTerms state instead of fetching data again [+] feat(privacy.tsx): add privacyTerms state to store privacy.json data and use it to set mdText, pageTitle, and scrollTitle --- app/components/privacy.tsx | 86 +++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/app/components/privacy.tsx b/app/components/privacy.tsx index edcb2358cb2..ec4d2e0eaea 100644 --- a/app/components/privacy.tsx +++ b/app/components/privacy.tsx @@ -10,6 +10,7 @@ import Locale, { import ConfirmIcon from "../icons/confirm.svg"; import LoadingIcon from "../icons/three-dots.svg"; +import CloseIcon from "../icons/close.svg"; import dynamic from "next/dynamic"; import { copyToClipboard } from "../utils"; @@ -17,46 +18,41 @@ const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , }); -export function PrivacyPage(props: { onClose?: () => void }) { +export function PrivacyPage() { const navigate = useNavigate(); const [mdText, setMdText] = useState(""); const [pageTitle, setPageTitle] = useState(""); - const [scrollTitle, setScrollTitle] = useState(""); + const [description, setDescription] = useState(""); const [showTerms, setShowTerms] = useState(true); + const [privacyTerms, setPrivacyTerms] = useState(null); useEffect(() => { - const fetchData = async () => { - const lang = getLang(); // Get the current language - const response = await fetch("privacy.json"); + const fetchPrivacyTerms = async () => { + const lang = getLang(); + const response = await fetch("./privacy.json"); const data = await response.json(); + setPrivacyTerms(data); const privacyPolicy = data[lang][0][1]; - const termsOfService = data[lang][1][1]; - setMdText(`${privacyPolicy}`); + setMdText(privacyPolicy); setPageTitle(data[lang][0][0]); - setScrollTitle(data[lang][0][1]); + setDescription(data[lang][0][2]); // Assuming the description is at index 2 + window.scrollTo({ top: 0, behavior: "smooth" }); }; - fetchData(); + if (!privacyTerms) { + fetchPrivacyTerms(); + } + }, [privacyTerms]); - const handleScroll = () => { - const privacyTitleElement = document.getElementById("privacy-title"); - const termsTitleElement = document.getElementById("terms-title"); - - if (privacyTitleElement && termsTitleElement) { - const privacyTitleRect = privacyTitleElement.getBoundingClientRect(); - const termsTitleRect = termsTitleElement.getBoundingClientRect(); - - if (privacyTitleRect.top < 0 && termsTitleRect.top >= 1) { - setScrollTitle("Privacy Policy"); - } else { - setScrollTitle("Terms of Service"); - } - } - }; - - window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); - }, []); + const handleAgree = () => { + const lang = getLang(); + const termsOfService = privacyTerms[lang][1][1]; + setShowTerms(false); + setMdText(termsOfService); + setPageTitle(privacyTerms[lang][1][0]); + setDescription(privacyTerms[lang][1][2]); // Assuming the description is at index 2 + window.scrollTo({ top: 0, behavior: "smooth" }); + }; const goChat = () => { navigate(Path.Chat); @@ -66,19 +62,6 @@ export function PrivacyPage(props: { onClose?: () => void }) { copyToClipboard(mdText); }; - const handleAgree = async () => { - const lang = getLang(); // Get the current language - const response = await fetch("privacy.json"); - const data = await response.json(); - const privacyPolicy = data[lang][1][1]; - const termsOfService = data[lang][1][1]; - setShowTerms(false); - setMdText(`${termsOfService}`); - setPageTitle(data[lang][1][0]); - setScrollTitle(data[lang][0][0]); - window.scrollTo({ top: 0, behavior: "smooth" }); // Scroll to the top of the page - }; - return (
@@ -100,7 +83,7 @@ export function PrivacyPage(props: { onClose?: () => void }) {
- {showTerms && ( + {showTerms ? (
void }) { bordered />
+
+ } + bordered + onClick={() => navigate(-1)} + /> +
- )} - {!showTerms && ( + ) : (
void }) { bordered />
+
+ } + bordered + onClick={() => navigate(-1)} + /> +
)} From 672cb91605f1f68cc74585260fd8e5214d5a7073 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Fri, 27 Oct 2023 08:32:19 +0700 Subject: [PATCH 02/21] Chore UI Page [Icons] [+] chore(icons): update eye-off.svg and eye.svg icons to have a smaller width and height of 16px --- app/icons/eye-off.svg | 4 ++-- app/icons/eye.svg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/icons/eye-off.svg b/app/icons/eye-off.svg index a0a44f7ac88..96b136537d9 100644 --- a/app/icons/eye-off.svg +++ b/app/icons/eye-off.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + diff --git a/app/icons/eye.svg b/app/icons/eye.svg index b5df29d5b7e..1dee7b4b3a2 100644 --- a/app/icons/eye.svg +++ b/app/icons/eye.svg @@ -1,4 +1,4 @@ - + - \ No newline at end of file + From 690c9694b458b7d75b8c9aec4568503b700b2ce0 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 1 Nov 2023 12:32:48 +0700 Subject: [PATCH 03/21] Docker Dev Environments [+] feat(compose-dev.yaml): add compose-dev.yaml file with configuration for the app service --- compose-dev.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 compose-dev.yaml diff --git a/compose-dev.yaml b/compose-dev.yaml new file mode 100644 index 00000000000..cc7fd0a1f28 --- /dev/null +++ b/compose-dev.yaml @@ -0,0 +1,12 @@ +services: + app: + entrypoint: + - sleep + - infinity + image: docker/dev-environments-javascript:stable-1 + init: true + volumes: + - type: bind + source: /var/run/docker.sock + target: /var/run/docker.sock + From 7cca5b5d74e42cb6c744495e27d0f3e01b3311ea Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Thu, 2 Nov 2023 02:05:04 +0700 Subject: [PATCH 04/21] CloudSync [Golang Sync] [WIP] [+] feat(settings.tsx): add support for GoSync provider in SyncConfigModal component [+] feat(locales): add localization support for GoSync provider in cn.ts, en.ts, and id.ts [+] feat(sync.ts): add gosync property to sync store and update version to 1.2 [+] feat(cloud): add GoSync client implementation in gosync.ts [+] feat(cloud): add GoSync provider to SyncClients in index.ts --- app/components/settings.tsx | 5 +++++ app/locales/cn.ts | 8 ++++++++ app/locales/en.ts | 8 ++++++++ app/locales/id.ts | 8 ++++++++ app/store/sync.ts | 11 ++++++++++- app/utils/cloud/gosync.ts | 11 +++++++++++ app/utils/cloud/index.ts | 3 +++ 7 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 app/utils/cloud/gosync.ts diff --git a/app/components/settings.tsx b/app/components/settings.tsx index b69df1c0bf3..6cac98cab58 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -570,6 +570,11 @@ function SyncConfigModal(props: { onClose?: () => void }) { )} + {syncStore.provider === ProviderType.GoSync && ( + + + + )} ); diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 6a6c26b0c34..66c966fc13b 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -281,6 +281,14 @@ const cn = { UserName: "备份名称", Password: "UpStash Redis REST Token", }, + + GoSync: { + Endpoint: "GoSync REST URL", + UserName: "备份名称", + Password: "GoSync REST 令牌", + FileName: "文件名", + }, + }, LocalState: "本地数据", diff --git a/app/locales/en.ts b/app/locales/en.ts index 2eca273c8b8..465e24f25a1 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -286,6 +286,14 @@ const en: LocaleType = { UserName: "Backup Name", Password: "UpStash Redis REST Token", }, + + GoSync: { + Endpoint: "GoSync REST Url", + UserName: "Backup Name", + Password: "GoSync REST Token", + FileName: "File Name", + }, + }, LocalState: "Local Data", diff --git a/app/locales/id.ts b/app/locales/id.ts index cea116910b2..dfc54044594 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -262,6 +262,14 @@ const id: PartialLocaleType = { "Pastikan Anda memiliki izin untuk sinkronisasi. Aktifkan Privat & Publik di sana.", }, }, + + GoSync: { + Endpoint: "URL GoSync REST", + UserName: "Nama Backup", + Password: "Token GoSync REST", + FileName: "Nama File", + }, + }, LocalState: "Data Lokal", Overview: (overview: any) => { diff --git a/app/store/sync.ts b/app/store/sync.ts index ce78a37ab44..3002cced244 100644 --- a/app/store/sync.ts +++ b/app/store/sync.ts @@ -58,6 +58,12 @@ const DEFAULT_SYNC_STATE = { filename: "", }, + gosync: { + filename: "", + username: STORAGE_KEY, + token: "", + }, + lastSyncTime: 0, lastProvider: "", lastUpdateTime: 0, @@ -226,12 +232,15 @@ export const useSyncStore = createPersistStore( }), { name: StoreKey.Sync, - version: 1.1, + version: 1.2, // golang syncing migrate(persistedState, version) { const newState = persistedState as typeof DEFAULT_SYNC_STATE; if (version < 1.1) { newState.upstash.username = STORAGE_KEY; } + if (version < 1.2) { + newState.gosync.username = STORAGE_KEY; + } return newState as any; }, }, diff --git a/app/utils/cloud/gosync.ts b/app/utils/cloud/gosync.ts new file mode 100644 index 00000000000..8b5a1b1672e --- /dev/null +++ b/app/utils/cloud/gosync.ts @@ -0,0 +1,11 @@ +import { STORAGE_KEY } from "@/app/constant"; +import { SyncStore } from "@/app/store/sync"; +import { corsFetch } from "../cors"; +import { chunks } from "../format"; + +export type GoSync = SyncStore["gosync"]; +export type GoSyncClient = ReturnType; + +export function createGoSyncClient(store: SyncStore) { + /** TODO */ +} diff --git a/app/utils/cloud/index.ts b/app/utils/cloud/index.ts index 063976ef638..67a592a359b 100644 --- a/app/utils/cloud/index.ts +++ b/app/utils/cloud/index.ts @@ -1,17 +1,20 @@ import { createWebDavClient } from "./webdav"; import { createUpstashClient } from "./upstash"; import { createGistClient } from "./gist"; +import { createGoSyncClient } from "./gosync"; export enum ProviderType { WebDAV = "webdav", UpStash = "upstash", GitHubGist = "githubGist", + GoSync = "gosync", } export const SyncClients = { [ProviderType.UpStash]: createUpstashClient, [ProviderType.WebDAV]: createWebDavClient, [ProviderType.GitHubGist]: createGistClient, + [ProviderType.GoSync]: createGoSyncClient, } as const; type SyncClientConfig = { From fbdcf934a8d22e109ee2776576fcd903d7508556 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sun, 22 Oct 2023 17:46:35 +0700 Subject: [PATCH 05/21] Feat UI Page [ChangeLog] [+] chore(changelog.tsx): refactor table generation logic [+] feat(changelog.tsx): add PR links to commit message summary and description --- app/components/changelog.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/components/changelog.tsx b/app/components/changelog.tsx index 2fb9baab65e..fd9a94f5d16 100644 --- a/app/components/changelog.tsx +++ b/app/components/changelog.tsx @@ -50,8 +50,14 @@ export function ChangeLog(props: { onClose?: () => void }) { } const authorSection = `[${author.replace("[bot]", "").replace(/\s/g, "")}](https://github.com/${author.replace("[bot]", "").replace(/\s/g, "")}) ${coAuthorsSection}`; + const prLinkRegex = /#(\d+)/g; // Regular expression to match '#' ref : autolinks github + const prLink = commitInfo?.commitMessage.summary.replace(prLinkRegex, '[$&](https://github.com/H0llyW00dzZ/ChatGPT-Next-Web/pull/$1/commits)'); + const descriptionWithLinks = commitInfo?.commitMessage.description.map((change: string) => + change.replace(prLinkRegex, '[$&](https://github.com/H0llyW00dzZ/ChatGPT-Next-Web/pull/$1/commits)') + ).join('\n\n\n'); - table += `\n\n\n![GitHub contributors](https://img.shields.io/github/contributors/Yidadaa/ChatGPT-Next-Web.svg) ![GitHub commits](https://badgen.net/github/commits/H0llyW00dzZ/ChatGPT-Next-Web) ![GitHub license](https://img.shields.io/github/license/H0llyW00dzZ/ChatGPT-Next-Web) [![GitHub forks](https://img.shields.io/github/forks/Yidadaa/ChatGPT-Next-Web.svg)](https://github.com/Yidadaa/ChatGPT-Next-Web/network/members) [![GitHub stars](https://img.shields.io/github/stars/Yidadaa/ChatGPT-Next-Web.svg)](https://github.com/Yidadaa/ChatGPT-Next-Web/stargazers) [![Github All Releases](https://img.shields.io/github/downloads/Yidadaa/ChatGPT-Next-Web/total.svg)](https://github.com/Yidadaa/ChatGPT-Next-Web/releases/)\n\n\n [![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/${author}) ![${author.replace("[bot]", "")}](https://github.com/${author.replace("[bot]", "")}.png?size=25) ${authorSection} :\n\n${commitInfo?.commitMessage.summary}\n\n\n${changesFormatted}\n\n\n`; } else { + table += `\n\n\n![GitHub contributors](https://img.shields.io/github/contributors/Yidadaa/ChatGPT-Next-Web.svg) ![GitHub commits](https://badgen.net/github/commits/H0llyW00dzZ/ChatGPT-Next-Web) ![GitHub license](https://img.shields.io/github/license/H0llyW00dzZ/ChatGPT-Next-Web) [![GitHub forks](https://img.shields.io/github/forks/Yidadaa/ChatGPT-Next-Web.svg)](https://github.com/Yidadaa/ChatGPT-Next-Web/network/members) [![GitHub stars](https://img.shields.io/github/stars/Yidadaa/ChatGPT-Next-Web.svg)](https://github.com/Yidadaa/ChatGPT-Next-Web/stargazers) [![Github All Releases](https://img.shields.io/github/downloads/Yidadaa/ChatGPT-Next-Web/total.svg)](https://github.com/Yidadaa/ChatGPT-Next-Web/releases/)\n\n\n [![GitHub](https://img.shields.io/badge/--181717?logo=github&logoColor=ffffff)](https://github.com/${author}) ![${author.replace("[bot]", "")}](https://github.com/${author.replace("[bot]", "")}.png?size=25) ${authorSection} :\n\n${prLink}\n\n\n${descriptionWithLinks}\n\n\n\n\n\n`; + } else { table += `###${commitInfo?.commitMessage.summary}###\nNo changes\n\n`; } From 664a08273f6e8718c688233d92c56d896f54b05f Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Fri, 13 Oct 2023 16:55:36 +0700 Subject: [PATCH 06/21] Fix UI Page & Build Setup - [ChangeLog Page] [+] fix(changelog.module.scss): adjust padding values for button container - [PrivacyPage] [+] fix(privacy.module.scss): adjust padding values for button container - [Build Setup] [+] fix(build.ts): filter out duplicate authors in Signed-off-by and Co-Authored-By lines --- app/components/changelog.module.scss | 11 +++++++---- app/components/privacy.module.scss | 22 +++++++++++++++++----- app/config/build.ts | 6 ++++-- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/components/changelog.module.scss b/app/components/changelog.module.scss index f47bdc09fe5..75cd3b285d1 100644 --- a/app/components/changelog.module.scss +++ b/app/components/changelog.module.scss @@ -16,6 +16,7 @@ font-size: 24px; font-weight: bold; line-height: 3; + position: relative; } .changelog-tips { @@ -51,6 +52,9 @@ height: 10%; width: 10%; margin-top: auto; /* Add margin for spacing */ + padding-top: 10px; + padding-bottom: 10px; + position: relative; button:not(:last-child) { margin-bottom: auto; @@ -73,7 +77,7 @@ justify-content: space-evenly; /* Spread the buttons evenly */ align-items: center; /* Center align the buttons vertically */ margin-top: auto; /* Adjust the margin */ - + position: relative; button { width: auto; /* Reset the width to auto */ } @@ -93,9 +97,8 @@ flex-direction: row; /* Change to horizontal layout */ justify-content: space-evenly; /* Spread the buttons evenly */ align-items: center; /* Center align the buttons vertically */ - height: 10%; - width: 10%; - margin-top: 20px; /* Adjust the margin */ + margin-top: auto; /* Adjust the margin */ + position: relative; } button { diff --git a/app/components/privacy.module.scss b/app/components/privacy.module.scss index 016b9f2019a..43b9673948a 100644 --- a/app/components/privacy.module.scss +++ b/app/components/privacy.module.scss @@ -12,11 +12,20 @@ margin-bottom: 20px; /* Add margin for spacing */ } + .privacy-header { + display: flex; + align-items: center; + justify-content: center; + margin-right: 10px; + } + .privacy-title { font-size: 24px; font-weight: bold; line-height: 3; - } + position: relative; + margin-right: 10px; /* Add some spacing to the right of the title */ + } .privacy-tips { box-sizing: border-box; @@ -51,6 +60,9 @@ height: 10%; width: 10%; margin-top: auto; /* Add margin for spacing */ + padding-top: 10px; + padding-bottom: 10px; + position: relative; button:not(:last-child) { margin-bottom: auto; @@ -62,6 +74,7 @@ justify-content: space-evenly; /* Spread the buttons evenly */ align-items: center; /* Center align the buttons vertically */ margin-top: auto; /* Adjust the margin */ + position: relative; button { width: auto; /* Set a fixed width for the buttons */ } @@ -73,7 +86,7 @@ justify-content: space-evenly; /* Spread the buttons evenly */ align-items: center; /* Center align the buttons vertically */ margin-top: auto; /* Adjust the margin */ - + position: relative; button { width: auto; /* Reset the width to auto */ } @@ -93,9 +106,8 @@ flex-direction: row; /* Change to horizontal layout */ justify-content: space-evenly; /* Spread the buttons evenly */ align-items: center; /* Center align the buttons vertically */ - height: 10%; - width: 10%; - margin-top: 20px; /* Adjust the margin */ + margin-top: auto; /* Adjust the margin */ + position: relative; } button { diff --git a/app/config/build.ts b/app/config/build.ts index 3b0c9b8d239..f47ea4e2811 100644 --- a/app/config/build.ts +++ b/app/config/build.ts @@ -53,13 +53,15 @@ export const getBuildConfig = () => { .split("\n") .map((line) => line.trim()) .filter((line) => line.startsWith("Signed-off-by:")) - .map((line) => line.substring("Signed-off-by:".length).trim().split(" <")[0]); + .map((line) => line.substring("Signed-off-by:".length).trim().split(" <")[0]) + .filter((author, index, self) => self.indexOf(author) === index); const coAuthoredBy: string[] = commitMessage .split("\n") .map((line) => line.trim()) .filter((line) => line.startsWith("Co-Authored-By:") || line.startsWith("Co-authored-by:")) - .map((line) => line.substring("Co-Authored-By:".length).trim().split(" <")[0]); + .map((line) => line.substring("Co-Authored-By:".length).trim().split(" <")[0]) + .filter((author, index, self) => self.indexOf(author) === index); const commitMessageObj = { summary: title || "No title", From 14c00ee8697707898e9310f2be090e6fb9d328a9 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Wed, 25 Oct 2023 16:38:25 +0700 Subject: [PATCH 07/21] Fix Language Indonesia [Memory Prompt Masks] [+] fix(id.ts): fix localization key for updating masks session memory prompt [+] fix(id.ts): fix localization keys for success and failure messages when updating masks session memory prompt --- app/locales/id.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/locales/id.ts b/app/locales/id.ts index dfc54044594..fc6d8d82e97 100644 --- a/app/locales/id.ts +++ b/app/locales/id.ts @@ -61,10 +61,10 @@ const id: PartialLocaleType = { save: "Simpan Percakapan Sesi Saat Ini", load: "Muat Percakapan Sesi", copymemoryai: "Salin sesi memori prompt AI", - updatemasks: "Perbarui sesi memori prompt untuk sebuah topeng", + updatemasks: "Perbarui sesi memori prompt untuk sebuah Masks", UI: { - MasksSuccess: "Berhasil memperbarui sesi topeng", - MasksFail: "Gagal memperbarui sesi topeng", + MasksSuccess: "Berhasil memperbarui sesi Masks", + MasksFail: "Gagal memperbarui sesi Masks", }, }, InputActions: { From 4c48573413691b1bc0f1a57e91b472ee8e999648 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Tue, 7 Nov 2023 20:25:44 +0700 Subject: [PATCH 08/21] Refactor Summarize Logic [+] fix(chat.ts): fix known issue where summarize is not using the current model selected [+] refactor(chat.ts): extract sessionModelConfig from currentSession().mask.modelConfig [+] refactor(chat.ts): pass sessionModelConfig to getSummarizeModel function --- app/store/chat.ts | 162 +++++++++++++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 51 deletions(-) diff --git a/app/store/chat.ts b/app/store/chat.ts index f4003f6fae3..1a011dc86ae 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -80,9 +80,10 @@ function createEmptySession(): ChatSession { }; } -function getSummarizeModel(currentModel: string) { - // if it is using gpt-* models, force to use 3.5 to summarize - return currentModel.startsWith("gpt") ? SUMMARIZE_MODEL : currentModel; +// fix known issue where summarize is not using the current model selected +function getSummarizeModel(currentModel: string, modelConfig: ModelConfig) { + // should be depends of user selected + return currentModel.startsWith("gpt") ? modelConfig.model : currentModel; } function countMessages(msgs: ChatMessage[]) { @@ -491,20 +492,62 @@ export const useChatStore = createPersistStore( content: Locale.Store.Prompt.Topic, }), ); - api.llm.chat({ - messages: topicMessages, - config: { - model: getSummarizeModel(session.mask.modelConfig.model), - }, - whitelist: true, - onFinish(message) { - get().updateCurrentSession( - (session) => - (session.topic = - message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC), - ); - }, - }); + + const sessionModelConfig = this.currentSession().mask.modelConfig; + const topicModel = getSummarizeModel(session.mask.modelConfig.model, sessionModelConfig); + + if (topicModel === "dall-e-2-beta-instruct-vision" || topicModel === "dall-e-3-beta-instruct-vision" || topicModel === "dall-e-2" || topicModel === "dall-e-3") { + // Summarize topic using gpt-3.5-turbo-0613 which is compatible with DALL-E-2 model + api.llm.chat({ + messages: topicMessages, + config: { + model: "gpt-4-vision-preview", + }, + whitelist: true, + onFinish(message) { + get().updateCurrentSession( + (session) => { + session.topic = + message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC; + // Add system message after summarizing the topic + // which is powerful based of fine-tuning + const systemMessage: ChatMessage = { + date: new Date().toLocaleString(), + id: nanoid(), + role: "system", + content: `${Locale.FineTuned.Sysmessage} ${session.topic}`, + }; + session.messages = [systemMessage, ...session.messages]; + }); + }, + }); + } else { + // Summarize topic using the selected model + api.llm.chat({ + messages: topicMessages, + config: { + model: topicModel, + }, + whitelist: true, + onFinish(message) { + get().updateCurrentSession( + (session) => { + session.topic = + message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC; + // Add system message after summarizing the topic + // which is powerful based of fine-tuning + const systemMessage: ChatMessage = { + date: new Date().toLocaleString(), + id: nanoid(), + role: "system", + content: `${Locale.FineTuned.Sysmessage} ${session.topic}`, + }; + session.messages = [systemMessage, ...session.messages]; + }); + showToast(Locale.Chat.Commands.UI.SummarizeSuccess); + }, + }); + } } const modelConfig = session.mask.modelConfig; @@ -513,8 +556,8 @@ export const useChatStore = createPersistStore( session.clearContextIndex ?? 0, ); let toBeSummarizedMsgs = messages - .filter((msg) => !msg.isError) - .slice(summarizeIndex); + .filter((msg) => !msg.isError) + .slice(summarizeIndex); const historyMsgLength = countMessages(toBeSummarizedMsgs); @@ -541,31 +584,55 @@ export const useChatStore = createPersistStore( historyMsgLength > modelConfig.compressMessageLengthThreshold && modelConfig.sendMemory ) { - api.llm.chat({ - messages: toBeSummarizedMsgs.concat( - createMessage({ - role: "system", - content: Locale.Store.Prompt.Summarize, - date: "", - }), - ), - config: { - ...modelConfig, - stream: true, - model: getSummarizeModel(session.mask.modelConfig.model), - }, - whitelist: false, - onUpdate(message) { - session.memoryPrompt = message; - }, - onFinish(message) { - console.log("[Memory] ", message); - session.lastSummarizeIndex = lastSummarizeIndex; - }, - onError(err) { - console.error("[Summarize] ", err); - }, - }); + const sessionModelConfig = this.currentSession().mask.modelConfig; + const summarizeModel = getSummarizeModel(session.mask.modelConfig.model, sessionModelConfig); + + if (summarizeModel === "dall-e-2-beta-instruct-vision" || summarizeModel === "dall-e-3-beta-instruct-vision" || summarizeModel === "dall-e-2" || summarizeModel === "dall-e-3") { + // Summarize using gpt-3.5-turbo-0613 which is compatible with DALL-E-2 model + api.llm.chat({ + messages: toBeSummarizedMsgs.concat( + createMessage({ + role: "system", + content: Locale.Store.Prompt.Summarize, + date: "", + }), + ), + config: { ...modelConfig, model: "gpt-4-vision-preview", stream: true }, + whitelist: true, + onFinish(message) { + console.log("[Memory] ", message); + session.lastSummarizeIndex = lastSummarizeIndex; + }, + onError(err) { + console.error("[Summarize] ", err); + }, + }); + } else { + // Summarize using the selected model + api.llm.chat({ + messages: toBeSummarizedMsgs.concat( + createMessage({ + role: "system", + content: Locale.Store.Prompt.Summarize, + date: "", + }), + ), + config: { ...modelConfig, stream: true }, + onUpdate(message) { + session.memoryPrompt = message; + }, + whitelist: true, + onFinish(message) { + console.log("[Memory] ", message); + session.lastSummarizeIndex = lastSummarizeIndex; + showToast(Locale.Chat.Commands.UI.SummarizeSuccess); + }, + onError(err) { + console.error("[Summarize] ", err); + showToast(Locale.Chat.Commands.UI.SummarizeFail); + }, + }); + } } }, @@ -583,13 +650,6 @@ export const useChatStore = createPersistStore( set(() => ({ sessions })); }, - clearChatData() { - set(() => ({ - sessions: [createEmptySession()], - currentSessionIndex: 0, - })); - }, - clearAllData() { localStorage.clear(); location.reload(); From 2cbc40e2e33cb9c4090156f534dd37b32e2070ad Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Mon, 6 Nov 2023 18:07:24 +0700 Subject: [PATCH 09/21] Feat & Fix Summarize [Notification Toast] [+] feat(chat.ts): add showToast for successful and failed summarization in useChatStore [+] fix(chat.ts): add showToast for successful summarization in onFinish callback From 97e1f5f89fe35413aeaa3a095efe4c0f29c263a8 Mon Sep 17 00:00:00 2001 From: H0llyW00dzZ Date: Sat, 28 Oct 2023 07:54:11 +0700 Subject: [PATCH 10/21] Feat UI Page [Chat Settings] [+] feat(chat.tsx): add support for exporting session data as a JSON file with a timestamped filename --- app/components/chat.tsx | 75 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index af4ede20628..d7d57f7e629 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -27,6 +27,8 @@ import PinIcon from "../icons/pin.svg"; import EditIcon from "../icons/rename.svg"; import ConfirmIcon from "../icons/confirm.svg"; import CancelIcon from "../icons/cancel.svg"; +import DownloadIcon from "../icons/download.svg"; +import UploadIcon from "../icons/upload.svg"; import LightIcon from "../icons/light.svg"; import DarkIcon from "../icons/dark.svg"; @@ -34,6 +36,8 @@ import AutoIcon from "../icons/auto.svg"; import BottomIcon from "../icons/bottom.svg"; import StopIcon from "../icons/pause.svg"; import RobotIcon from "../icons/robot.svg"; +import EyeOnIcon from "../icons/eye.svg"; +import EyeOffIcon from "../icons/eye-off.svg"; import { escapeRegExp } from "lodash"; import { @@ -103,12 +107,57 @@ export function SessionConfigModel(props: { onClose: () => void }) { const maskStore = useMaskStore(); const navigate = useNavigate(); + const [exporting, setExporting] = useState(false); + const isApp = !!getClientConfig()?.isApp; + + const handleExport = async () => { + if (exporting) return; + setExporting(true); + const currentDate = new Date(); + const datePart = isApp + ? `${currentDate.toLocaleDateString().replace(/\//g, '_')} ${currentDate.toLocaleTimeString().replace(/:/g, '_')}` + : `${currentDate.toLocaleString().replace(/:/g, '_')}`; + + const fileName = `${session.topic}-${datePart}.json`; + await downloadAs(session, fileName); + setExporting(false); + }; + + const importchat = async () => { + await readFromFile().then((content) => { + try { + const importedData = JSON.parse(content); + chatStore.updateCurrentSession((session) => { + Object.assign(session, importedData); + }); + } catch (e) { + console.error("[Import] Failed to import JSON file:", e); + showToast(Locale.Settings.Sync.ImportFailed); + } + }); + }; + return (
props.onClose()} actions={[ + } + bordered + text={Locale.UI.Export} + onClick={handleExport} + disabled={exporting} + />, + } + bordered + text={Locale.UI.Import} + onClick={importchat} + />, } @@ -413,6 +462,8 @@ export function ChatActions(props: { scrollToBottom: () => void; showPromptHints: () => void; hitBottom: boolean; + showContextPrompts: boolean; + toggleContextPrompts: () => void; }) { const config = useAppConfig(); const navigate = useNavigate(); @@ -485,6 +536,22 @@ export function ChatActions(props: { icon={} /> + + ) : ( + + ) + } + /> + { navigate(Path.Masks); @@ -653,11 +720,9 @@ function _Chat() { const loadchat = () => { readFromFile().then((content) => { try { - const importedSession = JSON.parse(content); + const importedData = JSON.parse(content); chatStore.updateCurrentSession((session) => { - session.messages = importedSession.messages; - session.topic = importedSession.topic; - session.memoryPrompt = importedSession.memoryPrompt; + Object.assign(session, importedData); // Set any other properties you want to update in the session }); } catch (e) { @@ -1330,6 +1395,8 @@ function _Chat() { setUserInput("/"); onSearch(""); }} + showContextPrompts={false} + toggleContextPrompts={() => showToast(Locale.WIP)} />