From d41ec2a89c96ac2e54238d030eefa48715ca3324 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Sun, 30 Jun 2024 20:45:06 +0530 Subject: [PATCH] feat: Add Chrome AI support --- src/assets/locale/en/chrome.json | 13 ++++ src/assets/locale/en/settings.json | 3 + src/assets/locale/es/chrome.json | 13 ++++ src/assets/locale/fr/chrome.json | 13 ++++ src/assets/locale/it/chrome.json | 13 ++++ src/assets/locale/ja-JP/chrome.json | 13 ++++ src/assets/locale/ml/chrome.json | 13 ++++ src/assets/locale/pt-BR/chrome.json | 13 ++++ src/assets/locale/ru/chrome.json | 13 ++++ src/assets/locale/zh/chrome.json | 13 ++++ src/components/Common/Markdown.tsx | 10 ++- src/components/Common/ModelSelect.tsx | 9 ++- src/components/Common/Playground/Message.tsx | 6 +- src/components/Common/ProviderIcon.tsx | 17 +++++ src/components/Layouts/Header.tsx | 6 +- .../Layouts/SettingsOptionLayout.tsx | 22 +++++- src/components/Option/Settings/chrome.tsx | 73 +++++++++++++++++++ .../Option/Settings/general-settings.tsx | 3 - src/hooks/useMessage.tsx | 12 +-- src/hooks/useMessageOption.tsx | 13 ++-- src/i18n/lang/en.ts | 4 +- src/i18n/lang/es.ts | 4 +- src/i18n/lang/fr.ts | 4 +- src/i18n/lang/it.ts | 4 +- src/i18n/lang/ja.ts | 4 +- src/i18n/lang/ml.ts | 4 +- src/i18n/lang/pt.ts | 4 +- src/i18n/lang/ru.ts | 4 +- src/i18n/lang/zh.ts | 4 +- src/models/ChatChromeAi.ts | 16 ++-- src/models/index.ts | 41 +++++++++++ src/routes/chrome.tsx | 2 + src/routes/option-settings-chrome.tsx | 15 ++++ src/services/chrome.ts | 38 ++++++++++ src/services/ollama.ts | 41 +++++++++-- src/utils/chrome.ts | 30 ++++++++ 36 files changed, 463 insertions(+), 47 deletions(-) create mode 100644 src/assets/locale/en/chrome.json create mode 100644 src/assets/locale/es/chrome.json create mode 100644 src/assets/locale/fr/chrome.json create mode 100644 src/assets/locale/it/chrome.json create mode 100644 src/assets/locale/ja-JP/chrome.json create mode 100644 src/assets/locale/ml/chrome.json create mode 100644 src/assets/locale/pt-BR/chrome.json create mode 100644 src/assets/locale/ru/chrome.json create mode 100644 src/assets/locale/zh/chrome.json create mode 100644 src/components/Common/ProviderIcon.tsx create mode 100644 src/components/Option/Settings/chrome.tsx create mode 100644 src/models/index.ts create mode 100644 src/routes/option-settings-chrome.tsx create mode 100644 src/services/chrome.ts create mode 100644 src/utils/chrome.ts diff --git a/src/assets/locale/en/chrome.json b/src/assets/locale/en/chrome.json new file mode 100644 index 0000000..88b0754 --- /dev/null +++ b/src/assets/locale/en/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Configure Chrome AI", + "status": { + "label": "Enable or Disable Chrome AI Support on Page Assist" + }, + "error": { + "browser_not_supported": "This version of Chrome is not supported by the Gemini Nano model. Please update to version 127 or later", + "ai_not_supported": "The setting chrome://flags/#prompt-api-for-gemini-nano is not enabled. Please enable it.", + "ai_not_ready": "Gemini Nano is not ready yet; you need to double-check Chrome settings.", + "internal_error": "An internal error occurred. Please try again later." + }, + "errorDescription": "In order to use Chrome AI, you need a browser version greater than 127, which is currently in the Dev and Canary channels. After downloading the supported version, follow these steps:\n\n1. Go to `chrome://flags/#prompt-api-for-gemini-nano` and select \"Enable\".\n2. Go to `chrome://flags/#optimization-guide-on-device-model` and select \"EnabledBypassPrefRequirement\".\n3. Go to `chrome://components`, search for \"Optimization Guide On Device Model\", and click \"Check for Update\". This will download the model. If you don't see the settings, repeat steps 1 and 2 and relaunch your browser." +} \ No newline at end of file diff --git a/src/assets/locale/en/settings.json b/src/assets/locale/en/settings.json index 4da5e8c..fb798d2 100644 --- a/src/assets/locale/en/settings.json +++ b/src/assets/locale/en/settings.json @@ -300,5 +300,8 @@ "webSearchFollowUpPromptError": "Please input your Web Search Follow Up Prompt!", "webSearchFollowUpPromptPlaceholder": "Your Web Search Follow Up Prompt" } + }, + "chromeAiSettings": { + "title": "Chrome AI Settings" } } \ No newline at end of file diff --git a/src/assets/locale/es/chrome.json b/src/assets/locale/es/chrome.json new file mode 100644 index 0000000..76238cb --- /dev/null +++ b/src/assets/locale/es/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Configurar Chrome AI", + "status": { + "label": "Habilitar o deshabilitar el soporte de Chrome AI en Page Assist" + }, + "error": { + "browser_not_supported": "Esta versión de Chrome no es compatible con el modelo Gemini Nano. Por favor, actualice a la versión 127 o posterior.", + "ai_not_supported": "La configuración chrome://flags/#prompt-api-for-gemini-nano no está habilitada. Por favor, habilítela.", + "ai_not_ready": "Gemini Nano aún no está listo; necesita verificar la configuración de Chrome.", + "internal_error": "Ocurrió un error interno. Por favor, inténtelo de nuevo más tarde." + }, + "errorDescription": "Para usar Chrome AI, necesita una versión del navegador mayor a la 127, que actualmente está en los canales Dev y Canary. Después de descargar la versión compatible, siga estos pasos:\n\n1. Vaya a `chrome://flags/#prompt-api-for-gemini-nano` y seleccione \"Habilitar\".\n2. Vaya a `chrome://flags/#optimization-guide-on-device-model` y seleccione \"EnabledBypassPrefRequirement\".\n3. Vaya a `chrome://components`, busque \"Optimization Guide On Device Model\" y haga clic en \"Buscar actualización\". Esto descargará el modelo. Si no ve la configuración, repita los pasos 1 y 2 y reinicie su navegador." +} diff --git a/src/assets/locale/fr/chrome.json b/src/assets/locale/fr/chrome.json new file mode 100644 index 0000000..cb2d974 --- /dev/null +++ b/src/assets/locale/fr/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Configurer Chrome AI", + "status": { + "label": "Activer ou désactiver la prise en charge de Chrome AI sur Page Assist" + }, + "error": { + "browser_not_supported": "Cette version de Chrome n'est pas supportée par le modèle Gemini Nano. Veuillez mettre à jour vers la version 127 ou ultérieure.", + "ai_not_supported": "Le paramètre chrome://flags/#prompt-api-for-gemini-nano n'est pas activé. Veuillez l'activer.", + "ai_not_ready": "Gemini Nano n'est pas encore prêt; vous devez vérifier les paramètres de Chrome.", + "internal_error": "Une erreur interne est survenue. Veuillez réessayer plus tard." + }, + "errorDescription": "Pour utiliser Chrome AI, vous avez besoin d'une version du navigateur supérieure à 127, actuellement disponible dans les canaux Dev et Canary. Après avoir téléchargé la version prise en charge, suivez ces étapes:\n\n1. Allez à `chrome://flags/#prompt-api-for-gemini-nano` et sélectionnez \"Activer\".\n2. Allez à `chrome://flags/#optimization-guide-on-device-model` et sélectionnez \"EnabledBypassPrefRequirement\".\n3. Allez à `chrome://components`, recherchez \"Optimization Guide On Device Model\" et cliquez sur \"Vérifier la mise à jour\". Cela téléchargera le modèle. Si vous ne voyez pas les paramètres, répétez les étapes 1 et 2 et relancez votre navigateur." +} diff --git a/src/assets/locale/it/chrome.json b/src/assets/locale/it/chrome.json new file mode 100644 index 0000000..86d6c68 --- /dev/null +++ b/src/assets/locale/it/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Configura Chrome AI", + "status": { + "label": "Abilita o disabilita il supporto di Chrome AI su Page Assist" + }, + "error": { + "browser_not_supported": "Questa versione di Chrome non è supportata dal modello Gemini Nano. Si prega di aggiornare alla versione 127 o successiva.", + "ai_not_supported": "L'impostazione chrome://flags/#prompt-api-for-gemini-nano non è abilitata. Si prega di abilitarla.", + "ai_not_ready": "Gemini Nano non è ancora pronto; è necessario verificare le impostazioni di Chrome.", + "internal_error": "Si è verificato un errore interno. Si prega di riprovare più tardi." + }, + "errorDescription": "Per utilizzare Chrome AI, è necessaria una versione del browser superiore alla 127, attualmente disponibile nei canali Dev e Canary. Dopo aver scaricato la versione supportata, segui questi passaggi:\n\n1. Vai a `chrome://flags/#prompt-api-for-gemini-nano` e seleziona \"Abilita\".\n2. Vai a `chrome://flags/#optimization-guide-on-device-model` e seleziona \"EnabledBypassPrefRequirement\".\n3. Vai a `chrome://components`, cerca \"Optimization Guide On Device Model\" e clicca su \"Controlla aggiornamenti\". Questo scaricherà il modello. Se non vedi le impostazioni, ripeti i passaggi 1 e 2 e riavvia il browser." +} diff --git a/src/assets/locale/ja-JP/chrome.json b/src/assets/locale/ja-JP/chrome.json new file mode 100644 index 0000000..f6205bd --- /dev/null +++ b/src/assets/locale/ja-JP/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Chrome AIの設定", + "status": { + "label": "Page AssistでChrome AIサポートを有効または無効にする" + }, + "error": { + "browser_not_supported": "このバージョンのChromeはGemini Nanoモデルに対応していません。バージョン127以降に更新してください。", + "ai_not_supported": "設定chrome://flags/#prompt-api-for-gemini-nanoが有効になっていません。有効にしてください。", + "ai_not_ready": "Gemini Nanoはまだ準備ができていません。Chromeの設定を再確認する必要があります。", + "internal_error": "内部エラーが発生しました。後でもう一度お試しください。" + }, + "errorDescription": "Chrome AIを使用するには、現在DevおよびCanaryチャンネルにあるバージョン127以上のブラウザが必要です。サポートされているバージョンをダウンロードした後、次の手順に従ってください:\n\n1. `chrome://flags/#prompt-api-for-gemini-nano`にアクセスし、「有効」を選択します。\n2. `chrome://flags/#optimization-guide-on-device-model`にアクセスし、「EnabledBypassPrefRequirement」を選択します。\n3. `chrome://components`にアクセスし、「Optimization Guide On Device Model」を検索して、「アップデートを確認」をクリックします。これにより、モデルがダウンロードされます。設定が表示されない場合は、手順1および2を繰り返し、ブラウザを再起動してください。" +} diff --git a/src/assets/locale/ml/chrome.json b/src/assets/locale/ml/chrome.json new file mode 100644 index 0000000..0b829f1 --- /dev/null +++ b/src/assets/locale/ml/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "ക്രോം എഐ കോൺഫിഗർ ചെയ്യുക", + "status": { + "label": "പേജ് അസിസ്റ്റിൽ ക്രോം എഐ പിന്തുണ സജ്ജമാക്കുക അല്ലെങ്കിൽ ഡിസബിൾ ചെയ്യുക" + }, + "error": { + "browser_not_supported": "ക്രോത്തിന്റെ ഈ പതിപ്പ് ജെമിനി നാനോ മോഡലിനെ പിന്തുണയ്ക്കുന്നില്ല. പതിപ്പ് 127 അല്ലെങ്കിൽ അതിനുശേഷം അപ്ഡേറ്റ് ചെയ്യുക.", + "ai_not_supported": "ക്രമീകരണം chrome://flags/#prompt-api-for-gemini-nano സജ്ജമാക്കപ്പെട്ടിട്ടില്ല. ദയവായി അതിനെ സജ്ജമാക്കുക.", + "ai_not_ready": "ജെമിനി നാനോ ഇപ്പോഴും സജ്ജമല്ല; നിങ്ങൾ ക്രോം ക്രമീകരണങ്ങൾ രണ്ടുതവണ പരിശോധിക്കണം.", + "internal_error": "ഒരു ആന്തരിക പിശക് സംഭവിച്ചു. ദയവായി കുറച്ചുദിവസം ശേഷം വീണ്ടും ശ്രമിക്കുക." + }, + "errorDescription": "ക്രോം എഐ ഉപയോഗിക്കുന്നതിന്, നിലവിൽ ഡെവ്, കാനറി ചാനലുകളിൽ ലഭ്യമായ 127-നെക്കാൾ ഉയർന്ന ബ്രൗസർ പതിപ്പ് ആവശ്യമാണ്. പിന്തുണയ്ക്കുന്ന പതിപ്പ് ഡൗൺലോഡ് ചെയ്ത ശേഷം, ഈ ചുവടുപടികൾ പിന്തുടരുക:\n\n1. `chrome://flags/#prompt-api-for-gemini-nano`-ലേക്ക് പോയി \"എനേബിൾ\" തിരഞ്ഞെടുക്കുക.\n2. `chrome://flags/#optimization-guide-on-device-model`-ലേക്ക് പോയി \"EnabledBypassPrefRequirement\" തിരഞ്ഞെടുക്കുക.\n3. `chrome://components`-ലേക്ക് പോയി, \"Optimization Guide On Device Model\" തിരയുക, \"അപ്ഡേറ്റ് പരിശോധിക്കുക\" ക്ലിക്ക് ചെയ്യുക. ഇത് മോഡൽ ഡൗൺലോഡ് ചെയ്യും. ക്രമീകരണങ്ങൾ കാണുന്നില്ലെങ്കിൽ, ചുവടുപടികൾ 1, 2 ആവർത്തിച്ച് ബ്രൗസർ വീണ്ടും തുറക്കുക." +} diff --git a/src/assets/locale/pt-BR/chrome.json b/src/assets/locale/pt-BR/chrome.json new file mode 100644 index 0000000..c589dfd --- /dev/null +++ b/src/assets/locale/pt-BR/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Configurar IA do Chrome", + "status": { + "label": "Ativar ou Desativar o Suporte de IA do Chrome no Assistente de Página" + }, + "error": { + "browser_not_supported": "Esta versão do Chrome não é suportada pelo modelo Gemini Nano. Por favor, atualize para a versão 127 ou posterior", + "ai_not_supported": "A configuração chrome://flags/#prompt-api-for-gemini-nano não está ativada. Por favor, ative-a.", + "ai_not_ready": "O Gemini Nano ainda não está pronto; você precisa verificar novamente as configurações do Chrome.", + "internal_error": "Ocorreu um erro interno. Por favor, tente novamente mais tarde." + }, + "errorDescription": "Para usar a IA do Chrome, você precisa de uma versão do navegador superior a 127, que atualmente está nos canais Dev e Canary. Após baixar a versão suportada, siga estes passos:\n\n1. Vá para `chrome://flags/#prompt-api-for-gemini-nano` e selecione \"Ativar\".\n2. Vá para `chrome://flags/#optimization-guide-on-device-model` e selecione \"EnabledBypassPrefRequirement\".\n3. Vá para `chrome://components`, procure por \"Optimization Guide On Device Model\" e clique em \"Verificar atualizações\". Isso baixará o modelo. Se você não vir as configurações, repita os passos 1 e 2 e reinicie seu navegador." +} \ No newline at end of file diff --git a/src/assets/locale/ru/chrome.json b/src/assets/locale/ru/chrome.json new file mode 100644 index 0000000..187257c --- /dev/null +++ b/src/assets/locale/ru/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "Настройка Chrome AI", + "status": { + "label": "Включить или отключить поддержку Chrome AI в помощнике страницы" + }, + "error": { + "browser_not_supported": "Эта версия Chrome не поддерживается моделью Gemini Nano. Пожалуйста, обновите до версии 127 или выше", + "ai_not_supported": "Настройка chrome://flags/#prompt-api-for-gemini-nano не включена. Пожалуйста, включите её.", + "ai_not_ready": "Gemini Nano ещё не готов; вам нужно перепроверить настройки Chrome.", + "internal_error": "Произошла внутренняя ошибка. Пожалуйста, повторите попытку позже." + }, + "errorDescription": "Чтобы использовать Chrome AI, вам нужна версия браузера выше 127, которая в настоящее время доступна в каналах Dev и Canary. После загрузки поддерживаемой версии выполните следующие шаги:\n\n1. Перейдите на `chrome://flags/#prompt-api-for-gemini-nano` и выберите \"Включить\".\n2. Перейдите на `chrome://flags/#optimization-guide-on-device-model` и выберите \"EnabledBypassPrefRequirement\".\n3. Перейдите на `chrome://components`, найдите \"Optimization Guide On Device Model\" и нажмите \"Проверить наличие обновлений\". Это загрузит модель. Если вы не видите настройки, повторите шаги 1 и 2 и перезапустите браузер." +} \ No newline at end of file diff --git a/src/assets/locale/zh/chrome.json b/src/assets/locale/zh/chrome.json new file mode 100644 index 0000000..b41a3ac --- /dev/null +++ b/src/assets/locale/zh/chrome.json @@ -0,0 +1,13 @@ +{ + "heading": "配置Chrome人工智能", + "status": { + "label": "在页面辅助功能中启用或禁用Chrome人工智能支持" + }, + "error": { + "browser_not_supported": "此版本的Chrome不受Gemini Nano模型支持。请更新到127版本或更高版本", + "ai_not_supported": "设置chrome://flags/#prompt-api-for-gemini-nano未启用。请启用它。", + "ai_not_ready": "Gemini Nano尚未准备就绪;您需要再次检查Chrome设置。", + "internal_error": "发生内部错误。请稍后重试。" + }, + "errorDescription": "为了使用Chrome人工智能,您需要127版本以上的浏览器,目前该版本在Dev和Canary渠道中。下载支持的版本后,请按照以下步骤操作:\n\n1. 前往`chrome://flags/#prompt-api-for-gemini-nano`并选择\"启用\",\n2. 前往`chrome://flags/#optimization-guide-on-device-model`并选择\"EnabledBypassPrefRequirement\",\n3. 前往`chrome://components`,搜索\"Optimization Guide On Device Model\",然后点击\"检查更新\"。这将下载模型。如果您没有看到这些设置,请重复步骤1和2,然后重新启动浏览器。" +} \ No newline at end of file diff --git a/src/components/Common/Markdown.tsx b/src/components/Common/Markdown.tsx index c736c59..149716a 100644 --- a/src/components/Common/Markdown.tsx +++ b/src/components/Common/Markdown.tsx @@ -6,11 +6,17 @@ import "property-information" import React from "react" import { CodeBlock } from "./CodeBlock" -export default function Markdown({ message }: { message: string }) { +export default function Markdown({ + message, + className = "prose break-words dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 dark:prose-dark" +}: { + message: string + className?: string +}) { return ( { const { t } = useTranslation("common") @@ -29,7 +29,10 @@ export const ModelSelect: React.FC = () => { label: (
- +
{d.name}
@@ -53,7 +56,7 @@ export const ModelSelect: React.FC = () => { trigger={["click"]}> diff --git a/src/components/Common/Playground/Message.tsx b/src/components/Common/Playground/Message.tsx index de18540..c23c90e 100644 --- a/src/components/Common/Playground/Message.tsx +++ b/src/components/Common/Playground/Message.tsx @@ -64,7 +64,11 @@ export const PlaygroundMessage = (props: Props) => {
- {props.isBot ? props.name : "You"} + {props.isBot + ? props.name === "chrome::gemini-nano::page-assist" + ? "Gemini Nano" + : props.name + : "You"} {props.isBot && diff --git a/src/components/Common/ProviderIcon.tsx b/src/components/Common/ProviderIcon.tsx new file mode 100644 index 0000000..a97776f --- /dev/null +++ b/src/components/Common/ProviderIcon.tsx @@ -0,0 +1,17 @@ +import { ChromeIcon } from "lucide-react" +import { OllamaIcon } from "../Icons/Ollama" + +export const ProviderIcons = ({ + provider, + className +}: { + provider: string + className?: string +}) => { + switch (provider) { + case "chrome": + return + default: + return + } +} diff --git a/src/components/Layouts/Header.tsx b/src/components/Layouts/Header.tsx index 1d6099a..f7251b1 100644 --- a/src/components/Layouts/Header.tsx +++ b/src/components/Layouts/Header.tsx @@ -21,6 +21,7 @@ import { useMessageOption } from "~/hooks/useMessageOption" import { Select, Tooltip } from "antd" import { getAllPrompts } from "@/db" import { ShareBtn } from "~/components/Common/ShareBtn" +import { ProviderIcons } from "../Common/ProviderIcon" type Props = { setSidebarOpen: (open: boolean) => void setOpenModelSettings: (open: boolean) => void @@ -132,7 +133,10 @@ export const Header: React.FC = ({ - + {model.name} ), diff --git a/src/components/Layouts/SettingsOptionLayout.tsx b/src/components/Layouts/SettingsOptionLayout.tsx index 9a76c34..ce96a52 100644 --- a/src/components/Layouts/SettingsOptionLayout.tsx +++ b/src/components/Layouts/SettingsOptionLayout.tsx @@ -6,11 +6,13 @@ import { BlocksIcon, InfoIcon, CombineIcon, + ChromeIcon } from "lucide-react" import { useTranslation } from "react-i18next" import { Link, useLocation } from "react-router-dom" import { OllamaIcon } from "../Icons/Ollama" import { Tag } from "antd" +import { BetaTag } from "../Common/Beta" function classNames(...classes: string[]) { return classes.filter(Boolean).join(" ") @@ -20,10 +22,12 @@ const LinkComponent = (item: { href: string name: string | JSX.Element icon: any - current: string + current: string, + beta?: boolean }) => { return ( -
  • +
  • + {item.name} + { + item.beta && + }
  • ) } @@ -65,7 +72,7 @@ export const SettingsLayout = ({ children }: { children: React.ReactNode }) => { icon={OrbitIcon} current={location.pathname} /> - { icon={OllamaIcon} current={location.pathname} /> + {import.meta.env.BROWSER === "chrome" && ( + + )} { + const { t } = useTranslation("chrome") + const [chromeAIStatus, setChromeAIStatus] = useStorage( + "chromeAIStatus", + false + ) + const [selectedModel, setSelectedModel] = useStorage("selectedModel") + + const { status, data } = useQuery({ + queryKey: ["fetchChromeAIInfo"], + queryFn: async () => { + const data = await getChromeAISupported() + return data + } + }) + return ( +
    + {status === "pending" && } + {status === "success" && ( +
    +
    +
    +

    + {t("heading")} +

    +
    +
    + +
    +
    + + {t("status.label")} + +
    + + { + setChromeAIStatus(value) + if ( + !value && + selectedModel === "chrome::gemini-nano::page-assist" + ) { + setSelectedModel(null) + } + }} + /> +
    + {data !== "success" && ( +
    + +
    + +
    +
    + )} +
    +
    + )} +
    + ) +} diff --git a/src/components/Option/Settings/general-settings.tsx b/src/components/Option/Settings/general-settings.tsx index d5d1c27..1b1e7cc 100644 --- a/src/components/Option/Settings/general-settings.tsx +++ b/src/components/Option/Settings/general-settings.tsx @@ -13,7 +13,6 @@ import { exportPageAssistData, importPageAssistData } from "@/libs/export-import" -import { BetaTag } from "@/components/Common/Beta" import { useStorage } from "@plasmohq/storage/hook" export const GeneralSettings = () => { @@ -87,7 +86,6 @@ export const GeneralSettings = () => {
    - {t("generalSettings.settings.copilotResumeLastChat.label")} @@ -99,7 +97,6 @@ export const GeneralSettings = () => {
    - {t("generalSettings.settings.hideCurrentChatModelSettings.label")} diff --git a/src/hooks/useMessage.tsx b/src/hooks/useMessage.tsx index 0b20edf..17a84a1 100644 --- a/src/hooks/useMessage.tsx +++ b/src/hooks/useMessage.tsx @@ -28,9 +28,9 @@ import { formatDocs } from "@/chain/chat-with-x" import { OllamaEmbeddingsPageAssist } from "@/models/OllamaEmbedding" import { useStorage } from "@plasmohq/storage/hook" import { useStoreChatModelSettings } from "@/store/model" -import { ChatOllama } from "@/models/ChatOllama" import { getAllDefaultModelSettings } from "@/services/model-settings" import { getSystemPromptForWeb } from "@/web/web" +import { pageAssistModel } from "@/models" export const useMessage = () => { const { @@ -98,7 +98,7 @@ export const useMessage = () => { const url = await getOllamaURL() const userDefaultModelSettings = await getAllDefaultModelSettings() - const ollama = new ChatOllama({ + const ollama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -225,7 +225,7 @@ export const useMessage = () => { const promptForQuestion = questionPrompt .replaceAll("{chat_history}", chat_history) .replaceAll("{question}", message) - const questionOllama = new ChatOllama({ + const questionOllama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -388,7 +388,7 @@ export const useMessage = () => { image = `data:image/jpeg;base64,${image.split(",")[1]}` } - const ollama = new ChatOllama({ + const ollama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -591,7 +591,7 @@ export const useMessage = () => { image = `data:image/jpeg;base64,${image.split(",")[1]}` } - const ollama = new ChatOllama({ + const ollama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -661,7 +661,7 @@ export const useMessage = () => { const promptForQuestion = questionPrompt .replaceAll("{chat_history}", chat_history) .replaceAll("{question}", message) - const questionOllama = new ChatOllama({ + const questionOllama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: diff --git a/src/hooks/useMessageOption.tsx b/src/hooks/useMessageOption.tsx index 40341ff..2b28aa7 100644 --- a/src/hooks/useMessageOption.tsx +++ b/src/hooks/useMessageOption.tsx @@ -31,7 +31,7 @@ import { useWebUI } from "@/store/webui" import { useStorage } from "@plasmohq/storage/hook" import { useStoreChatModelSettings } from "@/store/model" import { getAllDefaultModelSettings } from "@/services/model-settings" -import { ChatOllama } from "@/models/ChatOllama" +import { pageAssistModel } from "@/models" export const useMessageOption = () => { const { @@ -104,7 +104,7 @@ export const useMessageOption = () => { image = `data:image/jpeg;base64,${image.split(",")[1]}` } - const ollama = new ChatOllama({ + const ollama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -174,7 +174,7 @@ export const useMessageOption = () => { const promptForQuestion = questionPrompt .replaceAll("{chat_history}", chat_history) .replaceAll("{question}", message) - const questionOllama = new ChatOllama({ + const questionOllama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -347,7 +347,7 @@ export const useMessageOption = () => { image = `data:image/jpeg;base64,${image.split(",")[1]}` } - const ollama = new ChatOllama({ + const ollama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -463,6 +463,7 @@ export const useMessageOption = () => { signal: signal } ) + let count = 0 for await (const chunk of chunks) { contentToSave += chunk.content @@ -562,7 +563,7 @@ export const useMessageOption = () => { const url = await getOllamaURL() const userDefaultModelSettings = await getAllDefaultModelSettings() - const ollama = new ChatOllama({ + const ollama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: @@ -648,7 +649,7 @@ export const useMessageOption = () => { const promptForQuestion = questionPrompt .replaceAll("{chat_history}", chat_history) .replaceAll("{question}", message) - const questionOllama = new ChatOllama({ + const questionOllama = await pageAssistModel({ model: selectedModel!, baseUrl: cleanUrl(url), keepAlive: diff --git a/src/i18n/lang/en.ts b/src/i18n/lang/en.ts index 1522528..dcb0dc6 100644 --- a/src/i18n/lang/en.ts +++ b/src/i18n/lang/en.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/en/common.json"; import sidepanel from "@/assets/locale/en/sidepanel.json"; import settings from "@/assets/locale/en/settings.json"; import knowledge from "@/assets/locale/en/knowledge.json"; +import chrome from "@/assets/locale/en/chrome.json"; export const en = { option, @@ -11,5 +12,6 @@ export const en = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/es.ts b/src/i18n/lang/es.ts index c666286..0e04fa7 100644 --- a/src/i18n/lang/es.ts +++ b/src/i18n/lang/es.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/es/common.json"; import sidepanel from "@/assets/locale/es/sidepanel.json"; import settings from "@/assets/locale/es/settings.json"; import knowledge from "@/assets/locale/es/knowledge.json"; +import chrome from "@/assets/locale/es/chrome.json"; export const es = { option, @@ -11,5 +12,6 @@ export const es = { common, sidepanel, settings, - knowledge + knowledge, + chrome } diff --git a/src/i18n/lang/fr.ts b/src/i18n/lang/fr.ts index 9c741db..e6f49b7 100644 --- a/src/i18n/lang/fr.ts +++ b/src/i18n/lang/fr.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/fr/common.json"; import sidepanel from "@/assets/locale/fr/sidepanel.json"; import settings from "@/assets/locale/fr/settings.json"; import knowledge from "@/assets/locale/fr/knowledge.json"; +import chrome from "@/assets/locale/fr/chrome.json"; export const fr = { option, @@ -11,5 +12,6 @@ export const fr = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/it.ts b/src/i18n/lang/it.ts index 6be87df..b866c39 100644 --- a/src/i18n/lang/it.ts +++ b/src/i18n/lang/it.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/it/common.json"; import sidepanel from "@/assets/locale/it/sidepanel.json"; import settings from "@/assets/locale/it/settings.json"; import knowledge from "@/assets/locale/it/knowledge.json"; +import chrome from "@/assets/locale/it/chrome.json"; export const it = { option, @@ -11,5 +12,6 @@ export const it = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/ja.ts b/src/i18n/lang/ja.ts index 98f3a84..f4ff10e 100644 --- a/src/i18n/lang/ja.ts +++ b/src/i18n/lang/ja.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/ja-JP/common.json"; import sidepanel from "@/assets/locale/ja-JP/sidepanel.json"; import settings from "@/assets/locale/ja-JP/settings.json"; import knowledge from "@/assets/locale/ja-JP/knowledge.json"; +import chrome from "@/assets/locale/ja-JP/chrome.json"; export const ja = { @@ -12,5 +13,6 @@ export const ja = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/ml.ts b/src/i18n/lang/ml.ts index 70afc19..1b2bb45 100644 --- a/src/i18n/lang/ml.ts +++ b/src/i18n/lang/ml.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/ml/common.json"; import sidepanel from "@/assets/locale/ml/sidepanel.json"; import settings from "@/assets/locale/ml/settings.json"; import knowledge from "@/assets/locale/ml/knowledge.json"; +import chrome from "@/assets/locale/ml/chrome.json"; export const ml = { option, @@ -11,5 +12,6 @@ export const ml = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/pt.ts b/src/i18n/lang/pt.ts index c29f8f9..256568d 100644 --- a/src/i18n/lang/pt.ts +++ b/src/i18n/lang/pt.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/pt-BR/common.json"; import sidepanel from "@/assets/locale/pt-BR/sidepanel.json"; import settings from "@/assets/locale/pt-BR/settings.json"; import knowledge from "@/assets/locale/pt-BR/knowledge.json"; +import chrome from "@/assets/locale/pt-BR/chrome.json"; export const pt = { option, @@ -11,5 +12,6 @@ export const pt = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/ru.ts b/src/i18n/lang/ru.ts index 3887fa7..4dcb0f3 100644 --- a/src/i18n/lang/ru.ts +++ b/src/i18n/lang/ru.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/ru/common.json"; import sidepanel from "@/assets/locale/ru/sidepanel.json"; import settings from "@/assets/locale/ru/settings.json"; import knowledge from "@/assets/locale/ru/knowledge.json"; +import chrome from "@/assets/locale/ru/chrome.json"; export const ru = { option, @@ -11,5 +12,6 @@ export const ru = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/i18n/lang/zh.ts b/src/i18n/lang/zh.ts index 7381288..ad2249e 100644 --- a/src/i18n/lang/zh.ts +++ b/src/i18n/lang/zh.ts @@ -4,6 +4,7 @@ import common from "@/assets/locale/zh/common.json"; import sidepanel from "@/assets/locale/zh/sidepanel.json"; import settings from "@/assets/locale/zh/settings.json"; import knowledge from "@/assets/locale/zh/knowledge.json"; +import chrome from "@/assets/locale/zh/chrome.json"; export const zh = { @@ -12,5 +13,6 @@ export const zh = { common, sidepanel, settings, - knowledge + knowledge, + chrome } \ No newline at end of file diff --git a/src/models/ChatChromeAi.ts b/src/models/ChatChromeAi.ts index 6ed2426..de911d2 100644 --- a/src/models/ChatChromeAi.ts +++ b/src/models/ChatChromeAi.ts @@ -50,9 +50,16 @@ function formatPrompt(messages: BaseMessage[]): string { return messages .map((message) => { if (typeof message.content !== "string") { - throw new Error( - "ChatChromeAI does not support non-string message content." - ) + // console.log(message.content) + // throw new Error( + // "ChatChromeAI does not support non-string message content." + // ) + if (message.content.length > 0) { + //@ts-ignore + return message.content[0]?.text || "" + } + + return "" } return `${message._getType()}: ${message.content}` }) @@ -147,10 +154,9 @@ export class ChatChromeAI extends SimpleChatModel { runManager?: CallbackManagerForLLMRun ): AsyncGenerator { if (!this.session) { - throw new Error("Session not found. Please call `.initialize()` first.") + await this.initialize() } const textPrompt = this.promptFormatter(messages) - const stream = this.session.promptStreaming(textPrompt) const iterableStream = IterableReadableStream.fromReadableStream(stream) diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 0000000..d627a5d --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,41 @@ +import { ChatChromeAI } from "./ChatChromeAi" +import { ChatOllama } from "./ChatOllama" + +export const pageAssistModel = async ({ + model, + baseUrl, + keepAlive, + temperature, + topK, + topP, + numCtx, + seed +}: { + model: string + baseUrl: string + keepAlive: string + temperature: number + topK: number + topP: number + numCtx: number + seed: number +}) => { + switch (model) { + case "chrome::gemini-nano::page-assist": + return new ChatChromeAI({ + temperature, + topK + }) + default: + return new ChatOllama({ + baseUrl, + keepAlive, + temperature, + topK, + topP, + numCtx, + seed, + model + }) + } +} diff --git a/src/routes/chrome.tsx b/src/routes/chrome.tsx index 19c1031..4e78ba0 100644 --- a/src/routes/chrome.tsx +++ b/src/routes/chrome.tsx @@ -10,6 +10,7 @@ import OptionAbout from "./option-settings-about" import SidepanelChat from "./sidepanel-chat" import SidepanelSettings from "./sidepanel-settings" import OptionRagSettings from "./option-rag" +import OptionChrome from "./option-settings-chrome" export const OptionRoutingChrome = () => { return ( @@ -19,6 +20,7 @@ export const OptionRoutingChrome = () => { } /> } /> } /> + } /> } /> } /> } /> diff --git a/src/routes/option-settings-chrome.tsx b/src/routes/option-settings-chrome.tsx new file mode 100644 index 0000000..bc7f16e --- /dev/null +++ b/src/routes/option-settings-chrome.tsx @@ -0,0 +1,15 @@ +import { SettingsLayout } from "~/components/Layouts/SettingsOptionLayout" +import OptionLayout from "~/components/Layouts/Layout" +import { ChromeApp } from "@/components/Option/Settings/chrome" + +const OptionChrome = () => { + return ( + + + + + + ) +} + +export default OptionChrome diff --git a/src/services/chrome.ts b/src/services/chrome.ts new file mode 100644 index 0000000..dc7729b --- /dev/null +++ b/src/services/chrome.ts @@ -0,0 +1,38 @@ +import { Storage } from "@plasmohq/storage" + +const storage = new Storage() + +const DEFAULT_CHROME_AI_MODEL = { + name: "Gemini Nano", + model: "chrome::gemini-nano::page-assist", + modified_at: "", + provider: "chrome", + size: 0, + digest: "", + details: { + parent_model: "", + format: "", + family: "", + families: [], + parameter_size: "", + quantization_level: "" + } +} + +export const getChromeAIStatus = async (): Promise => { + const aiStatus = await storage.get("chromeAIStatus") + return aiStatus ?? false +} + +export const setChromeAIStatus = async (status: boolean): Promise => { + await storage.set("chromeAIStatus", status) +} + +export const getChromeAIModel = async () => { + const isEnable = await getChromeAIStatus() + if (isEnable) { + return [DEFAULT_CHROME_AI_MODEL] + } else { + return [] + } +} diff --git a/src/services/ollama.ts b/src/services/ollama.ts index be1da4f..b27f2cd 100644 --- a/src/services/ollama.ts +++ b/src/services/ollama.ts @@ -1,6 +1,7 @@ import { Storage } from "@plasmohq/storage" import { cleanUrl } from "../libs/clean-url" import { urlRewriteRuntime } from "../libs/runtime" +import { getChromeAIModel } from "./chrome" const storage = new Storage() @@ -144,6 +145,7 @@ export const deleteModel = async (model: string) => { return response.json() } + export const fetchChatModels = async ({ returnEmpty = false }: { @@ -174,15 +176,39 @@ export const fetchChatModels = async ({ quantization_level: string } }[] - return models?.filter((model) => { - return ( - !model?.details?.families?.includes("bert") && - !model?.details?.families?.includes("nomic-bert") - ) - }) + const chatModels = models + ?.filter((model) => { + return ( + !model?.details?.families?.includes("bert") && + !model?.details?.families?.includes("nomic-bert") + ) + }) + .map((model) => { + return { + ...model, + provider: "ollama" + } + }) + const chromeModel = await getChromeAIModel() + return [ + ...chatModels, + ...chromeModel + ] } catch (e) { console.error(e) - return await getAllModels({ returnEmpty }) + const allModels = await getAllModels({ returnEmpty }) + const models = allModels.map((model) => { + return { + ...model, + provider: "ollama" + } + }) + const chromeModel = await getChromeAIModel() + + return [ + ...models, + ...chromeModel + ] } } @@ -345,4 +371,3 @@ export const getPageShareUrl = async () => { export const setPageShareUrl = async (pageShareUrl: string) => { await storage.set("pageShareUrl", pageShareUrl) } - diff --git a/src/utils/chrome.ts b/src/utils/chrome.ts new file mode 100644 index 0000000..d18db81 --- /dev/null +++ b/src/utils/chrome.ts @@ -0,0 +1,30 @@ +export const getChromeAISupported = async () => { + try { + let browserInfo = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./) + let version = browserInfo ? parseInt(browserInfo[2], 10) : 0 + + if (version < 127) { + return "browser_not_supported" + } + + if (!("ai" in globalThis)) { + return "ai_not_supported" + } + + //@ts-ignore + const createSession = await ai?.canCreateGenericSession() + if (createSession !== "readily") { + return "ai_not_ready" + } + + return "success" + } catch (e) { + console.error(e) + return "internal_error" + } +} + +export const isChromeAISupported = async () => { + const result = await getChromeAISupported() + return result === "success" +}