diff --git a/app/api/openai/typing.ts b/app/api/openai/typing.ts index 2286d23124e..82d401f0797 100644 --- a/app/api/openai/typing.ts +++ b/app/api/openai/typing.ts @@ -1,9 +1,10 @@ import type { CreateChatCompletionRequest, CreateChatCompletionResponse, + CreateImageRequestSizeEnum, } from "openai"; export type ChatRequest = CreateChatCompletionRequest; export type ChatResponse = CreateChatCompletionResponse; - +export type ImageRequestSizeEnum = CreateImageRequestSizeEnum; export type Updater = (updater: (value: T) => void) => void; diff --git a/app/components/ImageList.tsx b/app/components/ImageList.tsx index 01e30013eb8..8d649f45431 100644 --- a/app/components/ImageList.tsx +++ b/app/components/ImageList.tsx @@ -1,6 +1,6 @@ import { ImagesResponseDataInner } from "openai"; import React, { FC } from "react"; -import styles from "./ImageList.module.scss"; +import styles from "./imageList.module.scss"; interface ImageListProps { images?: ImagesResponseDataInner[]; diff --git a/app/components/chat.tsx b/app/components/chat.tsx index 1b4f52b509e..0ff363fb694 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -25,7 +25,6 @@ import { Message, SubmitKey, useChatStore, - BOT_HELLO, createMessage, useAccessStore, Theme, @@ -560,9 +559,9 @@ export function Chat() { if ( context.length === 0 && - session.messages.at(0)?.content !== BOT_HELLO.content + session.messages.at(0)?.content !== session.botHello.content ) { - const copiedHello = Object.assign({}, BOT_HELLO); + const copiedHello = Object.assign({}, session.botHello); if (!accessStore.isAuthorized()) { copiedHello.content = Locale.Error.Unauthorized; } diff --git a/app/components/image-model-config.tsx b/app/components/image-model-config.tsx new file mode 100644 index 00000000000..aea8f80cdb9 --- /dev/null +++ b/app/components/image-model-config.tsx @@ -0,0 +1,74 @@ +import { + ALL_MODELS, + ImageModalConfigValidator, + ImageModelConfig, + ModalConfigValidator, + ModelConfig, +} from "../store"; + +import Locale from "../locales"; +import { InputRange } from "./input-range"; +import { Input, List, ListItem, Select } from "./ui-lib"; +import { ImageRequestSizeEnum } from "../api/openai/typing"; +import { CreateImageRequestSizeEnum } from "openai"; + +export function ImageModelConfigList(props: { + imageModelConfig: ImageModelConfig; + updateConfig: (updater: (config: ImageModelConfig) => void) => void; +}) { + return ( + <> + + + + + { + props.updateConfig((config) => { + config.command = e.currentTarget.value; // Assign the parsed value + return config; + }); + }} + /> + + + { + const newValue = parseInt(e.currentTarget.value, 10); // Parse the value as an integer + if (!isNaN(newValue)) { + props.updateConfig((config) => { + config.imageLimit = newValue; // Assign the parsed value + return config; + }); + } + }} + min={"1"} // Convert the min value to a string + max={"10"} // Convert the max value to a string + step={"1"} // Convert the step value to a string + /> + + + + + + ); +} diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 2e08c251e2e..82c28e0a0d3 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -40,6 +40,7 @@ import { ErrorBoundary } from "./error"; import { InputRange } from "./input-range"; import { useNavigate } from "react-router-dom"; import { Avatar, AvatarPicker } from "./emoji"; +import { ImageModelConfigList } from "./image-model-config"; function EditPromptModal(props: { id: number; onClose: () => void }) { const promptStore = usePromptStore(); @@ -580,6 +581,18 @@ export function Settings() { }} /> + + { + const imageModelConfig = { ...config.imageModelConfig }; + upater(imageModelConfig); + config.update( + (config) => (config.imageModelConfig = imageModelConfig), + ); + }} + /> + {shouldShowPromptModal && ( setShowPromptModal(false)} /> diff --git a/app/constant.ts b/app/constant.ts index 560d57a1b5e..36ca672a729 100644 --- a/app/constant.ts +++ b/app/constant.ts @@ -42,3 +42,4 @@ export const ACCESS_CODE_PREFIX = "ak-"; export const LAST_INPUT_KEY = "last-input"; export const IMAGE_PLACEHOLDER = "Loading your image..."; export const IMAGE_ERROR = "IMAGE_ERROR"; +export const COMMAND_IMAGE = "/image"; diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 9d8f5c50443..7403f1cdd2f 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -155,6 +155,13 @@ const cn = { Placeholder: "请输入访问密码", }, Model: "模型 (model)", + ImageModel: { + Title: "图片模型", + Model: "DALL-E模型", + Command: "生成图片指令", + CountLimit: "图片生成次数限制", + Size: "图片生成尺寸", + }, Temperature: { Title: "随机性 (temperature)", SubTitle: "值越大,回复越随机", @@ -170,8 +177,11 @@ const cn = { }, Store: { DefaultTopic: "新的聊天", - BotHello: - "您好!今天我能为您做些什么呢?\n 要生成图片,请使用 `/Image {关键词}。`", + BotHello: "您好!今天我能为您做些什么呢?", + BotHelloWithCommand: (command: string) => + "您好!今天我能为您做些什么呢?\n 要生成图片,请使用 `" + + command + + " {关键词}。`", Error: "出错了,稍后重试吧", Prompt: { History: (content: string) => diff --git a/app/locales/cs.ts b/app/locales/cs.ts index ab9b39a5387..47909dffedb 100644 --- a/app/locales/cs.ts +++ b/app/locales/cs.ts @@ -70,8 +70,8 @@ const cs: LocaleType = { }, Lang: { Name: "Language", // ATTENTION: if you wanna add a new translation, please do not translate this value, leave it as `Language` - All: "Všechny jazyky", - Options: { + All: "Všechny jazyky", + Options: { cn: "简体中文", en: "English", tw: "繁體中文", @@ -156,6 +156,13 @@ const cs: LocaleType = { Placeholder: "Potřebujete přístupový kód", }, Model: "Model", + ImageModel: { + Title: "Obrázkový model", + Model: "Model DALL-E", + Command: "Příkaz pro generování obrázků", + CountLimit: "Limit počtu generovaných obrázků", + Size: "Velikost generovaného obrázku", + }, Temperature: { Title: "Teplota", SubTitle: "Větší hodnota činí výstup náhodnějším", @@ -166,13 +173,16 @@ const cs: LocaleType = { }, PresencePenlty: { Title: "Přítomnostní korekce", - SubTitle: - "Větší hodnota zvyšuje pravděpodobnost nových témat.", + SubTitle: "Větší hodnota zvyšuje pravděpodobnost nových témat.", }, }, Store: { DefaultTopic: "Nová konverzace", BotHello: "Ahoj! Jak mohu dnes pomoci?", + BotHelloWithCommand: (command: string) => + "Ahoj! Jak mohu dnes pomoci?\n Pro generování obrázků použijte `" + + command + + " {klíčové slovo}.`", Error: "Něco se pokazilo, zkuste to prosím později.", Prompt: { History: (content: string) => @@ -182,7 +192,7 @@ const cs: LocaleType = { "Vytvořte prosím název o čtyřech až pěti slovech vystihující průběh našeho rozhovoru bez jakýchkoli úvodních slov, interpunkčních znamének, uvozovek, teček, symbolů nebo dalšího textu. Odstraňte uvozovky.", Summarize: "Krátce shrň naši diskusi v rozsahu do 200 slov a použij ji jako podnět pro budoucí kontext.", - }, + }, }, Copy: { Success: "Zkopírováno do schránky", @@ -231,7 +241,7 @@ const cs: LocaleType = { More: "Najít více", NotShow: "Nezobrazovat znovu", ConfirmNoShow: "Potvrdit zakázání?Můžete jej povolit později v nastavení.", -}, + }, UI: { Confirm: "Potvrdit", @@ -239,7 +249,7 @@ const cs: LocaleType = { Close: "Zavřít", Create: "Vytvořit", Edit: "Upravit", - } + }, }; export default cs; diff --git a/app/locales/de.ts b/app/locales/de.ts index 8cfe3be4515..8024ebd76eb 100644 --- a/app/locales/de.ts +++ b/app/locales/de.ts @@ -158,6 +158,13 @@ const de: LocaleType = { Placeholder: "Zugangscode erforderlich", }, Model: "Modell", + ImageModel: { + Title: "Bildmodell", + Model: "DALL-E Modell", + Command: "Bildgenerierungsbefehl", + CountLimit: "Bildgenerierungslimit", + Size: "Bildgenerierungsgröße", + }, Temperature: { Title: "Temperature", //Temperatur SubTitle: "Ein größerer Wert führt zu zufälligeren Antworten", @@ -175,6 +182,10 @@ const de: LocaleType = { Store: { DefaultTopic: "Neues Gespräch", BotHello: "Hallo! Wie kann ich Ihnen heute helfen?", + BotHelloWithCommand: (command: string) => + "Hallo! Wie kann ich Ihnen heute helfen?\n Um Bilder zu generieren, verwenden Sie `" + + command + + " {Schlüsselwort}.`", Error: "Etwas ist schief gelaufen, bitte versuchen Sie es später noch einmal.", Prompt: { diff --git a/app/locales/en.ts b/app/locales/en.ts index 1860cd0777a..04ba5a4c348 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -156,6 +156,13 @@ const en: LocaleType = { Placeholder: "Need Access Code", }, Model: "Model", + ImageModel: { + Title: "Image Model", + Model: "DALL-E Model", + Command: "Generate Image Command", + CountLimit: "Image Generation Limit", + Size: "Image Generation Size", + }, Temperature: { Title: "Temperature", SubTitle: "A larger value makes the more random output", @@ -172,8 +179,11 @@ const en: LocaleType = { }, Store: { DefaultTopic: "New Conversation", - BotHello: - "Hello! How can I assist you today?\n To generate images, use `/Image {keyword}.`", + BotHello: "Hello! How can I assist you today?", + BotHelloWithCommand: (command: string) => + "Hello! How can I assist you today?\n To generate images, use `" + + command + + " {keyword}.`", Error: "Something went wrong, please try again later.", Prompt: { History: (content: string) => diff --git a/app/locales/es.ts b/app/locales/es.ts index 5f66071ce00..f7bf5f4c6a2 100644 --- a/app/locales/es.ts +++ b/app/locales/es.ts @@ -156,6 +156,13 @@ const es: LocaleType = { Placeholder: "Necesita código de acceso", }, Model: "Modelo", + ImageModel: { + Title: "Modelo de imagen", + Model: "Modelo DALL-E", + Command: "Comando de generación de imágenes", + CountLimit: "Límite de generación de imágenes", + Size: "Tamaño de generación de imagen", + }, Temperature: { Title: "Temperatura", SubTitle: "Un valor mayor genera una salida más aleatoria", @@ -173,6 +180,10 @@ const es: LocaleType = { Store: { DefaultTopic: "Nueva conversación", BotHello: "¡Hola! ¿Cómo puedo ayudarte hoy?", + BotHelloWithCommand: (command: string) => + "¡Hola! ¿Cómo puedo ayudarte hoy?\n Para generar imágenes, usa `" + + command + + " {palabra clave}.`", Error: "Algo salió mal, por favor intenta nuevamente más tarde.", Prompt: { History: (content: string) => diff --git a/app/locales/it.ts b/app/locales/it.ts index f9daa7a37f6..6c2c4c2f534 100644 --- a/app/locales/it.ts +++ b/app/locales/it.ts @@ -157,6 +157,13 @@ const it: LocaleType = { Placeholder: "Inserisci il codice d'accesso", }, Model: "Modello GPT", + ImageModel: { + Title: "Modello di immagine", + Model: "Modello DALL-E", + Command: "Comando di generazione immagine", + CountLimit: "Limite di generazione immagine", + Size: "Dimensione di generazione immagine", + }, Temperature: { Title: "Temperature", SubTitle: "Un valore maggiore rende l'output più casuale", @@ -174,6 +181,10 @@ const it: LocaleType = { Store: { DefaultTopic: "Nuova conversazione", BotHello: "Ciao, come posso aiutarti oggi?", + BotHelloWithCommand: (command: string) => + "Ciao, come posso aiutarti oggi?\n Per generare immagini, usa `" + + command + + " {parola chiave}.`", Error: "Qualcosa è andato storto, riprova più tardi.", Prompt: { History: (content: string) => diff --git a/app/locales/jp.ts b/app/locales/jp.ts index 526574b308d..ef8a0049e1e 100644 --- a/app/locales/jp.ts +++ b/app/locales/jp.ts @@ -158,6 +158,13 @@ const jp: LocaleType = { Placeholder: "アクセスパスワードを入力してください", }, Model: "モデル (model)", + ImageModel: { + Title: "画像モデル", + Model: "DALL-Eモデル", + Command: "画像生成コマンド", + CountLimit: "画像生成回数制限", + Size: "画像生成サイズ", + }, Temperature: { Title: "ランダム性 (temperature)", SubTitle: @@ -175,6 +182,10 @@ const jp: LocaleType = { Store: { DefaultTopic: "新しいチャット", BotHello: "何かお手伝いできることはありますか", + BotHelloWithCommand: (command: string) => + "何かお手伝いできることはありますか\n 画像を生成するには、`" + + command + + " {キーワード}.`を使用してください", Error: "エラーが発生しました。しばらくしてからやり直してください。", Prompt: { History: (content: string) => diff --git a/app/locales/ru.ts b/app/locales/ru.ts index 437a54b2ba8..50dde81420a 100644 --- a/app/locales/ru.ts +++ b/app/locales/ru.ts @@ -85,51 +85,52 @@ const ru: LocaleType = { cs: "Čeština", }, }, - Avatar: "Аватар", - FontSize: { - Title: "Размер шрифта", - SubTitle: "Настроить размер шрифта контента чата", - }, - Update: { - Version: (x: string) => `Версия: ${x}`, - IsLatest: "Последняя версия", - CheckUpdate: "Проверить обновление", - IsChecking: "Проверка обновления...", - FoundUpdate: (x: string) => `Найдена новая версия: ${x}`, - GoToUpdate: "Обновить", - }, - SendKey: "Клавиша отправки", - Theme: "Тема", - TightBorder: "Узкая граница", - SendPreviewBubble: { - Title: "Отправить предпросмотр", - SubTitle: "Предварительный просмотр markdown в пузыре", + Avatar: "Аватар", + FontSize: { + Title: "Размер шрифта", + SubTitle: "Настроить размер шрифта контента чата", + }, + Update: { + Version: (x: string) => `Версия: ${x}`, + IsLatest: "Последняя версия", + CheckUpdate: "Проверить обновление", + IsChecking: "Проверка обновления...", + FoundUpdate: (x: string) => `Найдена новая версия: ${x}`, + GoToUpdate: "Обновить", + }, + SendKey: "Клавиша отправки", + Theme: "Тема", + TightBorder: "Узкая граница", + SendPreviewBubble: { + Title: "Отправить предпросмотр", + SubTitle: "Предварительный просмотр markdown в пузыре", + }, + Mask: { + Title: "Экран заставки маски", + SubTitle: "Показывать экран заставки маски перед началом нового чата", + }, + Prompt: { + Disable: { + Title: "Отключить автозаполнение", + SubTitle: "Ввод / для запуска автозаполнения", }, - Mask: { - Title: "Экран заставки маски", - SubTitle: "Показывать экран заставки маски перед началом нового чата", + List: "Список подсказок", + ListCount: (builtin: number, custom: number) => + `${builtin} встроенных, ${custom} пользовательских`, + Edit: "Редактировать", + Modal: { + Title: "Список подсказок", + Add: "Добавить", + Search: "Поиск подсказок", }, - Prompt: { - Disable: { - Title: "Отключить автозаполнение", - SubTitle: "Ввод / для запуска автозаполнения", - }, - List: "Список подсказок", - ListCount: (builtin: number, custom: number) => - `${builtin} встроенных, ${custom} пользовательских`, - Edit: "Редактировать", - Modal: { - Title: "Список подсказок", - Add: "Добавить", - Search: "Поиск подсказок", - }, - EditModal: { - Title: "Редактировать подсказку", - }, + EditModal: { + Title: "Редактировать подсказку", }, - HistoryCount: { - Title: "Количество прикрепляемых сообщений", - SubTitle: "Количество отправляемых сообщений, прикрепляемых к каждому запросу", + }, + HistoryCount: { + Title: "Количество прикрепляемых сообщений", + SubTitle: + "Количество отправляемых сообщений, прикрепляемых к каждому запросу", }, CompressThreshold: { Title: "Порог сжатия истории", @@ -156,6 +157,13 @@ const ru: LocaleType = { Placeholder: "Требуется код доступа", }, Model: "Модель", + ImageModel: { + Title: "Модель изображения", + Model: "Модель DALL-E", + Command: "Команда генерации изображений", + CountLimit: "Лимит генерации изображений", + Size: "Размер генерируемого изображения", + }, Temperature: { Title: "Температура", SubTitle: "Чем выше значение, тем более случайный вывод", @@ -173,6 +181,10 @@ const ru: LocaleType = { Store: { DefaultTopic: "Новый разговор", BotHello: "Здравствуйте! Как я могу вам помочь сегодня?", + BotHelloWithCommand: (command: string) => + "Здравствуйте! Как я могу вам помочь сегодня?\n Чтобы создать изображения, используйте `" + + command + + " {ключевое слово}.`", Error: "Что-то пошло не так. Пожалуйста, попробуйте еще раз позже.", Prompt: { History: (content: string) => @@ -186,7 +198,8 @@ const ru: LocaleType = { }, Copy: { Success: "Скопировано в буфер обмена", - Failed: "Не удалось скопировать, пожалуйста, предоставьте разрешение на доступ к буферу обмена", + Failed: + "Не удалось скопировать, пожалуйста, предоставьте разрешение на доступ к буферу обмена", }, Context: { Toast: (x: any) => `С ${x} контекстными подсказками`, @@ -214,7 +227,9 @@ const ru: LocaleType = { }, EditModal: { Title: (readonly: boolean) => - `Редактирование шаблона подсказки ${readonly ? "(только для чтения)" : ""}`, + `Редактирование шаблона подсказки ${ + readonly ? "(только для чтения)" : "" + }`, Download: "Скачать", Clone: "Клонировать", }, @@ -230,7 +245,8 @@ const ru: LocaleType = { SubTitle: "Общайтесь с душой за маской", More: "Найти еще", NotShow: "Не показывать снова", - ConfirmNoShow: "Подтвердите отключение? Вы можете включить это позже в настройках.", + ConfirmNoShow: + "Подтвердите отключение? Вы можете включить это позже в настройках.", }, UI: { diff --git a/app/locales/tr.ts b/app/locales/tr.ts index 42c3f78ebd1..83207086e3e 100644 --- a/app/locales/tr.ts +++ b/app/locales/tr.ts @@ -156,6 +156,13 @@ const tr: LocaleType = { Placeholder: "Erişim Kodu Gerekiyor", }, Model: "Model", + ImageModel: { + Title: "Resim Modeli", + Model: "DALL-E Modeli", + Command: "Resim Oluşturma Komutu", + CountLimit: "Resim Oluşturma Limiti", + Size: "Resim Oluşturma Boyutu", + }, Temperature: { Title: "Gerçeklik", SubTitle: @@ -175,6 +182,10 @@ const tr: LocaleType = { Store: { DefaultTopic: "Yeni Konuşma", BotHello: "Merhaba! Size bugün nasıl yardımcı olabilirim?", + BotHelloWithCommand: (command: string) => + "Merhaba! Size bugün nasıl yardımcı olabilirim?\n Resim oluşturmak için, `" + + command + + " {anahtar kelime}.` kullanın", Error: "Bir şeyler yanlış gitti. Lütfen daha sonra tekrar deneyiniz.", Prompt: { History: (content: string) => diff --git a/app/locales/tw.ts b/app/locales/tw.ts index a8dbf91a5ed..4534c135aa2 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -153,6 +153,13 @@ const tw: LocaleType = { Placeholder: "請輸入授權碼", }, Model: "模型 (model)", + ImageModel: { + Title: "圖片模型", + Model: "DALL-E模型", + Command: "生成圖片指令", + CountLimit: "圖片生成次數限制", + Size: "圖片生成尺寸", + }, Temperature: { Title: "隨機性 (temperature)", SubTitle: "值越大,回應越隨機", @@ -169,6 +176,8 @@ const tw: LocaleType = { Store: { DefaultTopic: "新的對話", BotHello: "請問需要我的協助嗎?", + BotHelloWithCommand: (command: string) => + "請問需要我的協助嗎?\n 若要生成圖片,請使用 `" + command + " {關鍵字}.`", Error: "出錯了,請稍後再嘗試", Prompt: { History: (content: string) => diff --git a/app/locales/vi.ts b/app/locales/vi.ts index d3be61058d9..d16cfb4d1b9 100644 --- a/app/locales/vi.ts +++ b/app/locales/vi.ts @@ -155,6 +155,13 @@ const vi: LocaleType = { Placeholder: "Nhập mã truy cập", }, Model: "Mô hình", + ImageModel: { + Title: "Mô hình hình ảnh", + Model: "Mô hình DALL-E", + Command: "Lệnh tạo hình ảnh", + CountLimit: "Giới hạn số lượng hình ảnh tạo ra", + Size: "Kích thước hình ảnh được tạo ra", + }, Temperature: { Title: "Tính ngẫu nhiên (temperature)", SubTitle: "Giá trị càng lớn, câu trả lời càng ngẫu nhiên", @@ -171,6 +178,10 @@ const vi: LocaleType = { Store: { DefaultTopic: "Cuộc trò chuyện mới", BotHello: "Xin chào! Mình có thể giúp gì cho bạn?", + BotHelloWithCommand: (command: string) => + "Xin chào! Mình có thể giúp gì cho bạn?\n Để tạo hình ảnh, hãy sử dụng `" + + command + + " {từ khóa}.`", Error: "Có lỗi xảy ra, vui lòng thử lại sau.", Prompt: { History: (content: string) => diff --git a/app/requests.ts b/app/requests.ts index d576de0502b..75b4d4c3a17 100644 --- a/app/requests.ts +++ b/app/requests.ts @@ -156,8 +156,7 @@ const makeImageRequestParam = ( ): CreateImageRequest => { // Set default values const defaultOptions: Omit = { - n: 4, - size: CreateImageRequestSizeEnum._512x512, + n: useAppConfig.getState().imageModelConfig.imageLimit, response_format: CreateImageRequestResponseFormatEnum.Url, user: "default_user", }; diff --git a/app/store/access.ts b/app/store/access.ts index 79b7b9900b2..15e0bcdd72a 100644 --- a/app/store/access.ts +++ b/app/store/access.ts @@ -1,7 +1,6 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; import { StoreKey } from "../constant"; -import { BOT_HELLO } from "./chat"; import { ALL_MODELS } from "./config"; export interface AccessControlStore { @@ -69,9 +68,9 @@ export const useAccessStore = create()( }); } - if ((res as any).botHello) { - BOT_HELLO.content = (res as any).botHello; - } + // if ((res as any).botHello) { + // BOT_HELLO.content = (res as any).botHello; + // } }) .catch(() => { console.error("[Config] failed to fetch config"); diff --git a/app/store/chat.ts b/app/store/chat.ts index bea33d9ae94..39cbdf7098e 100644 --- a/app/store/chat.ts +++ b/app/store/chat.ts @@ -57,17 +57,22 @@ export interface ChatSession { stat: ChatStat; lastUpdate: number; lastSummarizeIndex: number; - + botHello: Message; mask: Mask; } export const DEFAULT_TOPIC = Locale.Store.DefaultTopic; -export const BOT_HELLO: Message = createMessage({ +const BOT_HELLO: Message = createMessage({ role: "assistant", content: Locale.Store.BotHello, }); +const createBotHelloWithCommand = (command: string): Message => { + BOT_HELLO.content = Locale.Store.BotHelloWithCommand(command); + return BOT_HELLO; +}; function createEmptySession(): ChatSession { + const mask = createEmptyMask(); return { id: Date.now() + Math.random(), topic: DEFAULT_TOPIC, @@ -80,7 +85,8 @@ function createEmptySession(): ChatSession { }, lastUpdate: Date.now(), lastSummarizeIndex: 0, - mask: createEmptyMask(), + mask: mask, + botHello: createBotHelloWithCommand(mask.imageModelConfig.command), }; } @@ -245,6 +251,7 @@ export const useChatStore = create()( async onUserInput(content) { const session = get().currentSession(); const modelConfig = session.mask.modelConfig; + const imageModelConfig = session.mask.imageModelConfig; const userMessage: Message = createMessage({ role: "user", @@ -281,8 +288,15 @@ export const useChatStore = create()( session.messages.push(botMessage); }); - if (userMessage.content.startsWith("/image")) { - const keyword = userMessage.content.substring("/image".length); + if ( + userMessage.content + .trim() + .toLowerCase() + .startsWith(imageModelConfig.command.toLowerCase()) + ) { + const keyword = userMessage.content.substring( + imageModelConfig.command.toLowerCase().length, + ); console.log("keyword", keyword); requestImage(keyword, { onMessage(content, images, image_alt, done) { diff --git a/app/store/config.ts b/app/store/config.ts index ddc8ef1c7e0..4807a470de3 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -1,6 +1,8 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; -import { StoreKey } from "../constant"; +import { ImageRequestSizeEnum } from "../api/openai/typing"; +import { COMMAND_IMAGE, StoreKey } from "../constant"; +import { CreateImageRequestSizeEnum } from "openai"; export enum SubmitKey { Enter = "Enter", @@ -38,6 +40,11 @@ export const DEFAULT_CONFIG = { historyMessageCount: 4, compressMessageLengthThreshold: 1000, }, + imageModelConfig: { + imageLimit: 1, + command: COMMAND_IMAGE, + size: "256x256" as ImageRequestSizeEnum, + }, }; export type ChatConfig = typeof DEFAULT_CONFIG; @@ -48,7 +55,7 @@ export type ChatConfigStore = ChatConfig & { }; export type ModelConfig = ChatConfig["modelConfig"]; - +export type ImageModelConfig = ChatConfig["imageModelConfig"]; const ENABLE_GPT4 = true; export const ALL_MODELS = [ @@ -133,7 +140,19 @@ export const ModalConfigValidator = { return limitNumber(x, 0, 1, 1); }, }; - +export const ImageModalConfigValidator = { + size: (value: string): ImageRequestSizeEnum => { + const validSizes = Object.values( + CreateImageRequestSizeEnum, + ) as unknown as ImageRequestSizeEnum[]; + if (validSizes.includes(value as ImageRequestSizeEnum)) { + return value as ImageRequestSizeEnum; + } else { + console.warn(`Invalid size: ${value}. Defaulting to "256x256".`); + return "256x256"; + } + }, +}; export const useAppConfig = create()( persist( (set, get) => ({ diff --git a/app/store/mask.ts b/app/store/mask.ts index 98bd4702113..df4b0c4301e 100644 --- a/app/store/mask.ts +++ b/app/store/mask.ts @@ -3,15 +3,20 @@ import { persist } from "zustand/middleware"; import { BUILTIN_MASKS } from "../masks"; import { getLang, Lang } from "../locales"; import { DEFAULT_TOPIC, Message } from "./chat"; -import { ModelConfig, ModelType, useAppConfig } from "./config"; import { StoreKey } from "../constant"; - +import { + ImageModelConfig, + ModelConfig, + ModelType, + useAppConfig, +} from "./config"; export type Mask = { id: number; avatar: string; name: string; context: Message[]; modelConfig: ModelConfig; + imageModelConfig: ImageModelConfig; lang: Lang; builtin: boolean; }; @@ -40,6 +45,7 @@ export const createEmptyMask = () => name: DEFAULT_TOPIC, context: [], modelConfig: { ...useAppConfig.getState().modelConfig }, + imageModelConfig: { ...useAppConfig.getState().imageModelConfig }, lang: getLang(), builtin: false, } as Mask);