Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions apps/desktop/src/features/inference-server/instance-creator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { cn } from "@localai/theme/utils"
import { SpinnerButton } from "@localai/ui/button"
import { useState } from "react"

export const InstanceCreator = () => {
const [isLoading, setIsLoading] = useState(false)

const handleClick = async () => {
setIsLoading(true)
// Add your logic here to handle adding a new instance
// For example:
await createNewInstance()
setIsLoading(false)
}

return (
<div className="flex items-center justify-end gap-2">
<SpinnerButton
isSpinning={isLoading}
className={cn(
"w-48 justify-center border",
"bg-gray-5 text-gray-11 ring-gray-9 ring-2 hover:text-gray-12"
)}
onClick={handleClick}>
{isLoading ? "..." : "Create New Instance"}
</SpinnerButton>
</div>
)
}
function createNewInstance() {
throw new Error("Function not implemented.")
}
77 changes: 77 additions & 0 deletions apps/desktop/src/features/inference-server/server-list-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { cn } from "@localai/theme/utils"
import { Button } from "@localai/ui/button"
import { Input } from "@localai/ui/input"
import { Textarea } from "@localai/ui/textarea"
import {
ChevronLeftIcon,
ChevronRightIcon,
GearIcon
} from "@radix-ui/react-icons"
import dedent from "ts-dedent"

import { useToggle } from "~features/layout/use-toggle"
import { type ModelMetadata } from "~features/model-downloader/model-file"
import { useGlobal } from "~providers/global"
import { ModelProvider } from "~providers/model"

import { ServerConfig } from "./server-config"

export const ServerListItem = ({ model }: { model: ModelMetadata }) => {
const {
activeModelState: [activeModel]
} = useGlobal()
const [isConfigVisible, toggleConfig] = useToggle(false)
return (
<ModelProvider model={model}>
<div
className={cn(
"flex flex-col gap-4 rounded-md p-2 pl-3",
"text-gray-11 hover:text-gray-12",
"transition-colors group",
activeModel?.path === model.path
? "border border-green-7 hover:border-green-8"
: "border border-gray-7 hover:border-gray-8"
)}>
<div className="flex justify-between w-full">
<Button className="gap-0" onClick={() => toggleConfig()}>
{!isConfigVisible && <ChevronLeftIcon />}

<GearIcon
className={cn(
"will-change-transform",
"transition-transform",
isConfigVisible ? "rotate-180" : "-rotate-180"
)}
/>
{isConfigVisible && <ChevronRightIcon />}
</Button>
</div>
<ServerConfig />
{isConfigVisible && (
<>
<div
className={cn(
"transition-all",
isConfigVisible ? "w-1/4 opacity-100" : "w-0 opacity-0",
"border-l border-l-gray-6"
)}>
<div className="p-4 flex flex-col gap-6 overflow-auto items-start w-full">
<Textarea
rows={8}
title="Prompt template (WIP)"
defaultValue={dedent`
<BOT>: {SYSTEM}
<HUMAN>: {PROMPT}
<BOT>:
`}
/>
<Input placeholder="Temperature (WIP)" defaultValue={0.47} />
<Input placeholder="Max Tokens (WIP)" defaultValue={0.47} />
</div>
</div>
</>
)}
</div>
</ModelProvider>
)
}
4 changes: 4 additions & 0 deletions apps/desktop/src/features/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const BottomBar = () => {
on={<MoonIcon />}
off={<SunIcon />}
/>
<NavButton route={Route.ServerManager}>
<Home /> Server Manager
</NavButton>

</div>
)
}
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMemo } from "react"

import { Route, useGlobal } from "~providers/global"
import { ModelManagerView } from "~views/model-manager"
import { ServerManagerView } from "~views/server-manager"
import { ThreadView } from "~views/thread"

// Since NextJS router doesn't work with SPA yet, use manual router for now.
Expand All @@ -18,6 +19,8 @@ function IndexPage() {
case Route.ModelManager:
default:
return <ModelManagerView />
case Route.ServerManager:
return <ServerManagerView />
}
}, [currentRoute])
}
Expand Down
11 changes: 10 additions & 1 deletion apps/desktop/src/providers/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { getCachedIntegrity } from "~providers/model"

