gaojintao01
Add files using Git LFS
f8b5d42
// This component differs from the main LLMItem in that it shows if a provider is
// "ready for use" and if not - will then highjack the click handler to show a modal
// of the provider options that must be saved to continue.
import { createPortal } from "react-dom";
import ModalWrapper from "@/components/ModalWrapper";
import { useModal } from "@/hooks/useModal";
import { X, Gear } from "@phosphor-icons/react";
import System from "@/models/system";
import showToast from "@/utils/toast";
import { useEffect, useState } from "react";
const NO_SETTINGS_NEEDED = ["default", "none"];
export default function AgentLLMItem({
llm,
availableLLMs,
settings,
checked,
onClick,
}) {
const { isOpen, openModal, closeModal } = useModal();
const { name, value, logo, description } = llm;
const [currentSettings, setCurrentSettings] = useState(settings);
useEffect(() => {
async function getSettings() {
if (isOpen) {
const _settings = await System.keys();
setCurrentSettings(_settings ?? {});
}
}
getSettings();
}, [isOpen]);
function handleProviderSelection() {
// Determine if provider needs additional setup because its minimum required keys are
// not yet set in settings.
if (!checked) {
const requiresAdditionalSetup = (llm.requiredConfig || []).some(
(key) => !currentSettings[key]
);
if (requiresAdditionalSetup) {
openModal();
return;
}
onClick(value);
}
}
return (
<>
<div
onClick={handleProviderSelection}
className={`w-full p-2 rounded-md hover:cursor-pointer hover:bg-theme-bg-secondary ${
checked ? "bg-theme-bg-secondary" : ""
}`}
>
<input
type="checkbox"
value={value}
className="peer hidden"
checked={checked}
readOnly={true}
formNoValidate={true}
/>
<div className="flex gap-x-4 items-center justify-between">
<div className="flex gap-x-4 items-center">
<img
src={logo}
alt={`${name} logo`}
className="w-10 h-10 rounded-md"
/>
<div className="flex flex-col">
<div className="text-sm font-semibold text-white">{name}</div>
<div className="mt-1 text-xs text-white/60">{description}</div>
</div>
</div>
{checked &&
value !== "none" &&
!NO_SETTINGS_NEEDED.includes(value) && (
<button
onClick={(e) => {
e.preventDefault();
openModal();
}}
className="border-none p-2 text-white/60 hover:text-white hover:bg-theme-bg-hover rounded-md transition-all duration-300"
title="Edit Settings"
>
<Gear size={20} weight="bold" />
</button>
)}
</div>
</div>
<SetupProvider
availableLLMs={availableLLMs}
isOpen={isOpen}
provider={value}
closeModal={closeModal}
postSubmit={onClick}
settings={currentSettings}
/>
</>
);
}
function SetupProvider({
availableLLMs,
isOpen,
provider,
closeModal,
postSubmit,
settings,
}) {
if (!isOpen) return null;
const LLMOption = availableLLMs.find((llm) => llm.value === provider);
if (!LLMOption) return null;
async function handleUpdate(e) {
e.preventDefault();
e.stopPropagation();
const data = {};
const form = new FormData(e.target);
for (var [key, value] of form.entries()) data[key] = value;
const { error } = await System.updateSystem(data);
if (error) {
showToast(`Failed to save ${LLMOption.name} settings: ${error}`, "error");
return;
}
closeModal();
postSubmit();
return false;
}
// Cannot do nested forms, it will cause all sorts of issues, so we portal this out
// to the parent container form so we don't have nested forms.
return createPortal(
<ModalWrapper isOpen={isOpen}>
<div className="fixed inset-0 z-50 overflow-auto bg-black bg-opacity-50 flex items-center justify-center">
<div className="relative w-full max-w-2xl bg-theme-bg-secondary rounded-lg shadow border-2 border-theme-modal-border">
<div className="relative p-6 border-b rounded-t border-theme-modal-border">
<div className="w-full flex gap-x-2 items-center">
<h3 className="text-xl font-semibold text-white overflow-hidden overflow-ellipsis whitespace-nowrap">
{LLMOption.name} Settings
</h3>
</div>
<button
onClick={closeModal}
type="button"
className="absolute top-4 right-4 transition-all duration-300 bg-transparent rounded-lg text-sm p-1 inline-flex items-center hover:bg-theme-modal-border hover:border-theme-modal-border hover:border-opacity-50 border-transparent border"
>
<X size={24} weight="bold" className="text-white" />
</button>
</div>
<form id="provider-form" onSubmit={handleUpdate}>
<div className="px-7 py-6">
<div className="space-y-6 max-h-[60vh] overflow-y-auto p-1">
<p className="text-sm text-white/60">
To use {LLMOption.name} as this workspace's agent LLM you need
to set it up first.
</p>
<div>
{LLMOption.options(settings, { credentialsOnly: true })}
</div>
</div>
</div>
<div className="flex justify-between items-center mt-6 pt-6 border-t border-theme-modal-border px-7 pb-6">
<button
type="button"
onClick={closeModal}
className="transition-all duration-300 text-white hover:bg-zinc-700 px-4 py-2 rounded-lg text-sm"
>
Cancel
</button>
<button
type="submit"
form="provider-form"
className="transition-all duration-300 bg-white text-black hover:opacity-60 px-4 py-2 rounded-lg text-sm"
>
Save {LLMOption.name} settings
</button>
</div>
</form>
</div>
</div>
</ModalWrapper>,
document.getElementById("workspace-agent-settings-container")
);
}