Delete unused files and update API calls

This commit is contained in:
n4ze3m 2024-03-03 19:55:43 +05:30
parent e0c2c0c745
commit 0351beeaae
18 changed files with 389 additions and 211 deletions

View File

@ -0,0 +1,90 @@
import {
Book,
BrainCircuit,
CircuitBoardIcon,
Orbit
} from "lucide-react"
import { Link, useLocation } from "react-router-dom"
function classNames(...classes: string[]) {
return classes.filter(Boolean).join(" ")
}
const LinkComponent = (item: {
href: string
name: string
icon: any
current: string
}) => {
return (
<li>
<Link
to={item.href}
className={classNames(
item.current === item.href
? "bg-gray-100 text-indigo-600 dark:bg-[#262626] dark:text-white"
: "text-gray-700 hover:text-indigo-600 hover:bg-gray-100 dark:text-gray-200 dark:hover:text-white dark:hover:bg-[#262626]",
"group flex gap-x-3 rounded-md py-2 pl-2 pr-3 text-sm leading-6 font-semibold"
)}>
<item.icon
className={classNames(
item.current === item.href
? "text-indigo-600 dark:text-white"
: "text-gray-400 group-hover:text-indigo-600 dark:text-gray-200 dark:group-hover:text-white",
"h-6 w-6 shrink-0"
)}
aria-hidden="true"
/>
{item.name}
</Link>
</li>
)
}
export const SettingsLayout = ({ children }: { children: React.ReactNode }) => {
const location = useLocation()
return (
<>
<div className="mx-auto max-w-7xl lg:flex lg:gap-x-16 lg:px-8">
<aside className="flex lg:rounded-md bg-white lg:h-56 lg:p-4 lg:mt-20 overflow-x-auto lg:border border-b py-4 lg:block lg:w-64 lg:flex-none dark:bg-[#171717] dark:border-gray-600">
<nav className="flex-none px-4 sm:px-6 lg:px-0">
<ul
role="list"
className="flex gap-x-3 gap-y-1 whitespace-nowrap lg:flex-col">
<LinkComponent
href="/settings"
name="General Settings"
icon={Orbit}
current={location.pathname}
/>
<LinkComponent
href="/settings/ollama"
name="Ollama Settings"
icon={CircuitBoardIcon}
current={location.pathname}
/>
<LinkComponent
href="/settings/model"
name="Manage Model"
current={location.pathname}
icon={BrainCircuit}
/>
<LinkComponent
href="/settings/prompt"
name="Manage Prompt"
icon={Book}
current={location.pathname}
/>
</ul>
</nav>
</aside>
<main className={"px-4 py-16 sm:px-6 lg:flex-auto lg:px-0 lg:py-20"}>
<div className="mx-auto max-w-2xl space-y-16 sm:space-y-10 lg:mx-0 lg:max-w-none">
{children}
</div>
</main>
</div>
</>
)
}

View File

