Merge pull request #276 from n4ze3m/next

v1.3.9
This commit is contained in:
Muhammed Nazeem 2024-12-22 17:52:26 +05:30 committed by GitHub
commit 8d5ae2693a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 316 additions and 34 deletions

View File

@ -82,7 +82,7 @@ bun build:firefox
Once the extension is installed, you can open the sidebar via context menu or keyboard shortcut. Once the extension is installed, you can open the sidebar via context menu or keyboard shortcut.
Default Keyboard Shortcut: `Ctrl+Shift+P` Default Keyboard Shortcut: `Ctrl+Shift+Y`
### Web UI ### Web UI

View File

@ -74,6 +74,9 @@
"braveApi": { "braveApi": {
"label": "Brave API Key", "label": "Brave API Key",
"placeholder": "Enter your Brave API key" "placeholder": "Enter your Brave API key"
},
"googleDomain": {
"label": "Google Domain"
} }
}, },
"system": { "system": {

View File

@ -68,7 +68,7 @@ export const PlaygroundMessage = (props: Props) => {
)} )}
</div> </div>
</div> </div>
<div className="flex w-[calc(100%-50px)] flex-col gap-3 lg:w-[calc(100%-115px)]"> <div className="flex w-[calc(100%-50px)] flex-col gap-2 lg:w-[calc(100%-115px)]">
<span className="text-xs font-bold text-gray-800 dark:text-white"> <span className="text-xs font-bold text-gray-800 dark:text-white">
{props.isBot {props.isBot
? props.name === "chrome::gemini-nano::page-assist" ? props.name === "chrome::gemini-nano::page-assist"
@ -212,7 +212,9 @@ export const PlaygroundMessage = (props: Props) => {
{props.generationInfo && ( {props.generationInfo && (
<Popover <Popover
content={ content={
<GenerationInfo generationInfo={props.generationInfo} /> <GenerationInfo
generationInfo={props.generationInfo}
/>
} }
title={t("generationInfo")}> title={t("generationInfo")}>
<button className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"> <button className="flex items-center justify-center w-6 h-6 rounded-full bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">

View File

@ -7,6 +7,7 @@ import { OpenAiIcon } from "../Icons/OpenAI"
import { TogtherMonoIcon } from "../Icons/Togther" import { TogtherMonoIcon } from "../Icons/Togther"
import { OpenRouterIcon } from "../Icons/OpenRouter" import { OpenRouterIcon } from "../Icons/OpenRouter"
import { LLamaFile } from "../Icons/Llamafile" import { LLamaFile } from "../Icons/Llamafile"
import { GeminiIcon } from "../Icons/GeminiIcon"
export const ProviderIcons = ({ export const ProviderIcons = ({
provider, provider,
@ -34,6 +35,8 @@ export const ProviderIcons = ({
return <OpenRouterIcon className={className} /> return <OpenRouterIcon className={className} />
case "llamafile": case "llamafile":
return <LLamaFile className={className} /> return <LLamaFile className={className} />
case "gemini":
return <GeminiIcon className={className} />
default: default:
return <OllamaIcon className={className} /> return <OllamaIcon className={className} />
} }

View File

@ -0,0 +1,18 @@
import React from "react"
export const GeminiIcon = React.forwardRef<
SVGSVGElement,
React.SVGProps<SVGSVGElement>
>((props, ref) => {
return (
<svg
fill="currentColor"
fillRule="evenodd"
ref={ref}
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<path d="M12 24A14.304 14.304 0 000 12 14.304 14.304 0 0012 0a14.305 14.305 0 0012 12 14.305 14.305 0 00-12 12"></path>
</svg>
)
})

View File

@ -58,7 +58,7 @@ export const PlaygroundChat = () => {
/> />
))} ))}
{messages.length > 0 && ( {messages.length > 0 && (
<div className="w-full h-16 flex-shrink-0"></div> <div className="w-full h-10 flex-shrink-0"></div>
)} )}
</div> </div>
{!isAtBottom && ( {!isAtBottom && (

View File

@ -192,7 +192,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
return ( return (
<div <div
className={`px-3 pt-3 md:px-4 md:pt-4 bg-gray-100 dark:bg-[#262626] border rounded-t-xl dark:border-gray-600 className={`px-3 pt-3 bg-gray-100 dark:bg-[#262626] border rounded-t-xl dark:border-gray-600
${temporaryChat && "!bg-gray-300 dark:!bg-black "} ${temporaryChat && "!bg-gray-300 dark:!bg-black "}
`}> `}>
<div <div
@ -217,9 +217,10 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
</div> </div>
</div> </div>
<div> <div>
<div className={`flex rounded-t-xl bg-white dark:bg-transparent ${ <div
temporaryChat && "!bg-gray-300 dark:!bg-black" className={`flex rounded-t-xl bg-white dark:bg-transparent ${
}`}> temporaryChat && "!bg-gray-300 dark:!bg-black"
}`}>
<form <form
onSubmit={form.onSubmit(async (value) => { onSubmit={form.onSubmit(async (value) => {
stopListening() stopListening()
@ -275,17 +276,17 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
className="px-2 py-2 w-full resize-none bg-transparent focus-within:outline-none focus:ring-0 focus-visible:ring-0 ring-0 dark:ring-0 border-0 dark:text-gray-100" className="px-2 py-2 w-full resize-none bg-transparent focus-within:outline-none focus:ring-0 focus-visible:ring-0 ring-0 dark:ring-0 border-0 dark:text-gray-100"
onPaste={handlePaste} onPaste={handlePaste}
rows={1} rows={1}
style={{ minHeight: "40px" }} style={{ minHeight: "30px" }}
tabIndex={0} tabIndex={0}
placeholder={t("form.textarea.placeholder")} placeholder={t("form.textarea.placeholder")}
{...form.getInputProps("message")} {...form.getInputProps("message")}
/> />
<div className="mt-4 flex justify-between items-center"> <div className="mt-2 flex justify-between items-center">
<div className="flex"> <div className="flex">
{!selectedKnowledge && ( {!selectedKnowledge && (
<Tooltip title={t("tooltip.searchInternet")}> <Tooltip title={t("tooltip.searchInternet")}>
<div className="inline-flex items-center gap-2"> <div className="inline-flex items-center gap-2">
<PiGlobe className="h-5 w-5 dark:text-gray-300" /> <PiGlobe className={`h-5 w-5 dark:text-gray-300 `} />
<Switch <Switch
value={webSearch} value={webSearch}
onChange={(e) => setWebSearch(e)} onChange={(e) => setWebSearch(e)}

View File

@ -1,5 +1,6 @@
import { SaveButton } from "@/components/Common/SaveButton" import { SaveButton } from "@/components/Common/SaveButton"
import { getSearchSettings, setSearchSettings } from "@/services/search" import { getSearchSettings, setSearchSettings } from "@/services/search"
import { ALL_GOOGLE_DOMAINS } from "@/utils/google-domains"
import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider" import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
@ -18,6 +19,7 @@ export const SearchModeSettings = () => {
searxngURL: "", searxngURL: "",
searxngJSONMode: false, searxngJSONMode: false,
braveApiKey: "", braveApiKey: "",
googleDomain: ""
} }
}) })
@ -82,6 +84,32 @@ export const SearchModeSettings = () => {
</div> </div>
</> </>
)} )}
{form.values.searchProvider === "google" && (
<>
<div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between">
<span className="text-gray-700 dark:text-neutral-50">
{t("generalSettings.webSearch.googleDomain.label")}
</span>
<div>
<Select
showSearch
className="w-full mt-4 sm:mt-0 sm:w-[200px]"
options={ALL_GOOGLE_DOMAINS.map((e) => ({
label: e,
value: e
}))}
filterOption={(input, option) =>
option!.label.toLowerCase().indexOf(input.toLowerCase()) >=
0 ||
option!.value.toLowerCase().indexOf(input.toLowerCase()) >=
0
}
{...form.getInputProps("googleDomain")}
/>
</div>
</div>
</>
)}
{form.values.searchProvider === "brave-api" && ( {form.values.searchProvider === "brave-api" && (
<> <>
<div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between"> <div className="flex sm:flex-row flex-col space-y-4 sm:space-y-0 sm:justify-between">

View File

@ -0,0 +1,11 @@
import { ChatOpenAI } from "@langchain/openai";
export class ChatGoogleAI extends ChatOpenAI {
frequencyPenalty: number = undefined;
presencePenalty: number = undefined;
static lc_name() {
return "ChatGoogleAI";
}
}

View File

@ -4,6 +4,7 @@ import { ChatOllama } from "./ChatOllama"
import { getOpenAIConfigById } from "@/db/openai" import { getOpenAIConfigById } from "@/db/openai"
import { ChatOpenAI } from "@langchain/openai" import { ChatOpenAI } from "@langchain/openai"
import { urlRewriteRuntime } from "@/libs/runtime" import { urlRewriteRuntime } from "@/libs/runtime"
import { ChatGoogleAI } from "./ChatGoogleAI"
export const pageAssistModel = async ({ export const pageAssistModel = async ({
model, model,
@ -30,18 +31,15 @@ export const pageAssistModel = async ({
numPredict?: number numPredict?: number
useMMap?: boolean useMMap?: boolean
}) => { }) => {
if (model === "chrome::gemini-nano::page-assist") { if (model === "chrome::gemini-nano::page-assist") {
return new ChatChromeAI({ return new ChatChromeAI({
temperature, temperature,
topK, topK
}) })
} }
const isCustom = isCustomModel(model) const isCustom = isCustomModel(model)
if (isCustom) { if (isCustom) {
const modelInfo = await getModelInfo(model) const modelInfo = await getModelInfo(model)
const providerInfo = await getOpenAIConfigById(modelInfo.provider_id) const providerInfo = await getOpenAIConfigById(modelInfo.provider_id)
@ -50,6 +48,20 @@ export const pageAssistModel = async ({
await urlRewriteRuntime(providerInfo.baseUrl || "") await urlRewriteRuntime(providerInfo.baseUrl || "")
} }
if (providerInfo.provider === "gemini") {
return new ChatGoogleAI({
modelName: modelInfo.model_id,
openAIApiKey: providerInfo.apiKey || "temp",
temperature,
topP,
maxTokens: numPredict,
configuration: {
apiKey: providerInfo.apiKey || "temp",
baseURL: providerInfo.baseUrl || ""
}
}) as any
}
return new ChatOpenAI({ return new ChatOpenAI({
modelName: modelInfo.model_id, modelName: modelInfo.model_id,
openAIApiKey: providerInfo.apiKey || "temp", openAIApiKey: providerInfo.apiKey || "temp",
@ -58,13 +70,11 @@ export const pageAssistModel = async ({
maxTokens: numPredict, maxTokens: numPredict,
configuration: { configuration: {
apiKey: providerInfo.apiKey || "temp", apiKey: providerInfo.apiKey || "temp",
baseURL: providerInfo.baseUrl || "", baseURL: providerInfo.baseUrl || ""
}, }
}) as any }) as any
} }
return new ChatOllama({ return new ChatOllama({
baseUrl, baseUrl,
keepAlive, keepAlive,
@ -76,9 +86,6 @@ export const pageAssistModel = async ({
model, model,
numGpu, numGpu,
numPredict, numPredict,
useMMap, useMMap
}) })
} }

View File

@ -92,11 +92,21 @@ export const setBraveApiKey = async (braveApiKey: string) => {
await storage2.set("braveApiKey", braveApiKey) await storage2.set("braveApiKey", braveApiKey)
} }
export const getGoogleDomain = async () => {
const domain = await storage2.get("searchGoogleDomain")
return domain || "google.com"
}
export const setGoogleDomain = async (domain: string) => {
await storage2.set("searchGoogleDomain", domain)
}
export const getSearchSettings = async () => { export const getSearchSettings = async () => {
const [isSimpleInternetSearch, searchProvider, totalSearchResult, visitSpecificWebsite, const [isSimpleInternetSearch, searchProvider, totalSearchResult, visitSpecificWebsite,
searxngURL, searxngURL,
searxngJSONMode, searxngJSONMode,
braveApiKey braveApiKey,
googleDomain
] = ] =
await Promise.all([ await Promise.all([
getIsSimpleInternetSearch(), getIsSimpleInternetSearch(),
@ -105,7 +115,8 @@ export const getSearchSettings = async () => {
getIsVisitSpecificWebsite(), getIsVisitSpecificWebsite(),
getSearxngURL(), getSearxngURL(),
isSearxngJSONMode(), isSearxngJSONMode(),
getBraveApiKey() getBraveApiKey(),
getGoogleDomain()
]) ])
return { return {
@ -115,7 +126,8 @@ export const getSearchSettings = async () => {
visitSpecificWebsite, visitSpecificWebsite,
searxngURL, searxngURL,
searxngJSONMode, searxngJSONMode,
braveApiKey braveApiKey,
googleDomain
} }
} }
@ -126,7 +138,8 @@ export const setSearchSettings = async ({
visitSpecificWebsite, visitSpecificWebsite,
searxngJSONMode, searxngJSONMode,
searxngURL, searxngURL,
braveApiKey braveApiKey,
googleDomain
}: { }: {
isSimpleInternetSearch: boolean isSimpleInternetSearch: boolean
searchProvider: string searchProvider: string
@ -134,7 +147,8 @@ export const setSearchSettings = async ({
visitSpecificWebsite: boolean visitSpecificWebsite: boolean
searxngURL: string searxngURL: string
searxngJSONMode: boolean, searxngJSONMode: boolean,
braveApiKey: string braveApiKey: string,
googleDomain: string
}) => { }) => {
await Promise.all([ await Promise.all([
setIsSimpleInternetSearch(isSimpleInternetSearch), setIsSimpleInternetSearch(isSimpleInternetSearch),
@ -143,6 +157,7 @@ export const setSearchSettings = async ({
setIsVisitSpecificWebsite(visitSpecificWebsite), setIsVisitSpecificWebsite(visitSpecificWebsite),
setSearxngJSONMode(searxngJSONMode), setSearxngJSONMode(searxngJSONMode),
setSearxngURL(searxngURL), setSearxngURL(searxngURL),
setBraveApiKey(braveApiKey) setBraveApiKey(braveApiKey),
setGoogleDomain(googleDomain)
]) ])
} }

188
src/utils/google-domains.ts Normal file
View File

@ -0,0 +1,188 @@
export const ALL_GOOGLE_DOMAINS = [
"google.ad",
"google.ae",
"google.al",
"google.am",
"google.as",
"google.at",
"google.az",
"google.ba",
"google.be",
"google.bf",
"google.bg",
"google.bi",
"google.bj",
"google.bs",
"google.bt",
"google.by",
"google.ca",
"google.cd",
"google.cf",
"google.cg",
"google.ch",
"google.ci",
"google.cl",
"google.cm",
"google.co.ao",
"google.co.bw",
"google.co.ck",
"google.co.cr",
"google.co.id",
"google.co.il",
"google.co.in",
"google.co.jp",
"google.co.ke",
"google.co.kr",
"google.co.ls",
"google.co.ma",
"google.co.mz",
"google.co.nz",
"google.co.th",
"google.co.tz",
"google.co.ug",
"google.co.uk",
"google.co.uz",
"google.co.ve",
"google.co.vi",
"google.co.za",
"google.co.zm",
"google.co.zw",
"google.com",
"google.com.af",
"google.com.ag",
"google.com.ai",
"google.com.ar",
"google.com.au",
"google.com.bd",
"google.com.bh",
"google.com.bn",
"google.com.bo",
"google.com.br",
"google.com.bz",
"google.com.co",
"google.com.cu",
"google.com.cy",
"google.com.do",
"google.com.ec",
"google.com.eg",
"google.com.et",
"google.com.fj",
"google.com.gh",
"google.com.gi",
"google.com.gt",
"google.com.hk",
"google.com.jm",
"google.com.kh",
"google.com.kw",
"google.com.lb",
"google.com.ly",
"google.com.mm",
"google.com.mt",
"google.com.mx",
"google.com.my",
"google.com.na",
"google.com.ng",
"google.com.ni",
"google.com.np",
"google.com.om",
"google.com.pa",
"google.com.pe",
"google.com.pg",
"google.com.ph",
"google.com.pk",
"google.com.pr",
"google.com.py",
"google.com.qa",
"google.com.sa",
"google.com.sb",
"google.com.sg",
"google.com.sl",
"google.com.sv",
"google.com.tj",
"google.com.tr",
"google.com.tr",
"google.com.tw",
"google.com.ua",
"google.com.uy",
"google.com.vc",
"google.com.vn",
"google.cv",
"google.cz",
"google.de",
"google.dj",
"google.dk",
"google.dm",
"google.dz",
"google.ee",
"google.es",
"google.fi",
"google.fm",
"google.fr",
"google.ga",
"google.ge",
"google.gl",
"google.gm",
"google.gp",
"google.gr",
"google.gy",
"google.hn",
"google.hr",
"google.ht",
"google.hu",
"google.ie",
"google.iq",
"google.is",
"google.it",
"google.je",
"google.jo",
"google.kg",
"google.ki",
"google.kz",
"google.la",
"google.li",
"google.lk",
"google.lt",
"google.lu",
"google.lv",
"google.md",
"google.mg",
"google.mk",
"google.ml",
"google.mn",
"google.ms",
"google.mu",
"google.mv",
"google.mw",
"google.ne",
"google.nl",
"google.no",
"google.nr",
"google.nu",
"google.pl",
"google.ps",
"google.pt",
"google.ro",
"google.rs",
"google.ru",
"google.rw",
"google.sc",
"google.se",
"google.sh",
"google.si",
"google.sk",
"google.sm",
"google.sn",
"google.so",
"google.sr",
"google.td",
"google.tg",
"google.tk",
"google.tl",
"google.tm",
"google.tn",
"google.to",
"google.tt",
"google.vg",
"google.vu",
"google.ws"
]

View File

@ -44,5 +44,9 @@ export const OAI_API_PROVIDERS = [
value: "openrouter", value: "openrouter",
baseUrl: "https://openrouter.ai/api/v1" baseUrl: "https://openrouter.ai/api/v1"
}, },
{
label: "Google AI",
value: "gemini",
baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai"
}
] ]

View File

@ -1,5 +1,6 @@
import { pageAssistEmbeddingModel } from "@/models/embedding" import { pageAssistEmbeddingModel } from "@/models/embedding"
import { import {
getGoogleDomain,
getIsSimpleInternetSearch, getIsSimpleInternetSearch,
totalSearchResults totalSearchResults
} from "@/services/search" } from "@/services/search"
@ -18,15 +19,16 @@ import {
export const localGoogleSearch = async (query: string) => { export const localGoogleSearch = async (query: string) => {
const baseGoogleDomain = await getGoogleDomain()
await urlRewriteRuntime( await urlRewriteRuntime(
cleanUrl("https://www.google.com/search?hl=en&q=" + query), cleanUrl(`https://www.${baseGoogleDomain}/search?hl=en&q=` + query),
"google" "google"
) )
const abortController = new AbortController() const abortController = new AbortController()
setTimeout(() => abortController.abort(), 10000) setTimeout(() => abortController.abort(), 10000)
const htmlString = await fetch( const htmlString = await fetch(
"https://www.google.com/search?hl=en&q=" + query, `https://www.${baseGoogleDomain}/search?hl=en&q=` + query,
{ {
signal: abortController.signal signal: abortController.signal
} }

View File

@ -50,7 +50,7 @@ export default defineConfig({
outDir: "build", outDir: "build",
manifest: { manifest: {
version: "1.3.8", version: "1.3.9",
name: name:
process.env.TARGET === "firefox" process.env.TARGET === "firefox"
? "Page Assist - A Web UI for Local AI Models" ? "Page Assist - A Web UI for Local AI Models"
@ -81,7 +81,7 @@ export default defineConfig({
execute_side_panel: { execute_side_panel: {
description: "Open the side panel", description: "Open the side panel",
suggested_key: { suggested_key: {
default: "Ctrl+Shift+P" default: "Ctrl+Shift+Y"
} }
} }
}, },