θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
2 changes: 1 addition & 1 deletion .github/workflows/dev-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ concurrency:

on:
push:
branches: ['model-map-staleness'] # put your current branch to create a build. Core team only.
branches: ['2095-model-swap-in-chat'] # put your current branch to create a build. Core team only.
paths-ignore:
- '**.md'
- 'cloud-deployments/*'
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/ModalWrapper/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import { createPortal } from "react-dom";
*/

/**
*
* @param {ModalWrapperProps} props - ModalWrapperProps to pass
* @returns {import("react").ReactNode}
*
* @todo Add a closeModal prop to the ModalWrapper component so we can escape dismiss anywhere this is used
*/
export default function ModalWrapper({ children, isOpen, noPortal = false }) {
if (!isOpen) return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import useGetProviderModels, {
DISABLED_PROVIDERS,
} from "@/hooks/useGetProvidersModels";
import { useTranslation } from "react-i18next";

export default function ChatModelSelection({
provider,
setHasChanges,
selectedLLMModel,
setSelectedLLMModel,
}) {
const { defaultModels, customModels, loading } =
useGetProviderModels(provider);
const { t } = useTranslation();
if (DISABLED_PROVIDERS.includes(provider)) return null;

if (loading) {
return (
<div>
<div className="flex flex-col">
<label htmlFor="name" className="block input-label">
{t("chat_window.workspace_llm_manager.available_models", {
provider,
})}
</label>
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
{t(
"chat_window.workspace_llm_manager.available_models_description"
)}
</p>
</div>
<select
required={true}
disabled={true}
className="border-theme-modal-border border border-solid bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
>
<option disabled={true} selected={true}>
-- waiting for models --
</option>
</select>
</div>
);
}

return (
<div>
<div className="flex flex-col">
<label htmlFor="name" className="block input-label">
{t("chat_window.workspace_llm_manager.available_models", {
provider,
})}
</label>
<p className="text-white text-opacity-60 text-xs font-medium py-1.5">
{t("chat_window.workspace_llm_manager.available_models_description")}
</p>
</div>

<select
id="workspace-llm-model-select"
required={true}
value={selectedLLMModel}
onChange={(e) => {
setHasChanges(true);
setSelectedLLMModel(e.target.value);
}}
className="border-theme-modal-border border border-solid bg-theme-settings-input-bg text-white text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
>
{defaultModels.length > 0 && (
<optgroup label="General models">
{defaultModels.map((model) => {
return (
<option
key={model}
value={model}
selected={selectedLLMModel === model}
>
{model}
</option>
);
})}
</optgroup>
)}
{Array.isArray(customModels) && customModels.length > 0 && (
<optgroup label="Discovered models">
{customModels.map((model) => {
return (
<option
key={model.id}
value={model.id}
selected={selectedLLMModel === model.id}
>
{model.id}
</option>
);
})}
</optgroup>
)}
{/* For providers like TogetherAi where we partition model by creator entity. */}
{!Array.isArray(customModels) &&
Object.keys(customModels).length > 0 && (
<>
{Object.entries(customModels).map(([organization, models]) => (
<optgroup key={organization} label={organization}>
{models.map((model) => (
<option
key={model.id}
value={model.id}
selected={selectedLLMModel === model.id}
>
{model.name}
</option>
))}
</optgroup>
))}
</>
)}
</select>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useTranslation } from "react-i18next";

export default function LLMSelectorSidePanel({
availableProviders,
selectedLLMProvider,
onSearchChange,
onProviderClick,
}) {
const { t } = useTranslation();

return (
<div className="w-[40%] h-full flex flex-col gap-y-1 border-r-2 border-theme-modal-border py-2 px-[5px]">
<input
id="llm-search-input"
type="search"
placeholder={t("chat_window.workspace_llm_manager.search")}
onChange={onSearchChange}
className="search-input bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder outline-none text-sm rounded-lg px-2 py-2 w-full h-[32px] border-theme-modal-border border border-solid"
/>
<div className="flex flex-col gap-y-2 overflow-y-scroll ">
{availableProviders.map((llm) => (
<button
key={llm.value}
type="button"
data-llm-value={llm.value}
className={`border-none hover:cursor-pointer hover:bg-theme-checklist-item-bg-hover flex gap-x-2 items-center p-2 rounded-md ${selectedLLMProvider === llm.value ? "bg-theme-checklist-item-bg" : ""}`}
onClick={() => onProviderClick(llm.value)}
>
<img
src={llm.logo}
alt={`${llm.name} logo`}
className="w-6 h-6 rounded-md"
/>
<div className="flex flex-col">
<div className="text-xs text-theme-text-primary">{llm.name}</div>
</div>
</button>
))}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { createPortal } from "react-dom";
import ModalWrapper from "@/components/ModalWrapper";
import { X } from "@phosphor-icons/react";
import System from "@/models/system";
import showToast from "@/utils/toast";
import { useTranslation } from "react-i18next";

export default function SetupProvider({
isOpen,
closeModal,
postSubmit,
settings,
llmProvider,
}) {
if (!isOpen) 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 ${llmProvider.name} settings: ${error}`,
"error"
);
return;
}

closeModal();
postSubmit();
return false;
}

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">
{llmProvider.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 {llmProvider.name} as this workspace's LLM you need to
set it up first.
</p>
<div>
{llmProvider.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 settings
</button>
</div>
</form>
</div>
</div>
</ModalWrapper>,
document.body
);
}

export function NoSetupWarning({ showing, onSetupClick }) {
const { t } = useTranslation();
if (!showing) return null;

return (
<button
type="button"
onClick={onSetupClick}
className="border border-blue-500 rounded-lg p-2 flex flex-col items-center gap-y-2 bg-blue-600/10 text-blue-600 hover:bg-blue-600/20 transition-all duration-300"
>
<p className="text-sm text-center">
<b>{t("chat_window.workspace_llm_manager.missing_credentials")}</b>
</p>
<p className="text-xs text-center">
{t("chat_window.workspace_llm_manager.missing_credentials_description")}
</p>
</button>
);
}
Loading