feat: Add custom headers support

This commit is contained in:
n4ze3m 2024-06-30 00:21:43 +05:30
parent 86296c96b6
commit 52f9a2953a
14 changed files with 2126 additions and 1902 deletions

View File

@ -229,6 +229,18 @@
"label": "Custom Origin URL",
"placeholder": "Enter Custom Origin URL"
},
"headers": {
"label": "Custom Headers",
"add": "Add Header",
"key": {
"label": "Header Key",
"placeholder": "Authorization"
},
"value": {
"label": "Header Value",
"placeholder": "Bearer token"
}
},
"help": "If you have connection issues with Ollama on Page Assist, you can configure a custom origin URL. To learn more about the configuration, <anchor>click here</anchor>."
}
}

View File

@ -229,6 +229,18 @@
"label": "URL Personalizada",
"placeholder": "Ingresar URL Personalizada"
},
"headers": {
"label": "Encabezados Personalizados",
"add": "Agregar Encabezado",
"key": {
"label": "Clave del Encabezado",
"placeholder": "Autorización"
},
"value": {
"label": "Valor del Encabezado",
"placeholder": "Token Bearer"
}
},
"help": "Si tenes problemas de conexión con Ollama en Page Assist, podes configurar una URL de personalizada. Para saber más sobre la configuración, <anchor>click aqui</anchor>."
}
}

View File

@ -229,6 +229,18 @@
"label": "URL d'origine personnalisée",
"placeholder": "Entrez l'URL d'origine personnalisée"
},
"headers": {
"label": "En-têtes Personnalisés",
"add": "Ajouter En-tête",
"key": {
"label": "Clé de l'En-tête",
"placeholder": "Autorisation"
},
"value": {
"label": "Valeur de l'En-tête",
"placeholder": "Jeton Bearer"
}
},
"help": "Si vous avez des problèmes de connexion avec OLLAMA sur Page Assist, vous pouvez configurer une URL d'origine personnalisée. Pour en savoir plus sur la configuration, <anchor>cliquez ici</anchor>."
}
}

View File

@ -229,6 +229,18 @@
"label": "URL di Origine Personalizzato",
"placeholder": "Inserisci URL di Origine Personalizzato"
},
"headers": {
"label": "Intestazioni Personalizzate",
"add": "Aggiungi Intestazione",
"key": {
"label": "Chiave dell'Intestazione",
"placeholder": "Autorizzazione"
},
"value": {
"label": "Valore dell'Intestazione",
"placeholder": "Token Bearer"
}
},
"help": "Se hai problemi di connessione con Ollama su Page Assist, puoi configurare un URL di origine personalizzato. Per saperne di più sulla configurazione, <anchor>clicca qui</anchor>."
}
}

View File

@ -232,6 +232,18 @@
"label": "カスタムOriginのURL",
"placeholder": "カスタムOriginのURLを入力"
},
"headers": {
"label": "カスタムヘッダー",
"add": "ヘッダーを追加",
"key": {
"label": "ヘッダーキー",
"placeholder": "認証"
},
"value": {
"label": "ヘッダー値",
"placeholder": "ベアラートークン"
}
},
"help": "PageAssistでOllamaに接続の問題がある場合は、カスタムOriginのURLを設定できます。設定の詳細については、<anchor>ここをクリック</anchor>してください。"
}
}

View File

@ -232,6 +232,18 @@
"label": "Custom Origin URL",
"placeholder": "Enter Custom Origin URL"
},
"headers": {
"label": "കസ്റ്റം തലക്കെട്ടുകൾ",
"add": "തലക്കെട്ട് ചേർക്കുക",
"key": {
"label": "തലക്കെട്ട് കീ",
"placeholder": "അധികൃതപെടുത്തൽ"
},
"value": {
"label": "തലക്കെട്ട് മൂല്യം",
"placeholder": "ബെയറർ ടോക്കൺ"
}
},
"help": "ഏജ് അസിസ്റ്റന്റിൽ Ollama-യുമായി ബന്ധപ്പെടുമ്പോൾ ബന്ധതടസ്സം ഉണ്ടെങ്കിൽ, നിങ്ങൾക്ക് ഒരു വ്യക്തിഗത അസ്ഥിരത്വം URL കോൺഫിഗർ ചെയ്യാം. കോൺഫിഗറേഷനെക്കുറിച്ച് കൂടുതലറിയാൻ, <anchor>ഇവിടെ ക്ലിക്കുചെയ്യുക</anchor>."
}
}

View File

