mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-10-05 17:53:54 +02:00
Configurable models + updated assistants bar (#1942)
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
"""Add display_model_names to llm_provider
|
||||
|
||||
Revision ID: 473a1a7ca408
|
||||
Revises: 325975216eb3
|
||||
Create Date: 2024-07-25 14:31:02.002917
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "473a1a7ca408"
|
||||
down_revision = "325975216eb3"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
default_models_by_provider = {
|
||||
"openai": ["gpt-4", "gpt-4o", "gpt-4o-mini"],
|
||||
"bedrock": [
|
||||
"meta.llama3-1-70b-instruct-v1:0",
|
||||
"meta.llama3-1-8b-instruct-v1:0",
|
||||
"anthropic.claude-3-opus-20240229-v1:0",
|
||||
"mistral.mistral-large-2402-v1:0",
|
||||
"anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
],
|
||||
"anthropic": ["claude-3-opus-20240229", "claude-3-5-sonnet-20240620"],
|
||||
}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"llm_provider",
|
||||
sa.Column("display_model_names", postgresql.ARRAY(sa.String()), nullable=True),
|
||||
)
|
||||
|
||||
connection = op.get_bind()
|
||||
for provider, models in default_models_by_provider.items():
|
||||
connection.execute(
|
||||
sa.text(
|
||||
"UPDATE llm_provider SET display_model_names = :models WHERE provider = :provider"
|
||||
),
|
||||
{"models": models, "provider": provider},
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("llm_provider", "display_model_names")
|
@@ -76,6 +76,7 @@ def upsert_llm_provider(
|
||||
existing_llm_provider.fast_default_model_name = llm_provider.fast_default_model_name
|
||||
existing_llm_provider.model_names = llm_provider.model_names
|
||||
existing_llm_provider.is_public = llm_provider.is_public
|
||||
existing_llm_provider.display_model_names = llm_provider.display_model_names
|
||||
|
||||
if not existing_llm_provider.id:
|
||||
# If its not already in the db, we need to generate an ID by flushing
|
||||
|
@@ -928,6 +928,11 @@ class LLMProvider(Base):
|
||||
default_model_name: Mapped[str] = mapped_column(String)
|
||||
fast_default_model_name: Mapped[str | None] = mapped_column(String, nullable=True)
|
||||
|
||||
# Models to actually disp;aly to users
|
||||
# If nulled out, we assume in the application logic we should present all
|
||||
display_model_names: Mapped[list[str] | None] = mapped_column(
|
||||
postgresql.ARRAY(String), nullable=True
|
||||
)
|
||||
# The LLMs that are available for this provider. Only required if not a default provider.
|
||||
# If a default provider, then the LLM options are pulled from the `options.py` file.
|
||||
# If needed, can be pulled out as a separate table in the future.
|
||||
|
@@ -103,6 +103,7 @@ def port_api_key_to_postgres() -> None:
|
||||
default_model_name=default_model_name,
|
||||
fast_default_model_name=default_fast_model_name,
|
||||
model_names=None,
|
||||
display_model_names=[],
|
||||
is_public=True,
|
||||
)
|
||||
llm_provider = upsert_llm_provider(db_session, llm_provider_upsert)
|
||||
|
@@ -71,6 +71,7 @@ def load_llm_providers(db_session: Session) -> None:
|
||||
),
|
||||
model_names=model_names,
|
||||
is_public=True,
|
||||
display_model_names=[],
|
||||
)
|
||||
llm_provider = upsert_llm_provider(db_session, llm_provider_request)
|
||||
update_default_provider(db_session, llm_provider.id)
|
||||
|
@@ -31,9 +31,7 @@ OPEN_AI_MODEL_NAMES = [
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-4-vision-preview",
|
||||
# "gpt-4-32k", # not EOL but still doesnt work
|
||||
"gpt-4-0613",
|
||||
# "gpt-4-32k-0613", # not EOL but still doesnt work
|
||||
"gpt-4-0314",
|
||||
"gpt-4-32k-0314",
|
||||
"gpt-3.5-turbo",
|
||||
@@ -48,9 +46,11 @@ OPEN_AI_MODEL_NAMES = [
|
||||
BEDROCK_PROVIDER_NAME = "bedrock"
|
||||
# need to remove all the weird "bedrock/eu-central-1/anthropic.claude-v1" named
|
||||
# models
|
||||
BEDROCK_MODEL_NAMES = [model for model in litellm.bedrock_models if "/" not in model][
|
||||
::-1
|
||||
]
|
||||
BEDROCK_MODEL_NAMES = [
|
||||
model
|
||||
for model in litellm.bedrock_models
|
||||
if "/" not in model and "embed" not in model
|
||||
][::-1]
|
||||
|
||||
IGNORABLE_ANTHROPIC_MODELS = [
|
||||
"claude-2",
|
||||
|
@@ -32,6 +32,7 @@ class LLMProviderDescriptor(BaseModel):
|
||||
default_model_name: str
|
||||
fast_default_model_name: str | None
|
||||
is_default_provider: bool | None
|
||||
display_model_names: list[str] | None
|
||||
|
||||
@classmethod
|
||||
def from_model(
|
||||
@@ -48,6 +49,7 @@ class LLMProviderDescriptor(BaseModel):
|
||||
or fetch_models_for_provider(llm_provider_model.provider)
|
||||
or [llm_provider_model.default_model_name]
|
||||
),
|
||||
display_model_names=llm_provider_model.display_model_names,
|
||||
)
|
||||
|
||||
|
||||
@@ -62,6 +64,7 @@ class LLMProvider(BaseModel):
|
||||
fast_default_model_name: str | None
|
||||
is_public: bool = True
|
||||
groups: list[int] | None = None
|
||||
display_model_names: list[str] | None
|
||||
|
||||
|
||||
class LLMProviderUpsertRequest(LLMProvider):
|
||||
@@ -88,6 +91,7 @@ class FullLLMProvider(LLMProvider):
|
||||
default_model_name=llm_provider_model.default_model_name,
|
||||
fast_default_model_name=llm_provider_model.fast_default_model_name,
|
||||
is_default_provider=llm_provider_model.is_default_provider,
|
||||
display_model_names=llm_provider_model.display_model_names,
|
||||
model_names=(
|
||||
llm_provider_model.model_names
|
||||
or fetch_models_for_provider(llm_provider_model.provider)
|
||||
|
@@ -20,6 +20,7 @@ import {
|
||||
TextFormField,
|
||||
} from "@/components/admin/connectors/Field";
|
||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||
import { getDisplayNameForModel } from "@/lib/hooks";
|
||||
import { Bubble } from "@/components/Bubble";
|
||||
import { DocumentSetSelectable } from "@/components/documentSet/DocumentSetSelectable";
|
||||
import { Option } from "@/components/Dropdown";
|
||||
@@ -152,7 +153,7 @@ export function AssistantEditor({
|
||||
llmProviders.forEach((llmProvider) => {
|
||||
const providerOptions = llmProvider.model_names.map((modelName) => {
|
||||
return {
|
||||
name: modelName,
|
||||
name: getDisplayNameForModel(modelName),
|
||||
value: modelName,
|
||||
};
|
||||
});
|
||||
|
@@ -8,12 +8,17 @@ import {
|
||||
SelectorFormField,
|
||||
TextFormField,
|
||||
BooleanFormField,
|
||||
MultiSelectField,
|
||||
} from "@/components/admin/connectors/Field";
|
||||
import { useState } from "react";
|
||||
import { Bubble } from "@/components/Bubble";
|
||||
import { GroupsIcon } from "@/components/icons/icons";
|
||||
import { useSWRConfig } from "swr";
|
||||
import { useUserGroups } from "@/lib/hooks";
|
||||
import {
|
||||
defaultModelsByProvider,
|
||||
getDisplayNameForModel,
|
||||
useUserGroups,
|
||||
} from "@/lib/hooks";
|
||||
import { FullLLMProvider, WellKnownLLMProviderDescriptor } from "./interfaces";
|
||||
import { PopupSpec } from "@/components/admin/connectors/Popup";
|
||||
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
||||
@@ -69,12 +74,12 @@ export function LLMProviderUpdateForm({
|
||||
),
|
||||
is_public: existingLlmProvider?.is_public ?? true,
|
||||
groups: existingLlmProvider?.groups ?? [],
|
||||
display_model_names:
|
||||
existingLlmProvider?.display_model_names ||
|
||||
defaultModelsByProvider[llmProviderDescriptor.name] ||
|
||||
[],
|
||||
};
|
||||
|
||||
const [validatedConfig, setValidatedConfig] = useState(
|
||||
existingLlmProvider ? initialValues : null
|
||||
);
|
||||
|
||||
// Setup validation schema if required
|
||||
const validationSchema = Yup.object({
|
||||
name: Yup.string().required("Display Name is required"),
|
||||
@@ -109,6 +114,7 @@ export function LLMProviderUpdateForm({
|
||||
// EE Only
|
||||
is_public: Yup.boolean().required(),
|
||||
groups: Yup.array().of(Yup.number()),
|
||||
display_model_names: Yup.array().of(Yup.string()),
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -270,7 +276,7 @@ export function LLMProviderUpdateForm({
|
||||
subtext="The model to use by default for this provider unless otherwise specified."
|
||||
label="Default Model"
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
name,
|
||||
name: getDisplayNameForModel(name),
|
||||
value: name,
|
||||
}))}
|
||||
maxHeight="max-h-56"
|
||||
@@ -292,7 +298,7 @@ export function LLMProviderUpdateForm({
|
||||
the Default Model configured above.`}
|
||||
label="[Optional] Fast Model"
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
name,
|
||||
name: getDisplayNameForModel(name),
|
||||
value: name,
|
||||
}))}
|
||||
includeDefault
|
||||
@@ -311,13 +317,33 @@ export function LLMProviderUpdateForm({
|
||||
|
||||
<Divider />
|
||||
|
||||
<AdvancedOptionsToggle
|
||||
showAdvancedOptions={showAdvancedOptions}
|
||||
setShowAdvancedOptions={setShowAdvancedOptions}
|
||||
/>
|
||||
{llmProviderDescriptor.name != "azure" && (
|
||||
<AdvancedOptionsToggle
|
||||
showAdvancedOptions={showAdvancedOptions}
|
||||
setShowAdvancedOptions={setShowAdvancedOptions}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showAdvancedOptions && (
|
||||
<>
|
||||
{llmProviderDescriptor.llm_names.length > 0 && (
|
||||
<div className="w-full">
|
||||
<MultiSelectField
|
||||
selectedInitially={values.display_model_names}
|
||||
name="display_model_names"
|
||||
label="Display Models"
|
||||
subtext="Select the models to make available to users. Unselected models will not be available."
|
||||
options={llmProviderDescriptor.llm_names.map((name) => ({
|
||||
value: name,
|
||||
label: getDisplayNameForModel(name),
|
||||
}))}
|
||||
onChange={(selected) =>
|
||||
setFieldValue("display_model_names", selected)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isPaidEnterpriseFeaturesEnabled && userGroups && (
|
||||
<>
|
||||
<BooleanFormField
|
||||
|
@@ -32,6 +32,7 @@ export interface LLMProvider {
|
||||
fast_default_model_name: string | null;
|
||||
is_public: boolean;
|
||||
groups: number[];
|
||||
display_model_names: string[] | null;
|
||||
}
|
||||
|
||||
export interface FullLLMProvider extends LLMProvider {
|
||||
@@ -50,4 +51,5 @@ export interface LLMProviderDescriptor {
|
||||
is_default_provider: boolean | null;
|
||||
is_public: boolean;
|
||||
groups: number[];
|
||||
display_model_names: string[] | null;
|
||||
}
|
||||
|
@@ -1,7 +1,14 @@
|
||||
import { User } from "@/lib/types";
|
||||
import { Persona } from "../admin/assistants/interfaces";
|
||||
import { checkUserOwnsAssistant } from "@/lib/assistants/checkOwnership";
|
||||
import { FiLock, FiUnlock } from "react-icons/fi";
|
||||
import {
|
||||
FiImage,
|
||||
FiLock,
|
||||
FiMoreHorizontal,
|
||||
FiSearch,
|
||||
FiUnlock,
|
||||
} from "react-icons/fi";
|
||||
import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
|
||||
|
||||
export function AssistantSharedStatusDisplay({
|
||||
assistant,
|
||||
@@ -49,6 +56,20 @@ export function AssistantSharedStatusDisplay({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="relative mt-4 text-xs flex text-subtle">
|
||||
<span className="font-medium">Powers:</span>{" "}
|
||||
{assistant.tools.length == 0 ? (
|
||||
<p className="ml-2">None</p>
|
||||
) : (
|
||||
assistant.tools.map((tool, ind) => {
|
||||
if (tool.name === "SearchTool") {
|
||||
return <FiSearch key={ind} className="ml-1 h-3 w-3 my-auto" />;
|
||||
} else if (tool.name === "ImageGenerationTool") {
|
||||
return <FiImage key={ind} className="ml-1 h-3 w-3 my-auto" />;
|
||||
}
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -1,6 +1,9 @@
|
||||
import { Bubble } from "@/components/Bubble";
|
||||
import { ToolSnapshot } from "@/lib/tools/interfaces";
|
||||
import { FiImage, FiSearch, FiGlobe } from "react-icons/fi";
|
||||
import { FiImage, FiSearch, FiGlobe, FiMoreHorizontal } from "react-icons/fi";
|
||||
import { Persona } from "../admin/assistants/interfaces";
|
||||
import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
|
||||
import { useState } from "react";
|
||||
|
||||
export function ToolsDisplay({ tools }: { tools: ToolSnapshot[] }) {
|
||||
return (
|
||||
@@ -33,3 +36,97 @@ export function ToolsDisplay({ tools }: { tools: ToolSnapshot[] }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function AssistantTools({
|
||||
assistant,
|
||||
list,
|
||||
hovered,
|
||||
}: {
|
||||
assistant: Persona;
|
||||
list?: boolean;
|
||||
hovered?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<div className="relative text-xs flex text-subtle">
|
||||
<span
|
||||
className={`${assistant.tools.length > 0 && "py-1"} ${!list ? "font-semibold" : "text-subtle text-sm"}`}
|
||||
>
|
||||
Tools:
|
||||
</span>{" "}
|
||||
{assistant.tools.length == 0 ? (
|
||||
<p className="ml-1">None</p>
|
||||
) : (
|
||||
<div className="ml-1 flex flex-wrap gap-1">
|
||||
{assistant.tools.map((tool, ind) => {
|
||||
if (tool.name === "SearchTool") {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className={`
|
||||
px-1.5
|
||||
py-1
|
||||
rounded-lg
|
||||
border
|
||||
border-border
|
||||
w-fit
|
||||
flex
|
||||
items-center
|
||||
${hovered ? "bg-background-300" : list ? "bg-background-125" : "bg-background-100"}
|
||||
cursor-pointer`}
|
||||
>
|
||||
<div className="flex gap-x-1">
|
||||
<FiSearch key={ind} className="ml-1 h-3 w-3 my-auto" />
|
||||
Search
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (tool.name === "ImageGenerationTool") {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className={`
|
||||
px-1.5
|
||||
py-1
|
||||
rounded-lg
|
||||
border
|
||||
border-border
|
||||
w-fit
|
||||
flex
|
||||
${hovered ? "bg-background-300" : list ? "bg-background-125" : "bg-background-100"}
|
||||
cursor-pointer`}
|
||||
>
|
||||
<div className="flex items-center gap-x-1">
|
||||
<FiImage
|
||||
key={ind}
|
||||
className="ml-1 my-auto h-3 w-3 my-auto"
|
||||
/>
|
||||
Image Generation
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
key={ind}
|
||||
className={`
|
||||
px-1.5
|
||||
py-1
|
||||
rounded-lg
|
||||
border
|
||||
border-border
|
||||
w-fit
|
||||
flex
|
||||
items-center
|
||||
${hovered ? "bg-background-300" : list ? "bg-background-125" : "bg-background-100"}
|
||||
cursor-pointer`}
|
||||
>
|
||||
<div className="flex gap-x-1">{tool.name}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@ import {
|
||||
} from "@/lib/assistants/updateAssistantPreferences";
|
||||
import { usePopup } from "@/components/admin/connectors/Popup";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ToolsDisplay } from "../ToolsDisplay";
|
||||
import { AssistantTools, ToolsDisplay } from "../ToolsDisplay";
|
||||
|
||||
export function AssistantsGallery({
|
||||
assistants,
|
||||
@@ -99,7 +99,6 @@ export function AssistantsGallery({
|
||||
className="
|
||||
text-xl
|
||||
font-semibold
|
||||
mb-2
|
||||
my-auto
|
||||
ml-2
|
||||
text-strong
|
||||
@@ -189,13 +188,14 @@ export function AssistantsGallery({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{assistant.tools.length > 0 && (
|
||||
<ToolsDisplay tools={assistant.tools} />
|
||||
)}
|
||||
|
||||
<p className="text-sm mt-2">{assistant.description}</p>
|
||||
<p className="text-subtle text-sm mt-2">
|
||||
<p className="text-subtle text-sm my-2">
|
||||
Author: {assistant.owner?.email || "Danswer"}
|
||||
</p>
|
||||
{assistant.tools.length > 0 && (
|
||||
<AssistantTools list assistant={assistant} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
@@ -13,6 +13,7 @@ import {
|
||||
FiSearch,
|
||||
FiX,
|
||||
FiShare2,
|
||||
FiImage,
|
||||
} from "react-icons/fi";
|
||||
import Link from "next/link";
|
||||
import { orderAssistantsForUser } from "@/lib/assistants/orderAssistants";
|
||||
@@ -33,7 +34,8 @@ import { AssistantSharingModal } from "./AssistantSharingModal";
|
||||
import { AssistantSharedStatusDisplay } from "../AssistantSharedStatus";
|
||||
import useSWR from "swr";
|
||||
import { errorHandlingFetcher } from "@/lib/fetcher";
|
||||
import { ToolsDisplay } from "../ToolsDisplay";
|
||||
import { AssistantTools, ToolsDisplay } from "../ToolsDisplay";
|
||||
import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
|
||||
|
||||
function AssistantListItem({
|
||||
assistant,
|
||||
@@ -73,181 +75,188 @@ function AssistantListItem({
|
||||
show={showSharingModal}
|
||||
/>
|
||||
<div
|
||||
className="
|
||||
bg-background-emphasis
|
||||
className="flex bg-background-emphasis
|
||||
rounded-lg
|
||||
shadow-md
|
||||
p-4
|
||||
mb-4
|
||||
mb-4 flex-col"
|
||||
>
|
||||
<div
|
||||
className="
|
||||
|
||||
flex
|
||||
justify-between
|
||||
items-center
|
||||
"
|
||||
>
|
||||
<div className="w-3/4">
|
||||
<div className="flex items-center">
|
||||
<AssistantIcon assistant={assistant} />
|
||||
<h2 className="text-xl font-semibold my-auto ml-2">
|
||||
{assistant.name}
|
||||
</h2>
|
||||
</div>
|
||||
{assistant.tools.length > 0 && (
|
||||
<ToolsDisplay tools={assistant.tools} />
|
||||
)}
|
||||
<div className="text-sm mt-2">{assistant.description}</div>
|
||||
<div className="mt-2">
|
||||
<AssistantSharedStatusDisplay assistant={assistant} user={user} />
|
||||
</div>
|
||||
</div>
|
||||
{isOwnedByUser && (
|
||||
<div className="ml-auto flex items-center">
|
||||
{!assistant.is_public && (
|
||||
<div
|
||||
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
|
||||
onClick={() => setShowSharingModal(true)}
|
||||
>
|
||||
<FiShare2 size={16} />
|
||||
</div>
|
||||
)}
|
||||
<Link
|
||||
href={`/assistants/edit/${assistant.id}`}
|
||||
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
|
||||
>
|
||||
<FiEdit2 size={16} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<DefaultPopover
|
||||
content={
|
||||
<div className="hover:bg-hover rounded p-2 cursor-pointer">
|
||||
<FiMoreHorizontal size={16} />
|
||||
</div>
|
||||
}
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={5}
|
||||
>
|
||||
{[
|
||||
...(!isFirst
|
||||
? [
|
||||
<div
|
||||
key="move-up"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
const success = await moveAssistantUp(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been moved up.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be moved up.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiArrowUp /> Move Up
|
||||
</div>,
|
||||
]
|
||||
: []),
|
||||
...(!isLast
|
||||
? [
|
||||
<div
|
||||
key="move-down"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
const success = await moveAssistantDown(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been moved down.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be moved down.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiArrowDown /> Move Down
|
||||
</div>,
|
||||
]
|
||||
: []),
|
||||
isVisible ? (
|
||||
<div
|
||||
key="remove"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
if (
|
||||
currentChosenAssistants &&
|
||||
currentChosenAssistants.length === 1
|
||||
) {
|
||||
setPopup({
|
||||
message: `Cannot remove "${assistant.name}" - you must have at least one assistant.`,
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
<div className="w-3/4">
|
||||
<div className="flex items-center">
|
||||
<AssistantIcon assistant={assistant} />
|
||||
<h2 className="text-xl font-semibold my-auto ml-2">
|
||||
{assistant.name}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
const success = await removeAssistantFromList(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been removed from your list.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be removed from your list.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
<div className="text-sm mt-2">{assistant.description}</div>
|
||||
<div className="mt-2 flex items-center gap-x-3">
|
||||
<AssistantSharedStatusDisplay assistant={assistant} user={user} />
|
||||
{assistant.tools.length != 0 && (
|
||||
<AssistantTools list assistant={assistant} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isOwnedByUser && (
|
||||
<div className="ml-auto flex items-center">
|
||||
{!assistant.is_public && (
|
||||
<div
|
||||
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
|
||||
onClick={() => setShowSharingModal(true)}
|
||||
>
|
||||
<FiShare2 size={16} />
|
||||
</div>
|
||||
)}
|
||||
<Link
|
||||
href={`/assistants/edit/${assistant.id}`}
|
||||
className="mr-4 rounded p-2 cursor-pointer hover:bg-hover"
|
||||
>
|
||||
<FiX /> {isOwnedByUser ? "Hide" : "Remove"}
|
||||
<FiEdit2 size={16} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DefaultPopover
|
||||
content={
|
||||
<div className="hover:bg-hover rounded p-2 cursor-pointer">
|
||||
<FiMoreHorizontal size={16} />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
key="add"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
const success = await addAssistantToList(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been added to your list.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be added to your list.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiPlus /> Add
|
||||
</div>
|
||||
),
|
||||
]}
|
||||
</DefaultPopover>
|
||||
}
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={5}
|
||||
>
|
||||
{[
|
||||
...(!isFirst
|
||||
? [
|
||||
<div
|
||||
key="move-up"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
const success = await moveAssistantUp(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been moved up.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be moved up.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiArrowUp /> Move Up
|
||||
</div>,
|
||||
]
|
||||
: []),
|
||||
...(!isLast
|
||||
? [
|
||||
<div
|
||||
key="move-down"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
const success = await moveAssistantDown(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been moved down.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be moved down.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiArrowDown /> Move Down
|
||||
</div>,
|
||||
]
|
||||
: []),
|
||||
isVisible ? (
|
||||
<div
|
||||
key="remove"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
if (
|
||||
currentChosenAssistants &&
|
||||
currentChosenAssistants.length === 1
|
||||
) {
|
||||
setPopup({
|
||||
message: `Cannot remove "${assistant.name}" - you must have at least one assistant.`,
|
||||
type: "error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const success = await removeAssistantFromList(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been removed from your list.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be removed from your list.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiX /> {isOwnedByUser ? "Hide" : "Remove"}
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
key="add"
|
||||
className="flex items-center gap-x-2"
|
||||
onClick={async () => {
|
||||
const success = await addAssistantToList(
|
||||
assistant.id,
|
||||
currentChosenAssistants || allAssistantIds
|
||||
);
|
||||
if (success) {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" has been added to your list.`,
|
||||
type: "success",
|
||||
});
|
||||
router.refresh();
|
||||
} else {
|
||||
setPopup({
|
||||
message: `"${assistant.name}" could not be added to your list.`,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FiPlus /> Add
|
||||
</div>
|
||||
),
|
||||
]}
|
||||
</DefaultPopover>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@@ -2,7 +2,11 @@ import React, { useEffect, useRef, useState } from "react";
|
||||
import { FiPlusCircle, FiPlus, FiInfo, FiX } from "react-icons/fi";
|
||||
import { ChatInputOption } from "./ChatInputOption";
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { FilterManager, LlmOverrideManager } from "@/lib/hooks";
|
||||
import {
|
||||
FilterManager,
|
||||
getDisplayNameForModel,
|
||||
LlmOverrideManager,
|
||||
} from "@/lib/hooks";
|
||||
import { SelectedFilterDisplay } from "./SelectedFilterDisplay";
|
||||
import { useChatContext } from "@/components/context/ChatContext";
|
||||
import { getFinalLLM } from "@/lib/llm/utils";
|
||||
@@ -428,8 +432,16 @@ export function ChatInputBar({
|
||||
</Popup>
|
||||
|
||||
<Popup
|
||||
tab
|
||||
content={(close, ref) => (
|
||||
<LlmTab
|
||||
currentLlm={
|
||||
llmOverrideManager.llmOverride.modelName ||
|
||||
(selectedAssistant
|
||||
? selectedAssistant.llm_model_version_override ||
|
||||
llmName
|
||||
: llmName)
|
||||
}
|
||||
close={close}
|
||||
ref={ref}
|
||||
llmOverrideManager={llmOverrideManager}
|
||||
@@ -441,12 +453,13 @@ export function ChatInputBar({
|
||||
>
|
||||
<ChatInputOption
|
||||
flexPriority="second"
|
||||
name={
|
||||
name={getDisplayNameForModel(
|
||||
llmOverrideManager.llmOverride.modelName ||
|
||||
(selectedAssistant
|
||||
? selectedAssistant.llm_model_version_override || llmName
|
||||
: llmName)
|
||||
}
|
||||
(selectedAssistant
|
||||
? selectedAssistant.llm_model_version_override ||
|
||||
llmName
|
||||
: llmName)
|
||||
)}
|
||||
Icon={CpuIconSkeleton}
|
||||
/>
|
||||
</Popup>
|
||||
|
@@ -1,10 +1,12 @@
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { LLMProviderDescriptor } from "@/app/admin/models/llm/interfaces";
|
||||
import { AssistantTools } from "@/app/assistants/ToolsDisplay";
|
||||
import { Bubble } from "@/components/Bubble";
|
||||
import { AssistantIcon } from "@/components/assistants/AssistantIcon";
|
||||
import { getDisplayNameForModel } from "@/lib/hooks";
|
||||
import { getFinalLLM } from "@/lib/llm/utils";
|
||||
import React from "react";
|
||||
import { FiBookmark, FiImage, FiSearch } from "react-icons/fi";
|
||||
import React, { useState } from "react";
|
||||
import { FiBookmark, FiPlus } from "react-icons/fi";
|
||||
|
||||
interface AssistantsTabProps {
|
||||
selectedAssistant: Persona;
|
||||
@@ -24,80 +26,82 @@ export function AssistantsTab({
|
||||
return (
|
||||
<div className="py-4">
|
||||
<h3 className="px-4 text-lg font-semibold">Change Assistant</h3>
|
||||
<div className="px-2 mx-2 max-h-[500px] overflow-y-scroll my-3 grid grid-cols-2 gap-4">
|
||||
<div className="px-2 pb-2 mx-2 max-h-[500px] overflow-y-scroll my-3 grid grid-cols-1 gap-4">
|
||||
{availableAssistants.map((assistant) => (
|
||||
<div
|
||||
<AssistantCard
|
||||
key={assistant.id}
|
||||
className={`
|
||||
cursor-pointer
|
||||
p-4
|
||||
border
|
||||
rounded-lg
|
||||
shadow-md
|
||||
hover:bg-hover-light
|
||||
${
|
||||
selectedAssistant.id === assistant.id
|
||||
? "border-accent"
|
||||
: "border-border"
|
||||
}
|
||||
`}
|
||||
onClick={() => onSelect(assistant)}
|
||||
>
|
||||
<div className="flex items-center mb-2">
|
||||
<AssistantIcon assistant={assistant} />
|
||||
<div className="ml-2 line-clamp-2 ellipsis font-bold text-sm text-emphasis">
|
||||
{assistant.name}
|
||||
</div>
|
||||
</div>
|
||||
{assistant.tools.length > 0 && (
|
||||
<div className="text-xs text-subtle flex flex-wrap gap-2">
|
||||
{assistant.tools.map((tool) => {
|
||||
let toolName = tool.name;
|
||||
let toolIcon = null;
|
||||
|
||||
if (tool.name === "SearchTool") {
|
||||
toolName = "Search";
|
||||
toolIcon = <FiSearch className="mr-1 my-auto" />;
|
||||
} else if (tool.name === "ImageGenerationTool") {
|
||||
toolName = "Image Generation";
|
||||
toolIcon = <FiImage className="mr-1 my-auto" />;
|
||||
}
|
||||
return (
|
||||
<Bubble key={tool.id} isSelected={false}>
|
||||
<div className="flex line-wrap break-all flex-row gap-1">
|
||||
<div className="flex-none my-auto">{toolIcon}</div>
|
||||
{toolName}
|
||||
</div>
|
||||
</Bubble>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs text-subtle mb-2 mt-2">
|
||||
{assistant.description}
|
||||
</div>
|
||||
<div className="mt-2 flex flex-col gap-y-2">
|
||||
{assistant.document_sets.length > 0 && (
|
||||
<div className="text-xs text-subtle flex flex-wrap gap-2">
|
||||
<p className="my-auto font-medium">Document Sets:</p>
|
||||
{assistant.document_sets.map((set) => (
|
||||
<Bubble key={set.id} isSelected={false}>
|
||||
<div className="flex flex-row gap-1">
|
||||
<FiBookmark className="mr-1 my-auto" />
|
||||
{set.name}
|
||||
</div>
|
||||
</Bubble>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs text-subtle">
|
||||
<span className="font-medium">Default Model:</span>{" "}
|
||||
<i>{assistant.llm_model_version_override || llmName}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
assistant={assistant}
|
||||
isSelected={selectedAssistant.id === assistant.id}
|
||||
onSelect={onSelect}
|
||||
llmName={llmName}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const AssistantCard = ({
|
||||
assistant,
|
||||
isSelected,
|
||||
onSelect,
|
||||
llmName,
|
||||
}: {
|
||||
assistant: Persona;
|
||||
isSelected: boolean;
|
||||
onSelect: (assistant: Persona) => void;
|
||||
llmName: string;
|
||||
}) => {
|
||||
const [hovering, setHovering] = useState(false);
|
||||
return (
|
||||
<div
|
||||
onClick={() => onSelect(assistant)}
|
||||
key={assistant.id}
|
||||
className={`
|
||||
p-4
|
||||
cursor-pointer
|
||||
border
|
||||
hover:bg-hover
|
||||
shadow-md
|
||||
rounded
|
||||
rounded-lg
|
||||
border-border
|
||||
`}
|
||||
onMouseEnter={() => setHovering(true)}
|
||||
onMouseLeave={() => setHovering(false)}
|
||||
>
|
||||
<div className="flex items-center mb-2">
|
||||
<AssistantIcon assistant={assistant} />
|
||||
<div className="ml-2 line-clamp-1 ellipsis font-bold text-sm text-emphasis">
|
||||
{assistant.name}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-subtle mb-2 mt-2 line-clamp-3 py-1">
|
||||
{assistant.description}
|
||||
</div>
|
||||
<div className="mt-2 flex flex-col gap-y-1">
|
||||
{assistant.document_sets.length > 0 && (
|
||||
<div className="text-xs text-subtle flex flex-wrap gap-2">
|
||||
<p className="my-auto font-medium">Document Sets:</p>
|
||||
{assistant.document_sets.map((set) => (
|
||||
<Bubble key={set.id} isSelected={false}>
|
||||
<div className="flex flex-row gap-1">
|
||||
<FiBookmark className="mr-1 my-auto" />
|
||||
{set.name}
|
||||
</div>
|
||||
</Bubble>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xs text-subtle">
|
||||
<span className="font-semibold">Default model:</span>{" "}
|
||||
{getDisplayNameForModel(
|
||||
assistant.llm_model_version_override || llmName
|
||||
)}
|
||||
</div>
|
||||
<AssistantTools hovered={hovering} assistant={assistant} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { useChatContext } from "@/components/context/ChatContext";
|
||||
import { LlmOverride, LlmOverrideManager } from "@/lib/hooks";
|
||||
import { getDisplayNameForModel, LlmOverrideManager } from "@/lib/hooks";
|
||||
import React, { forwardRef, useCallback, useRef, useState } from "react";
|
||||
import { debounce } from "lodash";
|
||||
import { DefaultDropdown } from "@/components/Dropdown";
|
||||
@@ -7,20 +7,26 @@ import { Text } from "@tremor/react";
|
||||
import { Persona } from "@/app/admin/assistants/interfaces";
|
||||
import { destructureValue, getFinalLLM, structureValue } from "@/lib/llm/utils";
|
||||
import { updateModelOverrideForChatSession } from "../../lib";
|
||||
import { Tooltip } from "@/components/tooltip/Tooltip";
|
||||
import { InfoIcon } from "@/components/icons/icons";
|
||||
import { CustomTooltip } from "@/components/tooltip/CustomTooltip";
|
||||
|
||||
interface LlmTabProps {
|
||||
llmOverrideManager: LlmOverrideManager;
|
||||
currentAssistant: Persona;
|
||||
currentLlm: string;
|
||||
chatSessionId?: number;
|
||||
close?: () => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export const LlmTab = forwardRef<HTMLDivElement, LlmTabProps>(
|
||||
({ llmOverrideManager, currentAssistant, chatSessionId, close }, ref) => {
|
||||
(
|
||||
{ llmOverrideManager, currentAssistant, chatSessionId, currentLlm, close },
|
||||
ref
|
||||
) => {
|
||||
const { llmProviders } = useChatContext();
|
||||
const { llmOverride, setLlmOverride, temperature, setTemperature } =
|
||||
llmOverrideManager;
|
||||
|
||||
const { setLlmOverride, temperature, setTemperature } = llmOverrideManager;
|
||||
const [isTemperatureExpanded, setIsTemperatureExpanded] = useState(false);
|
||||
const [localTemperature, setLocalTemperature] = useState<number>(
|
||||
temperature || 0
|
||||
);
|
||||
@@ -44,94 +50,97 @@ export const LlmTab = forwardRef<HTMLDivElement, LlmTabProps>(
|
||||
);
|
||||
|
||||
const llmOptions: { name: string; value: string }[] = [];
|
||||
|
||||
llmProviders.forEach((llmProvider) => {
|
||||
llmProvider.model_names.forEach((modelName) => {
|
||||
llmOptions.push({
|
||||
name: modelName,
|
||||
value: structureValue(
|
||||
llmProvider.name,
|
||||
llmProvider.provider,
|
||||
modelName
|
||||
),
|
||||
});
|
||||
});
|
||||
(llmProvider.display_model_names || llmProvider.model_names).forEach(
|
||||
(modelName) => {
|
||||
llmOptions.push({
|
||||
name: modelName,
|
||||
value: structureValue(
|
||||
llmProvider.name,
|
||||
llmProvider.provider,
|
||||
modelName
|
||||
),
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm font-medium mb-2">Choose Model</label>
|
||||
<Text className="mb-1">
|
||||
Override the default model for the{" "}
|
||||
<i className="font-medium">{currentAssistant.name}</i> assistant. The
|
||||
override will only apply for the current chat session.
|
||||
</Text>
|
||||
<Text className="mb-3">
|
||||
Default Model: <i className="font-medium">{defaultLlmName}</i>.
|
||||
</Text>
|
||||
|
||||
<div ref={ref} className="w-96">
|
||||
<DefaultDropdown
|
||||
ref={ref}
|
||||
options={llmOptions}
|
||||
selected={structureValue(
|
||||
llmOverride.name,
|
||||
llmOverride.provider,
|
||||
llmOverride.modelName
|
||||
)}
|
||||
onSelect={(value) => {
|
||||
setLlmOverride(destructureValue(value as string));
|
||||
if (chatSessionId) {
|
||||
updateModelOverrideForChatSession(
|
||||
chatSessionId,
|
||||
value as string
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="w-full">
|
||||
<div className="flex w-full content-center gap-x-2">
|
||||
<label className="block text-sm font-medium mb-2">Choose Model</label>
|
||||
</div>
|
||||
|
||||
<label className="block text-sm font-medium mb-2 mt-4">
|
||||
Temperature
|
||||
</label>
|
||||
|
||||
<Text className="mb-8">
|
||||
Adjust the temperature of the LLM. Higher temperatures will make the
|
||||
LLM generate more creative and diverse responses, while lower
|
||||
temperature will make the LLM generate more conservative and focused
|
||||
responses.
|
||||
</Text>
|
||||
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
type="range"
|
||||
onChange={(e) =>
|
||||
handleTemperatureChange(parseFloat(e.target.value))
|
||||
}
|
||||
className="
|
||||
w-full
|
||||
p-2
|
||||
border
|
||||
border-border
|
||||
rounded-md
|
||||
"
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.01"
|
||||
value={localTemperature}
|
||||
/>
|
||||
<div
|
||||
className="absolute text-sm"
|
||||
style={{
|
||||
left: `${(localTemperature || 0) * 50}%`,
|
||||
transform: `translateX(-${Math.min(
|
||||
Math.max((localTemperature || 0) * 50, 10),
|
||||
90
|
||||
)}%)`,
|
||||
top: "-1.5rem",
|
||||
}}
|
||||
<div className="max-h-[300px] flex flex-col gap-y-1 overflow-y-scroll">
|
||||
{llmOptions.map(({ name, value }, index) => {
|
||||
return (
|
||||
<button
|
||||
key={index}
|
||||
className={`w-full py-1.5 px-2 text-sm ${currentLlm == name ? "bg-background-200" : "bg-background-100/50 hover:bg-background-100"} text-left rounded`}
|
||||
onClick={() => {
|
||||
setLlmOverride(destructureValue(value));
|
||||
if (chatSessionId) {
|
||||
updateModelOverrideForChatSession(
|
||||
chatSessionId,
|
||||
value as string
|
||||
);
|
||||
}
|
||||
close();
|
||||
}}
|
||||
>
|
||||
{getDisplayNameForModel(name)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<button
|
||||
className="flex items-center text-sm font-medium transition-colors duration-200"
|
||||
onClick={() => setIsTemperatureExpanded(!isTemperatureExpanded)}
|
||||
>
|
||||
{localTemperature}
|
||||
</div>
|
||||
<span className="mr-2 text-xs text-primary">
|
||||
{isTemperatureExpanded ? "▼" : "►"}
|
||||
</span>
|
||||
<span>Temperature</span>
|
||||
</button>
|
||||
|
||||
{isTemperatureExpanded && (
|
||||
<>
|
||||
<Text className="mt-2 mb-8">
|
||||
Adjust the temperature of the LLM. Higher temperatures will make
|
||||
the LLM generate more creative and diverse responses, while
|
||||
lower temperature will make the LLM generate more conservative
|
||||
and focused responses.
|
||||
</Text>
|
||||
|
||||
<div className="relative w-full">
|
||||
<input
|
||||
type="range"
|
||||
onChange={(e) =>
|
||||
handleTemperatureChange(parseFloat(e.target.value))
|
||||
}
|
||||
className="w-full p-2 border border-border rounded-md"
|
||||
min="0"
|
||||
max="2"
|
||||
step="0.01"
|
||||
value={localTemperature}
|
||||
/>
|
||||
<div
|
||||
className="absolute text-sm"
|
||||
style={{
|
||||
left: `${(localTemperature || 0) * 50}%`,
|
||||
transform: `translateX(-${Math.min(
|
||||
Math.max((localTemperature || 0) * 50, 10),
|
||||
90
|
||||
)}%)`,
|
||||
top: "-1.5rem",
|
||||
}}
|
||||
>
|
||||
{localTemperature}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -17,7 +17,7 @@ export function Bubble({
|
||||
<div
|
||||
className={
|
||||
`
|
||||
px-3
|
||||
px-1.5
|
||||
py-1
|
||||
rounded-lg
|
||||
border
|
||||
|
@@ -191,6 +191,77 @@ export function TextFormField({
|
||||
);
|
||||
}
|
||||
|
||||
export function MultiSelectField({
|
||||
name,
|
||||
label,
|
||||
subtext,
|
||||
options,
|
||||
onChange,
|
||||
error,
|
||||
hideError,
|
||||
small,
|
||||
selectedInitially,
|
||||
}: {
|
||||
selectedInitially: string[];
|
||||
name: string;
|
||||
label: string;
|
||||
subtext?: string | JSX.Element;
|
||||
options: { value: string; label: string }[];
|
||||
onChange?: (selected: string[]) => void;
|
||||
error?: string;
|
||||
hideError?: boolean;
|
||||
small?: boolean;
|
||||
}) {
|
||||
const [selectedOptions, setSelectedOptions] =
|
||||
useState<string[]>(selectedInitially);
|
||||
|
||||
const handleCheckboxChange = (value: string) => {
|
||||
const newSelectedOptions = selectedOptions.includes(value)
|
||||
? selectedOptions.filter((option) => option !== value)
|
||||
: [...selectedOptions, value];
|
||||
|
||||
setSelectedOptions(newSelectedOptions);
|
||||
if (onChange) {
|
||||
onChange(newSelectedOptions);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<Label small={small}>{label}</Label>
|
||||
{error ? (
|
||||
<ManualErrorMessage>{error}</ManualErrorMessage>
|
||||
) : (
|
||||
!hideError && (
|
||||
<ErrorMessage
|
||||
name={name}
|
||||
component="div"
|
||||
className="text-error my-auto text-sm"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
{subtext && <SubLabel>{subtext}</SubLabel>}
|
||||
<div className="mt-2">
|
||||
{options.map((option) => (
|
||||
<label key={option.value} className="flex items-center mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
name={name}
|
||||
value={option.value}
|
||||
checked={selectedOptions.includes(option.value)}
|
||||
onChange={() => handleCheckboxChange(option.value)}
|
||||
className="mr-2"
|
||||
/>
|
||||
{option.label}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
interface MarkdownPreviewProps {
|
||||
name: string;
|
||||
label: string;
|
||||
|
@@ -8,11 +8,13 @@ interface PopupProps {
|
||||
) => ReactNode;
|
||||
position?: "top" | "bottom" | "left" | "right";
|
||||
removePadding?: boolean;
|
||||
tab?: boolean;
|
||||
}
|
||||
|
||||
const Popup: React.FC<PopupProps> = ({
|
||||
children,
|
||||
content,
|
||||
tab,
|
||||
removePadding,
|
||||
position = "top",
|
||||
}) => {
|
||||
@@ -76,7 +78,7 @@ const Popup: React.FC<PopupProps> = ({
|
||||
break;
|
||||
default: // top
|
||||
popupStyle.bottom = `${triggerRect.height + 5}px`;
|
||||
popupStyle.left = `${triggerRect.width + 100}px`;
|
||||
popupStyle.left = `${triggerRect.width + 50}px`;
|
||||
popupStyle.transform = "translateX(-50%)";
|
||||
}
|
||||
|
||||
@@ -94,7 +96,7 @@ const Popup: React.FC<PopupProps> = ({
|
||||
ref={popupRef}
|
||||
className={`absolute bg-white border border-gray-200 rounded-lg shadow-lg ${
|
||||
!removePadding && "p-4"
|
||||
} min-w-[400px]`}
|
||||
} ${tab ? " w-[400px] " : "min-w-[400px]"}`}
|
||||
style={getPopupStyle()}
|
||||
>
|
||||
{content(closePopup, contentRef)}
|
||||
|
@@ -42,6 +42,7 @@ export const CustomTooltip = ({
|
||||
line,
|
||||
showTick = false,
|
||||
delay = 500,
|
||||
position = "bottom",
|
||||
}: {
|
||||
content: string | ReactNode;
|
||||
children: JSX.Element;
|
||||
@@ -51,6 +52,7 @@ export const CustomTooltip = ({
|
||||
showTick?: boolean;
|
||||
delay?: number;
|
||||
citation?: boolean;
|
||||
position?: "top" | "bottom";
|
||||
}) => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
@@ -99,8 +101,8 @@ export const CustomTooltip = ({
|
||||
</span>
|
||||
{isVisible && (
|
||||
<div
|
||||
className={`absolute z-10 ${citation ? "max-w-[350px]" : "w-40"} ${large ? "w-96" : line && "max-w-64 w-auto"}
|
||||
left-1/2 transform -translate-x-1/2 mt-2 text-sm
|
||||
className={`absolute z-[1000] ${citation ? "max-w-[350px]" : "w-40"} ${large ? "w-96" : line && "max-w-64 w-auto"}
|
||||
left-1/2 transform -translate-x-1/2 ${position === "top" ? "bottom-full mb-2" : "mt-2"} text-sm
|
||||
${
|
||||
light
|
||||
? "text-gray-800 bg-background-200"
|
||||
@@ -110,7 +112,7 @@ export const CustomTooltip = ({
|
||||
>
|
||||
{showTick && (
|
||||
<div
|
||||
className={`absolute w-3 h-3 -top-1.5 left-1/2 transform -translate-x-1/2 rotate-45
|
||||
className={`absolute w-3 h-3 -top-1.5 ${position === "top" ? "bottom-1.5" : "-top-1.5"} left-1/2 transform -translate-x-1/2 rotate-45
|
||||
${light ? "bg-background-200" : "bg-background-800"}`}
|
||||
/>
|
||||
)}
|
||||
|
@@ -90,6 +90,7 @@ export async function fetchChatData(searchParams: {
|
||||
|
||||
const tagsResponse = results[6] as Response | null;
|
||||
const llmProviders = (results[7] || []) as LLMProviderDescriptor[];
|
||||
|
||||
const foldersResponse = results[8] as Response | null; // Handle folders result
|
||||
|
||||
const authDisabled = authTypeMetadata?.authType === "disabled";
|
||||
|
@@ -210,3 +210,77 @@ export const useUserGroups = (): {
|
||||
refreshUserGroups: () => mutate(USER_GROUP_URL),
|
||||
};
|
||||
};
|
||||
|
||||
const MODEL_DISPLAY_NAMES: { [key: string]: string } = {
|
||||
// OpenAI models
|
||||
"gpt-4": "GPT 4",
|
||||
"gpt-4o": "GPT 4o",
|
||||
"gpt-4o-mini": "GPT 4o Mini",
|
||||
"gpt-4-0314": "GPT 4 (March 2023)",
|
||||
"gpt-4-0613": "GPT 4 (June 2023)",
|
||||
"gpt-4-32k-0314": "GPT 4 32k (March 2023)",
|
||||
"gpt-4-turbo": "GPT 4 Turbo",
|
||||
"gpt-4-turbo-preview": "GPT 4 Turbo (Preview)",
|
||||
"gpt-4-1106-preview": "GPT 4 Turbo (November 2023)",
|
||||
"gpt-4-vision-preview": "GPT 4 Vision (Preview)",
|
||||
"gpt-3.5-turbo": "GPT 3.5 Turbo",
|
||||
"gpt-3.5-turbo-0125": "GPT 3.5 Turbo (January 2024)",
|
||||
"gpt-3.5-turbo-1106": "GPT 3.5 Turbo (November 2023)",
|
||||
"gpt-3.5-turbo-16k": "GPT 3.5 Turbo 16k",
|
||||
"gpt-3.5-turbo-0613": "GPT 3.5 Turbo (June 2023)",
|
||||
"gpt-3.5-turbo-16k-0613": "GPT 3.5 Turbo 16k (June 2023)",
|
||||
"gpt-3.5-turbo-0301": "GPT 3.5 Turbo (March 2023)",
|
||||
|
||||
// Anthropic models
|
||||
"claude-3-opus-20240229": "Claude 3 Opus",
|
||||
"claude-3-sonnet-20240229": "Claude 3 Sonnet",
|
||||
"claude-3-haiku-20240307": "Claude 3 Haiku",
|
||||
"claude-2.1": "Claude 2.1",
|
||||
"claude-2.0": "Claude 2.0",
|
||||
"claude-instant-1.2": "Claude Instant 1.2",
|
||||
"claude-3-5-sonnet-20240620": "Claude 3.5 Sonnet",
|
||||
|
||||
// Bedrock models
|
||||
"meta.llama3-1-70b-instruct-v1:0": "Llama 3.1 70B",
|
||||
"meta.llama3-1-8b-instruct-v1:0": "Llama 3.1 8B",
|
||||
"meta.llama3-70b-instruct-v1:0": "Llama 3 70B",
|
||||
"meta.llama3-8b-instruct-v1:0": "Llama 3 8B",
|
||||
"meta.llama2-70b-chat-v1": "Llama 2 70B",
|
||||
"meta.llama2-13b-chat-v1": "Llama 2 13B",
|
||||
"cohere.command-r-v1:0": "Command R",
|
||||
"cohere.command-r-plus-v1:0": "Command R Plus",
|
||||
"cohere.command-light-text-v14": "Command Light Text",
|
||||
"cohere.command-text-v14": "Command Text",
|
||||
"anthropic.claude-instant-v1": "Claude Instant",
|
||||
"anthropic.claude-v2:1": "Claude v2.1",
|
||||
"anthropic.claude-v2": "Claude v2",
|
||||
"anthropic.claude-v1": "Claude v1",
|
||||
"anthropic.claude-3-opus-20240229-v1:0": "Claude 3 Opus",
|
||||
"anthropic.claude-3-haiku-20240307-v1:0": "Claude 3 Haiku",
|
||||
"anthropic.claude-3-5-sonnet-20240620-v1:0": "Claude 3.5 Sonnet",
|
||||
"anthropic.claude-3-sonnet-20240229-v1:0": "Claude 3 Sonnet",
|
||||
"mistral.mistral-large-2402-v1:0": "Mistral Large",
|
||||
"mistral.mixtral-8x7b-instruct-v0:1": "Mixtral 8x7B Instruct",
|
||||
"mistral.mistral-7b-instruct-v0:2": "Mistral 7B Instruct",
|
||||
"amazon.titan-text-express-v1": "Titan Text Express",
|
||||
"amazon.titan-text-lite-v1": "Titan Text Lite",
|
||||
"ai21.jamba-instruct-v1:0": "Jamba Instruct",
|
||||
"ai21.j2-ultra-v1": "J2 Ultra",
|
||||
"ai21.j2-mid-v1": "J2 Mid",
|
||||
};
|
||||
|
||||
export function getDisplayNameForModel(modelName: string): string {
|
||||
return MODEL_DISPLAY_NAMES[modelName] || modelName;
|
||||
}
|
||||
|
||||
export const defaultModelsByProvider: { [name: string]: string[] } = {
|
||||
openai: ["gpt-4", "gpt-4o", "gpt-4o-mini"],
|
||||
bedrock: [
|
||||
"meta.llama3-1-70b-instruct-v1:0",
|
||||
"meta.llama3-1-8b-instruct-v1:0",
|
||||
"anthropic.claude-3-opus-20240229-v1:0",
|
||||
"mistral.mistral-large-2402-v1:0",
|
||||
"anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||
],
|
||||
anthropic: ["claude-3-opus-20240229", "claude-3-5-sonnet-20240620"],
|
||||
};
|
||||
|
Reference in New Issue
Block a user