@ -2,16 +2,14 @@ import React, { useState } from "react"
import { useLocation, NavLink } from "react-router-dom"
import { Sidebar } from "./Sidebar"
import { Drawer, Layout, Modal, Select, Tooltip } from "antd"
import { Drawer, Select, Tooltip } from "antd"
import { useQuery } from "@tanstack/react-query"
import { getAllModels } from "~services/ollama"
import { useMessageOption } from "~hooks/useMessageOption"
import { Settings } from "./Settings"
import {
Book,
BrainCircuit,
ChevronLeft,
CogIcon,
GithubIcon,
PanelLeftIcon,
SquarePen
} from "lucide-react"
@ -22,7 +20,6 @@ export default function OptionLayout({
children: React.ReactNode
}) {
const [sidebarOpen, setSidebarOpen] = useState(false)
const [open, setOpen] = useState(false)
const {
data: models,
@ -38,89 +35,99 @@ export default function OptionLayout({
const { selectedModel, setSelectedModel, clearChat } = useMessageOption()
return (
<Layout className="bg-white dark:bg-[#171717] md:flex">
<div className="flex items-center p-3 fixed flex-row justify-between border-b border-gray-200 dark:border-gray-600 bg-white dark:bg-[#171717] w-full z-10">
<div className="flex items-center flex-row gap-3">
{pathname !== "/" && (
<div>
<NavLink
to="/"
className="text-gray-500 items-center dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<ChevronLeft className="w-6 h-6" />
</NavLink>
<div>
<div>
<div className="flex flex-col">
<div className="sticky top-0 z-[999] flex h-16 p-3 bg-white border-b border-gray-200 dark:bg-[#171717] dark:border-gray-600">
<div className="flex gap-2 items-center">
{pathname !== "/" && (
<div>
<NavLink
to="/"
className="text-gray-500 items-center dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<ChevronLeft className="w-6 h-6" />
</NavLink>
</div>
)}
<div>
<button
className="text-gray-500 dark:text-gray-400"
onClick={() => setSidebarOpen(true)}>
<PanelLeftIcon className="w-6 h-6" />
</button>
</div>
<div>
<button
onClick={clearChat}
className="inline-flex items-center rounded-lg border dark:border-gray-700 bg-transparent px-3 py-3 text-sm font-medium leading-4 text-gray-800 shadow-sm dark:text-white disabled:opacity-50 ">
<SquarePen className="h-4 w-4 mr-3" />
New Chat
</button>
</div>
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
{"/"}
</span>
<div>
<Select
value={selectedModel}
onChange={setSelectedModel}
size="large"
loading={isModelsLoading || isModelsFetching}
filterOption={(input, option) =>
option.label.toLowerCase().indexOf(input.toLowerCase()) >=
0 ||
option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
showSearch
placeholder="Select a model"
className="w-64 "
options={models?.map((model) => ({
label: model.name,
value: model.model
}))}
/>
</div>
</div>
<div className="flex flex-1 justify-end px-4">
<div className="ml-4 flex items-center md:ml-6">
<div className="flex gap-4 items-center">
{/* <Tooltip title="Manage Prompts">
<NavLink
to="/prompts"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<Book className="w-6 h-6" />
</NavLink>
</Tooltip> */}
<Tooltip title="Github Repository">
<a
href="https://github.com/n4ze3m/page-assist"
target="_blank"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<GithubIcon className="w-6 h-6" />
</a>
</Tooltip>
{/* <Tooltip title="Manage Ollama Models">
<NavLink
to="/models"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<BrainCircuit className="w-6 h-6" />
</NavLink>
</Tooltip> */}
<Tooltip title="Manage Ollama Models">
<NavLink
to="/settings"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<CogIcon className="w-6 h-6" />
</NavLink>
</Tooltip>
</div>
</div>
</div>
)}
<div>
<button
className="text-gray-500 dark:text-gray-400"
onClick={() => setSidebarOpen(true)}>
<PanelLeftIcon className="w-6 h-6" />
</button>
</div>
<div>
<button
onClick={clearChat}
className="inline-flex items-center rounded-lg border dark:border-gray-700 bg-transparent px-3 py-3 text-sm font-medium leading-4 text-gray-800 shadow-sm dark:text-white disabled:opacity-50 ">
<SquarePen className="h-4 w-4 mr-3" />
New Chat
</button>
</div>
<span className="text-lg font-thin text-zinc-300 dark:text-zinc-600">
{"/"}
</span>
<div>
<Select
value={selectedModel}
onChange={setSelectedModel}
size="large"
loading={isModelsLoading || isModelsFetching}
filterOption={(input, option) =>
option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 ||
option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
showSearch
placeholder="Select a model"
className="w-64 "
options={models?.map((model) => ({
label: model.name,
value: model.model
}))}
/>
</div>
</div>
<div className="flex gap-4 items-center">
<Tooltip title="Manage Prompts">
<NavLink
to="/prompts"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<Book className="w-6 h-6" />
</NavLink>
</Tooltip>
{/* <Tooltip title="Github Repository">
<a
href="https://github.com/n4ze3m/page-assist"
target="_blank"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<GithubIcon className="w-6 h-6" />
</a>
</Tooltip> */}
<Tooltip title="Manage Ollama Models">
<NavLink
to="/models"
className="!text-gray-500 dark:text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 transition-colors">
<BrainCircuit className="w-6 h-6" />
</NavLink>
</Tooltip>
<button
onClick={() => setOpen(true)}
className="text-gray-500 dark:text-gray-400">
<CogIcon className="w-6 h-6" />
</button>
<main className="flex-1">{children}</main>
</div>
</div>
<Layout.Content>{children}</Layout.Content>
<Drawer
title={"Chat History"}
placement="left"
@ -129,16 +136,6 @@ export default function OptionLayout({
open={sidebarOpen}>
<Sidebar />
</Drawer>
<Modal
open={open}
width={800}
title={"Settings"}
onOk={() => setOpen(false)}
footer={null}
onCancel={() => setOpen(false)}>
<Settings setClose={() => setOpen(false)} />
</Modal>
</Layout>
</div>
)
}

View File

@ -22,7 +22,7 @@ export const ModelsBody = () => {
const { data, status } = useQuery({
queryKey: ["fetchAllModels"],
queryFn: getAllModels
queryFn: () => getAllModels({ returnEmpty: true })
})
const { mutate: deleteOllamaModel } = useMutation({
@ -67,8 +67,8 @@ export const ModelsBody = () => {
})
return (
<div className="z-10 min-h-screen">
<div className="mt-16 mx-auto py-10 max-w-7xl px-3 sm:px-6 lg:px-8">
<div>
<div>
{/* Add new model button */}
<div className="mb-6">
<div className="-ml-4 -mt-2 flex flex-wrap items-center justify-end sm:flex-nowrap">

View File

@ -70,17 +70,15 @@ export const Playground = () => {
ref={drop}
className={`${
dropState === "dragging" ? "bg-gray-100 dark:bg-gray-800 z-10" : ""
} min-h-screen`}>
<PlaygroundChat />
} bg-white dark:bg-[#171717]`}>
<PlaygroundChat />
<div className="flex flex-col items-center">
<div className="flex-grow">
<div className="w-full flex justify-center">
<div className="bottom-0 w-full bg-transparent border-0 fixed pt-2">
<div className="stretch mx-2 flex flex-row gap-3 md:mx-4 lg:mx-auto lg:max-w-2xl xl:max-w-3xl justify-center items-center">
<div className="relative h-full flex-1 items-center justify-center md:flex-col">
<PlaygroundForm
dropedFile={dropedFile}
/>
<PlaygroundForm dropedFile={dropedFile} />
</div>
</div>
</div>

View File

@ -18,7 +18,7 @@ export const PlaygroundChat = () => {
<PlaygroundEmpty />
</div>
)}
{messages.length > 0 && <div className="w-full h-16 flex-shrink-0"></div>}
{/* {messages.length > 0 && <div className="w-full h-16 flex-shrink-0"></div>} */}
{messages.map((message, index) => (
<PlaygroundMessage
key={index}

View File

@ -101,8 +101,8 @@ export const PromptBody = () => {
})
return (
<div className="z-10 min-h-screen">
<div className="mt-16 mx-auto py-10 max-w-7xl px-3 sm:px-6 lg:px-8">
<div>
<div>
<div className="mb-6">
<div className="-ml-4 -mt-2 flex flex-wrap items-center justify-end sm:flex-nowrap">
<div className="ml-4 mt-2 flex-shrink-0">

View File

@ -11,6 +11,7 @@ import {
saveForRag,
setOllamaURL as saveOllamaURL
} from "~services/ollama"
import { SettingPrompt } from "./prompt"
export const SettingsOllama = () => {
const [ollamaURL, setOllamaURL] = useState<string>("")
@ -20,7 +21,7 @@ export const SettingsOllama = () => {
const [ollamaURL, allModels, chunkOverlap, chunkSize, defaultEM] =
await Promise.all([
getOllamaURL(),
getAllModels(),
getAllModels({returnEmpty: true}),
defaultEmbeddingChunkOverlap(),
defaultEmbeddingChunkSize(),
defaultEmbeddingModelForRag()
@ -46,98 +47,130 @@ export const SettingsOllama = () => {
})
return (
<div className="flex flex-col gap-3">
<div className="flex flex-col space-y-3">
{status === "pending" && <Skeleton paragraph={{ rows: 4 }} active />}
{status === "success" && (
<>
<div className="flex flex-col space-y-6">
<div>
<label
htmlFor="ollamaURL"
className="text-sm font-medium dark:text-gray-200">
Ollama URL
</label>
<input
type="url"
id="ollamaURL"
value={ollamaURL}
onChange={(e) => {
setOllamaURL(e.target.value)
}}
placeholder="Your Ollama URL"
className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100"
/>
</div>
<div className="flex justify-end">
<SaveButton
onClick={() => {
saveOllamaURL(ollamaURL)
}}
className="mt-2"
/>
</div>
<Form
layout="vertical"
onFinish={(data) => {
saveRAG({
model: data.defaultEM,
chunkSize: data.chunkSize,
overlap: data.chunkOverlap
})
}}
initialValues={{
chunkSize: ollamaInfo?.chunkSize,
chunkOverlap: ollamaInfo?.chunkOverlap,
defaultEM: ollamaInfo?.defaultEM
}}>
<Form.Item
name="defaultEM"
label="Embedding Model"
help="Highly recommended to use embedding models like `nomic-embed-text`."
rules={[{ required: true, message: "Please select a model!" }]}>
<Select
size="large"
filterOption={(input, option) =>
option.label.toLowerCase().indexOf(input.toLowerCase()) >=
0 ||
option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
showSearch
placeholder="Select a model"
style={{ width: "100%" }}
className="mt-4"
options={ollamaInfo.models?.map((model) => ({
label: model.name,
value: model.model
}))}
/>
</Form.Item>
<Form.Item
name="chunkSize"
label="Chunk Size"
rules={[
{ required: true, message: "Please input your chunk size!" }
]}>
<InputNumber style={{ width: "100%" }} placeholder="Chunk Size" />
</Form.Item>
<Form.Item
name="chunkOverlap"
label="Chunk Overlap"
rules={[
{ required: true, message: "Please input your chunk overlap!" }
]}>
<InputNumber
style={{ width: "100%" }}
placeholder="Chunk Overlap"
/>
</Form.Item>
<div className="flex justify-end">
<SaveButton disabled={isSaveRAGPending} btnType="submit" />
<div>
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
Configure Ollama
</h2>
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div>
</div>
</Form>
</>
<div>
<label
htmlFor="ollamaURL"
className="text-sm font-medium dark:text-gray-200">
Ollama URL
</label>
<input
type="url"
id="ollamaURL"
value={ollamaURL}
onChange={(e) => {
setOllamaURL(e.target.value)
}}
placeholder="Your Ollama URL"
className="w-full p-2 border border-gray-300 rounded-md dark:bg-[#262626] dark:text-gray-100"
/>
</div>
<div className="flex justify-end">
<SaveButton
onClick={() => {
saveOllamaURL(ollamaURL)
}}
className="mt-2"
/>
</div>
</div>
<div>
<div>
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
Configure RAG
</h2>
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div>
</div>
<Form
layout="vertical"
onFinish={(data) => {
saveRAG({
model: data.defaultEM,
chunkSize: data.chunkSize,
overlap: data.chunkOverlap
})
}}
initialValues={{
chunkSize: ollamaInfo?.chunkSize,
chunkOverlap: ollamaInfo?.chunkOverlap,
defaultEM: ollamaInfo?.defaultEM
}}>
<Form.Item
name="defaultEM"
label="Embedding Model"
help="Highly recommended to use embedding models like `nomic-embed-text`."
rules={[{ required: true, message: "Please select a model!" }]}>
<Select
size="large"
filterOption={(input, option) =>
option.label.toLowerCase().indexOf(input.toLowerCase()) >=
0 ||
option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
showSearch
placeholder="Select a model"
style={{ width: "100%" }}
className="mt-4"
options={ollamaInfo.models?.map((model) => ({
label: model.name,
value: model.model
}))}
/>
</Form.Item>
<Form.Item
name="chunkSize"
label="Chunk Size"
rules={[
{ required: true, message: "Please input your chunk size!" }
]}>
<InputNumber
style={{ width: "100%" }}
placeholder="Chunk Size"
/>
</Form.Item>
<Form.Item
name="chunkOverlap"
label="Chunk Overlap"
rules={[
{
required: true,
message: "Please input your chunk overlap!"
}
]}>
<InputNumber
style={{ width: "100%" }}
placeholder="Chunk Overlap"
/>
</Form.Item>
<div className="flex justify-end">
<SaveButton disabled={isSaveRAGPending} btnType="submit" />
</div>
</Form>
</div>
<div>
<div>
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
Configure RAG Prompt
</h2>
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3 mb-6"></div>
</div>
<SettingPrompt />
</div>
</div>
)}
</div>
)

View File

@ -15,9 +15,15 @@ export const SettingOther = () => {
const { mode, toggleDarkMode } = useDarkMode()
return (
<div className="flex flex-col space-y-4">
<dl className="flex flex-col space-y-6">
<div>
<h2 className="text-base font-semibold leading-7 text-gray-900 dark:text-white">
Web UI Settings
</h2>
<div className="border border-b border-gray-200 dark:border-gray-600 mt-3"></div>
</div>
<div className="flex flex-row justify-between">
<span className="text-gray-500 dark:text-gray-400 text-md">
<span className="text-gray-500 dark:text-gray-400 text-lg">
Speech Recognition Language
</span>
@ -37,7 +43,7 @@ export const SettingOther = () => {
/>
</div>
<div className="flex flex-row justify-between">
<span className="text-gray-500 dark:text-gray-400 text-md">
<span className="text-gray-500 dark:text-gray-400 text-lg">
Change Theme
</span>
@ -53,7 +59,7 @@ export const SettingOther = () => {
</button>
</div>
<div className="flex flex-row justify-between">
<span className="text-gray-500 dark:text-gray-400 text-md">
<span className="text-gray-500 dark:text-gray-400 text-lg">
Delete Chat History
</span>
@ -76,6 +82,6 @@ export const SettingOther = () => {
Delete
</button>
</div>
</div>
</dl>
)
}

View File

@ -1,5 +1,5 @@
import { useQuery, useQueryClient } from "@tanstack/react-query"
import { Skeleton, Radio, Form } from "antd"
import { Skeleton, Radio, Form, Alert } from "antd"
import React from "react"
import { SaveButton } from "~components/Common/SaveButton"
import {
@ -12,7 +12,7 @@ import {
export const SettingPrompt = () => {
const [selectedValue, setSelectedValue] = React.useState<"normal" | "web">(
"normal"
"web"
)
const queryClient = useQueryClient()
@ -41,7 +41,6 @@ export const SettingPrompt = () => {
{status === "success" && (
<div>
<h2 className="text-md font-semibold dark:text-white">Prompt</h2>
<div className="my-3 flex justify-end">
<Radio.Group
defaultValue={selectedValue}
@ -63,6 +62,14 @@ export const SettingPrompt = () => {
initialValues={{
prompt: data.prompt
}}>
<Form.Item>
<Alert
message="Configuring the system prompt here is deprecated. Please use the Manage Prompts section to add or edit prompts. This section will be removed in a future release"
type="warning"
showIcon
closable
/>
</Form.Item>
<Form.Item label="System Prompt" name="prompt">
<textarea
value={data.prompt}

View File

@ -22,7 +22,7 @@ export const EmptySidePanel = () => {
queryFn: async () => {
const ollamaURL = await getOllamaURL()
const isOk = await isOllamaRunning()
const models = await getAllModels()
const models = await getAllModels({ returnEmpty: false })
return {
isOk,

View File

@ -48,7 +48,7 @@ export const SettingsBody = () => {
getOllamaURL(),
systemPromptForNonRag(),
promptForRag(),
getAllModels(),
getAllModels({ returnEmpty: true }),
defaultEmbeddingChunkOverlap(),
defaultEmbeddingChunkSize(),
defaultEmbeddingModelForRag()

8
src/options.html Normal file
View File

@ -0,0 +1,8 @@
<!doctype html>
<html>
<head>
<title>__plasmo_static_index_title__</title>
<meta charset="utf-8" />
</head>
<body class="bg-white dark:bg-[#171717]"></body>
</html>

View File

@ -3,8 +3,10 @@ import { SidepanelChat } from "./sidepanel-chat"
import { useDarkMode } from "~hooks/useDarkmode"
import { SidepanelSettings } from "./sidepanel-settings"
import { OptionIndex } from "./option-index"
import { OptionModal } from "./option-model"
import { OptionPrompt } from "./option-prompt"
import { OptionModal } from "./option-settings-model"
import { OptionPrompt } from "./option-settings-prompt"
import { OptionOllamaSettings } from "./options-settings-ollama"
import { OptionSettings } from "./option-settings"
export const OptionRouting = () => {
const { mode } = useDarkMode()
@ -13,8 +15,10 @@ export const OptionRouting = () => {
<div className={mode === "dark" ? "dark" : "light"}>
<Routes>
<Route path="/" element={<OptionIndex />} />
<Route path="/models" element={<OptionModal />} />
<Route path="/prompts" element={<OptionPrompt />} />
<Route path="/settings" element={<OptionSettings />} />
<Route path="/settings/model" element={<OptionModal />} />
<Route path="/settings/prompt" element={<OptionPrompt />} />
<Route path="/settings/ollama" element={<OptionOllamaSettings />} />
</Routes>
</div>
)

View File

@ -1,10 +1,13 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout"
import { ModelsBody } from "~components/Option/Models"
export const OptionModal = () => {
return (
<OptionLayout>
<ModelsBody />
<SettingsLayout>
<ModelsBody />
</SettingsLayout>
</OptionLayout>
)
}

View File

@ -1,10 +1,13 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout"
import { PromptBody } from "~components/Option/Prompt"
export const OptionPrompt = () => {
return (
<OptionLayout>
<PromptBody />
<SettingsLayout>
<PromptBody />
</SettingsLayout>
</OptionLayout>
)
}

View File

@ -0,0 +1,13 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout"
import { SettingOther } from "~components/Option/Settings/other"
export const OptionSettings = () => {
return (
<OptionLayout>
<SettingsLayout>
<SettingOther />
</SettingsLayout>
</OptionLayout>
)
}

View File

@ -0,0 +1,13 @@
import { SettingsLayout } from "~components/Layouts/SettingsOptionLayout"
import OptionLayout from "~components/Option/Layout"
import { SettingsOllama } from "~components/Option/Settings/ollama"
export const OptionOllamaSettings = () => {
return (
<OptionLayout>
<SettingsLayout>
<SettingsOllama />
</SettingsLayout>
</OptionLayout>
)
}

View File

@ -60,10 +60,13 @@ export const isOllamaRunning = async () => {
}
}
export const getAllModels = async () => {
export const getAllModels = async ({ returnEmpty = false }: { returnEmpty?: boolean }) => {
const baseUrl = await getOllamaURL()
const response = await fetch(`${cleanUrl(baseUrl)}/api/tags`)
if (!response.ok) {
if (returnEmpty) {
return []
}
throw new Error(response.statusText)
}
const json = await response.json()