Skip to content

Commit

Permalink
feat: Model Providers page
Browse files Browse the repository at this point in the history
  • Loading branch information
ivyjeong13 committed Dec 5, 2024
1 parent bc35a41 commit 4f4f7af
Show file tree
Hide file tree
Showing 14 changed files with 614 additions and 17 deletions.
7 changes: 6 additions & 1 deletion pkg/api/handlers/modelprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,15 @@ func convertToolReferenceToModelProvider(ctx context.Context, gClient *gptscript
return types.ModelProvider{}, err
}

name := ref.Status.Tool.Name
if name == "" {
name = ref.Name
}

mp := types.ModelProvider{
Metadata: MetadataFrom(&ref),
ModelProviderManifest: types.ModelProviderManifest{
Name: ref.Name,
Name: name,
ToolReference: ref.Spec.Reference,
},
ModelProviderStatus: *status,
Expand Down
5 changes: 5 additions & 0 deletions ui/admin/app/components/header/HeaderNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ function RouteBreadcrumbs() {
<BreadcrumbPage>OAuth Apps</BreadcrumbPage>
</BreadcrumbItem>
)}
{routeInfo?.path === "/model-providers" && (
<BreadcrumbItem>
<BreadcrumbPage>Model Providers</BreadcrumbPage>
</BreadcrumbItem>
)}
</BreadcrumbList>
</Breadcrumb>
);
Expand Down
100 changes: 100 additions & 0 deletions ui/admin/app/components/model-providers/ModelProviderConfigure.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { BoxesIcon, SettingsIcon } from "lucide-react";
import { useState } from "react";
import useSWR from "swr";

import { ModelProvider } from "~/lib/model/modelProviders";
import { ModelProviderApiService } from "~/lib/service/api/modelProviderApiService";

import { ModelProviderForm } from "~/components/model-providers/ModelProviderForm";
import { LoadingSpinner } from "~/components/ui/LoadingSpinner";
import { Button } from "~/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";

type ModelProviderConfigureProps = {
modelProvider: ModelProvider;
};

export function ModelProviderConfigure({
modelProvider,
}: ModelProviderConfigureProps) {
const [dialogIsOpen, setDialogIsOpen] = useState(false);
const handleOpenChange = (open: boolean) => {
if (!open) {
setDialogIsOpen(false);
}
};

return (
<Dialog open={dialogIsOpen} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
<Button
size="icon"
variant="ghost"
className="mt-0"
onClick={() => setDialogIsOpen(true)}
>
<SettingsIcon />
</Button>
</DialogTrigger>

<DialogDescription hidden>
Configure Model Provider
</DialogDescription>

<DialogContent>
<ModelProviderConfigureContent
modelProvider={modelProvider}
onSuccess={() => setDialogIsOpen(false)}
/>
</DialogContent>
</Dialog>
);
}

export function ModelProviderConfigureContent({
modelProvider,
onSuccess,
}: {
modelProvider: ModelProvider;
onSuccess: () => void;
}) {
const revealModelProvider = useSWR(
ModelProviderApiService.revealModelProviderById.key(modelProvider.id),
({ modelProviderId }) =>
ModelProviderApiService.revealModelProviderById(modelProviderId)
);

const requiredParameters = modelProvider.requiredConfigurationParameters;
const parameters = revealModelProvider.data;

return revealModelProvider.isLoading ? (
<LoadingSpinner />
) : (
<>
<DialogHeader>
<DialogTitle className="mb-4 flex items-center gap-2">
<BoxesIcon />{" "}
{modelProvider.configured
? `Configure ${modelProvider.name}`
: `Set Up ${modelProvider.name}`}
</DialogTitle>
<ModelProviderForm
modelProviderId={modelProvider.id}
onSuccess={() => {
console.log("B");
onSuccess();
}}
parameters={parameters ?? {}}
requiredParameters={requiredParameters ?? []}
/>
</DialogHeader>
</>
);
}
83 changes: 83 additions & 0 deletions ui/admin/app/components/model-providers/ModelProviderContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
ReactNode,
createContext,
useCallback,
useContext,
useState,
} from "react";
import useSWR, { mutate } from "swr";

import { ModelProvider } from "~/lib/model/modelProviders";
import { ModelProviderApiService } from "~/lib/service/api/modelProviderApiService";

import { useAsync } from "~/hooks/useAsync";

interface ModelProviderContextType {
modelProviders: ModelProvider[];
configured: boolean;
configureModelProvider: (
modelProviderId: string,
value: Record<string, string>
) => void;
isUpdating: boolean;
error?: unknown;
lastUpdated?: Date;
}

const ModelProviderContext = createContext<
ModelProviderContextType | undefined
>(undefined);

export function ModelProviderProvider({ children }: { children: ReactNode }) {
const getModelProviders = useSWR(
ModelProviderApiService.getModelProviders.key(),
() => ModelProviderApiService.getModelProviders(),
{ fallbackData: [] }
);

const [lastUpdated, setLastSaved] = useState<Date>();

const handleConfigureModelProvider = useCallback(
(modelProviderId: string, values: Record<string, string>) =>
ModelProviderApiService.configureModelProviderById(
modelProviderId,
values
)
.then(() => {
getModelProviders.mutate();
mutate(ModelProviderApiService.getModelProviders.key());
setLastSaved(new Date());
})
.catch(console.error),
[getModelProviders]
);

const configureModelProvider = useAsync(handleConfigureModelProvider);
const configured = getModelProviders.data.some(
(modelProvider) => modelProvider.configured
);
return (
<ModelProviderContext.Provider
value={{
modelProviders: getModelProviders.data,
configured,
configureModelProvider: configureModelProvider.execute,
isUpdating: configureModelProvider.isLoading,
lastUpdated,
error: configureModelProvider.error,
}}
>
{children}
</ModelProviderContext.Provider>
);
}

export function useModelProviders() {
const context = useContext(ModelProviderContext);
if (context === undefined) {
throw new Error(
"useModelProvider must be used within a ModelProviderProvider"
);
}
return context;
}
Loading

0 comments on commit 4f4f7af

Please sign in to comment.