@ -229,6 +229,18 @@
"label": "URL de Origem Personalizada",
"placeholder": "Insira a URL de Origem Personalizada"
},
"headers": {
"label": "Cabeçalhos Personalizados",
"add": "Adicionar Cabeçalho",
"key": {
"label": "Chave do Cabeçalho",
"placeholder": "Autorização"
},
"value": {
"label": "Valor do Cabeçalho",
"placeholder": "Token Bearer"
}
},
"help": "Se você tiver problemas de conexão com o Ollama no Page Assist, você pode configurar uma URL de origem personalizada. Para saber mais sobre a configuração, <anchor>clique aqui</anchor>."
}
}

View File

@ -230,6 +230,18 @@
"label": "Пользовательский исходный URL",
"placeholder": "Введите пользовательский исходный URL"
},
"headers": {
"label": "Пользовательские Заголовки",
"add": "Добавить Заголовок",
"key": {
"label": "Ключ Заголовка",
"placeholder": "Авторизация"
},
"value": {
"label": "Значение Заголовка",
"placeholder": "Токен Bearer"
}
},
"help": "Если у вас возникают проблемы с подключением к Ollama на странице помощника, вы можете настроить пользовательский исходный URL. Чтобы узнать больше о конфигурации, <anchor>нажмите здесь</anchor>."
}
}

View File

@ -234,6 +234,18 @@
"label": "自定义来源 URL",
"placeholder": "输入自定义来源 URL"
},
"headers": {
"label": "自定义头部",
"add": "添加头部",
"key": {
"label": "头部键",
"placeholder": "授权"
},
"value": {
"label": "头部值",
"placeholder": "承载令牌"
}
},
"help": "如果您在 Page Assist 上与 Ollama 有连接问题,您可以配置自定义来源 URL。要了解更多关于配置的信息,<anchor>点击此处</anchor>。"
}
}

View File

@ -1,49 +1,132 @@
import { useStorage } from "@plasmohq/storage/hook"
import { Input, Switch } from "antd"
import { Divider, Input, Switch } from "antd"
import { useTranslation } from "react-i18next"
import { Form } from "antd"
import React from "react"
import {
customOllamaHeaders,
getRewriteUrl,
isUrlRewriteEnabled,
setCustomOllamaHeaders,
setRewriteUrl,
setUrlRewriteEnabled
} from "@/services/app"
import { Trash2Icon } from "lucide-react"
import { SaveButton } from "../SaveButton"
export const AdvanceOllamaSettings = () => {
const [urlRewriteEnabled, setUrlRewriteEnabled] = useStorage(
"urlRewriteEnabled",
false
)
const [form] = Form.useForm()
const watchUrlRewriteEnabled = Form.useWatch("urlRewriteEnabled", form)
const fetchAdvancedData = async () => {
const [urlRewriteEnabled, rewriteUrl, headers] = await Promise.all([
isUrlRewriteEnabled(),
getRewriteUrl(),
customOllamaHeaders()
])
form.setFieldsValue({ urlRewriteEnabled, rewriteUrl, headers })
}
React.useEffect(() => {
fetchAdvancedData()
}, [])
const [rewriteUrl, setRewriteUrl] = useStorage(
"rewriteUrl",
"http://127.0.0.1:11434"
)
const { t } = useTranslation("settings")
return (
<div className="space-y-4">
<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("ollamaSettings.settings.advanced.urlRewriteEnabled.label")}
</span>
<div>
<Switch
className="mt-4 sm:mt-0"
checked={urlRewriteEnabled}
onChange={(checked) => setUrlRewriteEnabled(checked)}
/>
</div>
</div>
<div className="flex flex-col space-y-4 sm:space-y-0 sm:justify-between">
<span className="text-gray-700 dark:text-neutral-50 mb-3">
{t("ollamaSettings.settings.advanced.rewriteUrl.label")}
</span>
<div>
<Form
onFinish={(e) => {
const headers = e?.headers?.filter(
(header: { key: string; value: string }) => header.key && header.value
)
setUrlRewriteEnabled(e.urlRewriteEnabled)
setRewriteUrl(e.rewriteUrl)
setCustomOllamaHeaders(headers)
}}
form={form}
layout="vertical"
className="space-y-4">
<Form.Item
name="urlRewriteEnabled"
label={t("ollamaSettings.settings.advanced.urlRewriteEnabled.label")}>
<Switch />
</Form.Item>
<Form.Item
required={watchUrlRewriteEnabled}
name="rewriteUrl"
label={t("ollamaSettings.settings.advanced.rewriteUrl.label")}>
<Input
disabled={!watchUrlRewriteEnabled}
className="w-full"
value={rewriteUrl}
disabled={!urlRewriteEnabled}
placeholder={t(
"ollamaSettings.settings.advanced.rewriteUrl.placeholder"
)}
onChange={(e) => setRewriteUrl(e.target.value)}
/>
</Form.Item>
<Form.List name="headers">
{(fields, { add, remove }) => (
<div className="flex flex-col ">
<div className="flex justify-between items-center">
<h3 className="text-md font-semibold">
{t("ollamaSettings.settings.advanced.headers.label")}
</h3>
<button
type="button"
className="dark:bg-white dark:text-black text-white bg-black p-1.5 text-xs rounded-md"
onClick={() => {
add()
}}>
{t("ollamaSettings.settings.advanced.headers.add")}
</button>
</div>
{fields.map((field, index) => (
<div key={field.key} className="flex items-center w-full">
<div className="flex-grow flex space-x-4">
<Form.Item
label={t(
"ollamaSettings.settings.advanced.headers.key.label"
)}
name={[field.name, "key"]}
className="flex-1 mb-0">
<Input
className="w-full"
placeholder={t(
"ollamaSettings.settings.advanced.headers.key.placeholder"
)}
/>
</Form.Item>
<Form.Item
label={t(
"ollamaSettings.settings.advanced.headers.value.label"
)}
name={[field.name, "value"]}
className="flex-1 mb-0">
<Input
className="w-full"
placeholder={t(
"ollamaSettings.settings.advanced.headers.value.placeholder"
)}
/>
</Form.Item>
</div>
<button
type="button"
onClick={() => {
remove(field.name)
}}
className="shrink-0 ml-2 text-red-500 dark:text-red-400">
<Trash2Icon className="w-5 h-5" />
</button>
</div>
))}
</div>
)}
</Form.List>
<Divider />
<Form.Item className="flex justify-end">
<SaveButton btnType="submit" />
</Form.Item>
</Form>
)
}