export enum Route {
ModelManager = "model-manager",
Thread = "thread"
Thread = "thread",
ServerManager = "ServerManager"
}

let _prefix: string
Expand Down Expand Up @@ -146,6 +147,14 @@ const useGlobalProvider = () => {
}
}, [activeThread, activeRoute])

useEffect(() => {
if (routeState[0] === Route.ServerManager) {
setTitle("Server Manager")
} else if (activeThreadState[0]) {
setTitle(activeThreadState[0].name.slice(0, -2))
}
}, [activeThreadState[0], routeState[0]])

return {
getWindow: () => windowRef.current,
loadModel,
Expand Down
20 changes: 20 additions & 0 deletions apps/desktop/src/providers/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client"

import { createProvider } from "puro"
import { useContext } from "react"

import type { ModelMetadata } from "~features/model-downloader/model-file"

/**
* Requires a global provider
*/
const useServerProvider = ({ model }: { model: ModelMetadata }) => {
return {
model
}
}

const { BaseContext, Provider } = createProvider(useServerProvider)

export const useServer = () => useContext(BaseContext)
export const ServerProvider = Provider
103 changes: 103 additions & 0 deletions apps/desktop/src/views/server-manager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Button, SpinnerButton } from "@localai/ui/button"
import { Input } from "@localai/ui/input"
import {
DotsHorizontalIcon,
OpenInNewWindowIcon,
ReloadIcon
} from "@radix-ui/react-icons"
import { open as dialogOpen } from "@tauri-apps/api/dialog"

import { InstanceCreator } from "~features/inference-server/instance-creator"
import { ServerListItem } from "~features/inference-server/server-list-item"
import { InvokeCommand, invoke } from "~features/invoke"
import { ViewBody, ViewContainer, ViewHeader } from "~features/layout/view"
import { ChatSideBarToggle } from "~features/thread/side-bar"
import { useGlobal } from "~providers/global"

export function ServerManagerView() {
const {
activeModelState: [activeModel],
modelsDirectoryState: {
isRefreshing,
modelsDirectory,
models,
updateModelsDirectory
}
} = useGlobal()

return (
<ViewContainer className="relative z-50">
<ViewHeader>
<ChatSideBarToggle />
<div className="flex w-full">
{!!modelsDirectory && (
<SpinnerButton
className="w-10 p-3 rounded-r-none"
Icon={ReloadIcon}
isSpinning={isRefreshing}
title="Refresh Server Instance Directory"
onClick={async () => {
await updateModelsDirectory()
}}
/>
)}
<Input
className="w-full lg:w-96 rounded-none border-gray-3"
value={modelsDirectory}
readOnly
placeholder="Server directory"
/>

<Button
title="Change server directory"
className="w-10 p-3 rounded-none"
onClick={async () => {
const selected = (await dialogOpen({
directory: true,
multiple: false
})) as string

if (!selected) {
return
}
await updateModelsDirectory(selected)
}}>
<DotsHorizontalIcon />
</Button>

<Button
title="Open server directory"
className="w-10 p-3 rounded-l-none"
onClick={() => {
invoke(InvokeCommand.OpenDirectory, {
path: modelsDirectory
})
}}>
<OpenInNewWindowIcon />
</Button>
</div>
<InstanceCreator />
</ViewHeader>
<ViewBody className="flex flex-col p-4 gap-2">
{models.length === 0 && (
<p className="text-gray-9 italic pointer-events-none text-center">
{`Here you can create and save custom instances of your models to use as servers. To begin, just click the "+" button.`}
</p>
)}
<div className="flex flex-col p-2 gap-6">
{models
.sort((a, b) =>
activeModel?.path === a.path
? -1
: activeModel?.path === b.path
? 1
: 0
)
.map((model) => (
<ServerListItem key={model.name} model={model} />
))}
</div>
</ViewBody>
</ViewContainer>
)
}
1 change: 1 addition & 0 deletions models/_shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum LicenseType {
}

export type ModelInfo = {
path: any
name?: string
size: number
downloadUrl: string
Expand Down