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.
Default Keyboard Shortcut: `Ctrl+Shift+P`
Default Keyboard Shortcut: `Ctrl+Shift+Y`
### Web UI

View File

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

View File

@ -68,7 +68,7 @@ export const PlaygroundMessage = (props: Props) => {
)}
</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">
{props.isBot
? props.name === "chrome::gemini-nano::page-assist"
@ -212,7 +212,9 @@ export const PlaygroundMessage = (props: Props) => {
{props.generationInfo && (
<Popover
content={
<GenerationInfo generationInfo={props.generationInfo} />
<GenerationInfo
generationInfo={props.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">

View File

@ -7,6 +7,7 @@ import { OpenAiIcon } from "../Icons/OpenAI"
import { TogtherMonoIcon } from "../Icons/Togther"
import { OpenRouterIcon } from "../Icons/OpenRouter"
import { LLamaFile } from "../Icons/Llamafile"
import { GeminiIcon } from "../Icons/GeminiIcon"
export const ProviderIcons = ({
provider,
@ -34,6 +35,8 @@ export const ProviderIcons = ({
return <OpenRouterIcon className={className} />
case "llamafile":
return <LLamaFile className={className} />
case "gemini":
return <GeminiIcon className={className} />
default:
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 && (
<div className="w-full h-16 flex-shrink-0"></div>
<div className="w-full h-10 flex-shrink-0"></div>
)}
</div>
{!isAtBottom && (

View File

@ -192,7 +192,7 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
return (
<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 "}
`}>
<div
@ -217,7 +217,8 @@ export const PlaygroundForm = ({ dropedFile }: Props) => {
</div>
</div>
<div>
<div className={`flex rounded-t-xl bg-white dark:bg-transparent ${
<div
className={`flex rounded-t-xl bg-white dark:bg-transparent ${
temporaryChat && "!bg-gray-300 dark:!bg-black"
}`}>
<form
@ -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"
onPaste={handlePaste}
rows={1}
style={{ minHeight: "40px" }}
style={{ minHeight: "30px" }}
tabIndex={0}
placeholder={t("form.textarea.placeholder")}
{...form.getInputProps("message")}
/>
<div className="mt-4 flex justify-between items-center">
<div className="mt-2 flex justify-between items-center">
<div className="flex">
{!selectedKnowledge && (
<Tooltip title={t("tooltip.searchInternet")}>
<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
value={webSearch}
onChange={(e) => setWebSearch(e)}

View File

@ -1,5 +1,6 @@
import { SaveButton } from "@/components/Common/SaveButton"
import { getSearchSettings, setSearchSettings } from "@/services/search"
import { ALL_GOOGLE_DOMAINS } from "@/utils/google-domains"
import { SUPPORTED_SERACH_PROVIDERS } from "@/utils/search-provider"
import { useForm } from "@mantine/form"
import { useQuery } from "@tanstack/react-query"
@ -18,6 +19,7 @@ export const SearchModeSettings = () => {
searxngURL: "",
searxngJSONMode: false,
braveApiKey: "",
googleDomain: ""
}
})
@ -82,6 +84,32 @@ export const SearchModeSettings = () => {
</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" && (
<>
<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 { ChatOpenAI } from "@langchain/openai"
import { urlRewriteRuntime } from "@/libs/runtime"
import { ChatGoogleAI } from "./ChatGoogleAI"
export const pageAssistModel = async ({
model,
@ -30,18 +31,15 @@ export const pageAssistModel = async ({
numPredict?: number
useMMap?: boolean
}) => {
if (model === "chrome::gemini-nano::page-assist") {
return new ChatChromeAI({
temperature,
topK,
topK
})
}
const isCustom = isCustomModel(model)
if (isCustom) {
const modelInfo = await getModelInfo(model)
const providerInfo = await getOpenAIConfigById(modelInfo.provider_id)
@ -50,6 +48,20 @@ export const pageAssistModel = async ({
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({
modelName: modelInfo.model_id,
openAIApiKey: providerInfo.apiKey || "temp",
@ -58,13 +70,11 @@ export const pageAssistModel = async ({
maxTokens: numPredict,
configuration: {
apiKey: providerInfo.apiKey || "temp",
baseURL: providerInfo.baseUrl || "",
},
baseURL: providerInfo.baseUrl || ""
}
}) as any
}
return new ChatOllama({
baseUrl,
keepAlive,
@ -76,9 +86,6 @@ export const pageAssistModel = async ({
model,
numGpu,
numPredict,
useMMap,
useMMap
})
}

View File

@ -92,11 +92,21 @@ export const setBraveApiKey = async (braveApiKey: string) => {
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 () => {
const [isSimpleInternetSearch, searchProvider, totalSearchResult, visitSpecificWebsite,
searxngURL,
searxngJSONMode,
braveApiKey
braveApiKey,
googleDomain
] =
await Promise.all([
getIsSimpleInternetSearch(),
@ -105,7 +115,8 @@ export const getSearchSettings = async () => {
getIsVisitSpecificWebsite(),
getSearxngURL(),
isSearxngJSONMode(),
getBraveApiKey()
getBraveApiKey(),
getGoogleDomain()
])
return {
@ -115,7 +126,8 @@ export const getSearchSettings = async () => {
visitSpecificWebsite,
searxngURL,
searxngJSONMode,
braveApiKey
braveApiKey,
googleDomain
}
}
@ -126,7 +138,8 @@ export const setSearchSettings = async ({
visitSpecificWebsite,
searxngJSONMode,
searxngURL,
braveApiKey
braveApiKey,
googleDomain
}: {
isSimpleInternetSearch: boolean
searchProvider: string
@ -134,7 +147,8 @@ export const setSearchSettings = async ({
visitSpecificWebsite: boolean
searxngURL: string
searxngJSONMode: boolean,
braveApiKey: string
braveApiKey: string,
googleDomain: string
}) => {
await Promise.all([
setIsSimpleInternetSearch(isSimpleInternetSearch),
@ -143,6 +157,7 @@ export const setSearchSettings = async ({
setIsVisitSpecificWebsite(visitSpecificWebsite),
setSearxngJSONMode(searxngJSONMode),
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",
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 {
getGoogleDomain,
getIsSimpleInternetSearch,
totalSearchResults
} from "@/services/search"
@ -18,15 +19,16 @@ import {
export const localGoogleSearch = async (query: string) => {
const baseGoogleDomain = await getGoogleDomain()
await urlRewriteRuntime(
cleanUrl("https://www.google.com/search?hl=en&q=" + query),
cleanUrl(`https://www.${baseGoogleDomain}/search?hl=en&q=` + query),
"google"
)
const abortController = new AbortController()
setTimeout(() => abortController.abort(), 10000)
const htmlString = await fetch(
"https://www.google.com/search?hl=en&q=" + query,
`https://www.${baseGoogleDomain}/search?hl=en&q=` + query,
{
signal: abortController.signal
}

View File

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