View File

@ -50,6 +50,14 @@ export const SettingsOllama = () => {
className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100"
/>
</div>
<div className="flex justify-end mb-3">
<SaveButton
onClick={() => {
saveOllamaURL(ollamaURL)
}}
className="mt-2"
/>
</div>
<Collapse
size="small"
items={[
@ -79,15 +87,6 @@ export const SettingsOllama = () => {
}
]}
/>
<div className="flex justify-end">
<SaveButton
onClick={() => {
saveOllamaURL(ollamaURL)
}}
className="mt-2"
/>
</div>
</div>
<ModelSettings />

View File

@ -1,6 +1,7 @@
import { Embeddings, EmbeddingsParams } from "@langchain/core/embeddings"
import type { StringWithAutocomplete } from "@langchain/core/utils/types"
import { parseKeepAlive } from "./utils/ollama"
import { getCustomOllamaHeaders } from "@/services/app"
export interface OllamaInput {
embeddingOnly?: boolean
@ -213,12 +214,14 @@ export class OllamaEmbeddingsPageAssist extends Embeddings {
"http://127.0.0.1:"
)
}
const customHeaders = await getCustomOllamaHeaders()
const response = await fetch(`${formattedBaseUrl}/api/embeddings`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...this.headers
...this.headers,
...customHeaders
},
body: JSON.stringify({
prompt,

View File

@ -1,184 +1,189 @@
import { IterableReadableStream } from "@langchain/core/utils/stream";
import type { StringWithAutocomplete } from "@langchain/core/utils/types";
import { BaseLanguageModelCallOptions } from "@langchain/core/language_models/base";
import { IterableReadableStream } from "@langchain/core/utils/stream"
import type { StringWithAutocomplete } from "@langchain/core/utils/types"
import { BaseLanguageModelCallOptions } from "@langchain/core/language_models/base"
import { getCustomOllamaHeaders } from "@/services/app"
export interface OllamaInput {
embeddingOnly?: boolean;
f16KV?: boolean;
frequencyPenalty?: number;
headers?: Record<string, string>;
keepAlive?: any;
logitsAll?: boolean;
lowVram?: boolean;
mainGpu?: number;
model?: string;
baseUrl?: string;
mirostat?: number;
mirostatEta?: number;
mirostatTau?: number;
numBatch?: number;
numCtx?: number;
numGpu?: number;
numGqa?: number;
numKeep?: number;
numPredict?: number;
numThread?: number;
penalizeNewline?: boolean;
presencePenalty?: number;
repeatLastN?: number;
repeatPenalty?: number;
ropeFrequencyBase?: number;
ropeFrequencyScale?: number;
temperature?: number;
stop?: string[];
tfsZ?: number;
topK?: number;
topP?: number;
typicalP?: number;
useMLock?: boolean;
useMMap?: boolean;
vocabOnly?: boolean;
seed?: number;
format?: StringWithAutocomplete<"json">;
embeddingOnly?: boolean
f16KV?: boolean
frequencyPenalty?: number
headers?: Record<string, string>
keepAlive?: any
logitsAll?: boolean
lowVram?: boolean
mainGpu?: number
model?: string
baseUrl?: string
mirostat?: number
mirostatEta?: number
mirostatTau?: number
numBatch?: number
numCtx?: number
numGpu?: number
numGqa?: number
numKeep?: number
numPredict?: number
numThread?: number
penalizeNewline?: boolean
presencePenalty?: number
repeatLastN?: number
repeatPenalty?: number
ropeFrequencyBase?: number
ropeFrequencyScale?: number
temperature?: number
stop?: string[]
tfsZ?: number
topK?: number
topP?: number
typicalP?: number
useMLock?: boolean
useMMap?: boolean
vocabOnly?: boolean
seed?: number
format?: StringWithAutocomplete<"json">
}
export interface OllamaRequestParams {
model: string;
format?: StringWithAutocomplete<"json">;
images?: string[];
model: string
format?: StringWithAutocomplete<"json">
images?: string[]
options: {
embedding_only?: boolean;
f16_kv?: boolean;
frequency_penalty?: number;
logits_all?: boolean;
low_vram?: boolean;
main_gpu?: number;
mirostat?: number;
mirostat_eta?: number;
mirostat_tau?: number;
num_batch?: number;
num_ctx?: number;
num_gpu?: number;
num_gqa?: number;
num_keep?: number;
num_thread?: number;
num_predict?: number;
penalize_newline?: boolean;
presence_penalty?: number;
repeat_last_n?: number;
repeat_penalty?: number;
rope_frequency_base?: number;
rope_frequency_scale?: number;
temperature?: number;
stop?: string[];
tfs_z?: number;
top_k?: number;
top_p?: number;
typical_p?: number;
use_mlock?: boolean;
use_mmap?: boolean;
vocab_only?: boolean;
};
embedding_only?: boolean
f16_kv?: boolean
frequency_penalty?: number
logits_all?: boolean
low_vram?: boolean
main_gpu?: number
mirostat?: number
mirostat_eta?: number
mirostat_tau?: number
num_batch?: number
num_ctx?: number
num_gpu?: number
num_gqa?: number
num_keep?: number
num_thread?: number
num_predict?: number
penalize_newline?: boolean
presence_penalty?: number
repeat_last_n?: number
repeat_penalty?: number
rope_frequency_base?: number
rope_frequency_scale?: number
temperature?: number
stop?: string[]
tfs_z?: number
top_k?: number
top_p?: number
typical_p?: number
use_mlock?: boolean
use_mmap?: boolean
vocab_only?: boolean
}
}
export type OllamaMessage = {
role: StringWithAutocomplete<"user" | "assistant" | "system">;
content: string;
images?: string[];
};
role: StringWithAutocomplete<"user" | "assistant" | "system">
content: string
images?: string[]
}
export interface OllamaGenerateRequestParams extends OllamaRequestParams {
prompt: string;
prompt: string
}
export interface OllamaChatRequestParams extends OllamaRequestParams {
messages: OllamaMessage[];
messages: OllamaMessage[]
}
export type BaseOllamaGenerationChunk = {
model: string;
created_at: string;
done: boolean;
total_duration?: number;
load_duration?: number;
prompt_eval_count?: number;
prompt_eval_duration?: number;
eval_count?: number;
eval_duration?: number;
};
model: string
created_at: string
done: boolean
total_duration?: number
load_duration?: number
prompt_eval_count?: number
prompt_eval_duration?: number
eval_count?: number
eval_duration?: number
}
export type OllamaGenerationChunk = BaseOllamaGenerationChunk & {
response: string;
};
response: string
}
export type OllamaChatGenerationChunk = BaseOllamaGenerationChunk & {
message: OllamaMessage;
};
message: OllamaMessage
}
export type OllamaCallOptions = BaseLanguageModelCallOptions & {
headers?: Record<string, string>;
};
headers?: Record<string, string>
}
async function* createOllamaStream(
url: string,
params: OllamaRequestParams,
options: OllamaCallOptions
) {
let formattedUrl = url;
let formattedUrl = url
if (formattedUrl.startsWith("http://localhost:")) {
// Node 18 has issues with resolving "localhost"
// See https://github.com/node-fetch/node-fetch/issues/1624
formattedUrl = formattedUrl.replace(
"http://localhost:",
"http://127.0.0.1:"
);
)
}
const customHeaders = await getCustomOllamaHeaders()
const response = await fetch(formattedUrl, {
method: "POST",
body: JSON.stringify(params),
headers: {
"Content-Type": "application/json",
...options.headers,
...customHeaders
},
signal: options.signal,
});
signal: options.signal
})
if (!response.ok) {
let error;
const responseText = await response.text();
let error
const responseText = await response.text()
try {
const json = JSON.parse(responseText);
const json = JSON.parse(responseText)
error = new Error(
`Ollama call failed with status code ${response.status}: ${json.error}`
);
)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
error = new Error(
`Ollama call failed with status code ${response.status}: ${responseText}`
);
)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(error as any).response = response;
throw error;
;(error as any).response = response
throw error
}
if (!response.body) {
throw new Error(
"Could not begin Ollama stream. Please check the given URL and try again."
);
)
}
const stream = IterableReadableStream.fromReadableStream(response.body);
const stream = IterableReadableStream.fromReadableStream(response.body)
const decoder = new TextDecoder();
let extra = "";
const decoder = new TextDecoder()
let extra = ""
for await (const chunk of stream) {
const decoded = extra + decoder.decode(chunk);
const lines = decoded.split("\n");
extra = lines.pop() || "";
const decoded = extra + decoder.decode(chunk)
const lines = decoded.split("\n")
extra = lines.pop() || ""
for (const line of lines) {
try {
yield JSON.parse(line);
yield JSON.parse(line)
} catch (e) {
console.warn(`Received a non-JSON parseable chunk: ${line}`);
console.warn(`Received a non-JSON parseable chunk: ${line}`)
}
}
}
@ -189,7 +194,7 @@ export async function* createOllamaGenerateStream(
params: OllamaGenerateRequestParams,
options: OllamaCallOptions
): AsyncGenerator<OllamaGenerationChunk> {
yield* createOllamaStream(`${baseUrl}/api/generate`, params, options);
yield* createOllamaStream(`${baseUrl}/api/generate`, params, options)
}
export async function* createOllamaChatStream(
@ -197,10 +202,9 @@ export async function* createOllamaChatStream(
params: OllamaChatRequestParams,
options: OllamaCallOptions
): AsyncGenerator<OllamaChatGenerationChunk> {
yield* createOllamaStream(`${baseUrl}/api/chat`, params, options);
yield* createOllamaStream(`${baseUrl}/api/chat`, params, options)
}
export const parseKeepAlive = (keepAlive: any) => {
if (keepAlive === "-1") {
return -1

View File

@ -5,10 +5,10 @@ const DEFAULT_URL_REWRITE_URL = "http://127.0.0.1:11434"
export const isUrlRewriteEnabled = async () => {
const enabled = await storage.get<boolean | undefined>("urlRewriteEnabled")
return enabled
return enabled ?? false
}
export const setUrlRewriteEnabled = async (enabled: boolean) => {
await storage.set("urlRewriteEnabled", enabled ? "true" : "false")
await storage.set("urlRewriteEnabled", enabled)
}
export const getRewriteUrl = async () => {
@ -35,12 +35,10 @@ export const getAdvancedOllamaSettings = async () => {
}
}
export const copilotResumeLastChat = async () => {
return await storage.get<boolean>("copilotResumeLastChat")
}
export const defaultSidebarOpen = async () => {
const sidebarOpen = await storage.get("sidebarOpen")
if (!sidebarOpen || sidebarOpen === "") {
@ -49,7 +47,36 @@ export const defaultSidebarOpen = async () => {
return sidebarOpen
}
export const setSidebarOpen = async (sidebarOpen: string) => {
await storage.set("sidebarOpen", sidebarOpen)
}
export const customOllamaHeaders = async (): Promise<
{ key: string; value: string }[]
> => {
const headers = await storage.get<
{ key: string; value: string }[] | undefined
>("customOllamaHeaders")
if (!headers) {
return []
}
return headers
}
export const setCustomOllamaHeaders = async (headers: string[]) => {
await storage.set("customOllamaHeaders", headers)
}
export const getCustomOllamaHeaders = async (): Promise<
Record<string, string>
> => {
const headers = await customOllamaHeaders()
const headerMap: Record<string, string> = {}
for (const header of headers) {
headerMap[header.key] = header.value
}
return headerMap
}