mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-06-29 17:20:44 +02:00
Update Assistants Creation UI (#1714)
* slide up "Tools" * rework assistants page * update layout * reorg complete - pending: useful header text? * add tooltips * alter organizational structure * rm shadcn * rm dependencies * revalidate dependencies * restore * update component structure * [s] format * rm package json * add package-lock.json [s] * collapsible * naming + width * formatting * formatting * updated user flow - Fix error/detail messages - Fix tooltip delay - Fix icons * 1 -> 2 * naming fixes * ran pretty * fix build issue? * web build issues?
This commit is contained in:
BIN
web/public/Amazon.webp
Normal file
BIN
web/public/Amazon.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
8
web/public/Anthropic.svg
Normal file
8
web/public/Anthropic.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="256px" height="176px" viewBox="0 0 256 176" version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
|
||||||
|
<title>Anthropic</title>
|
||||||
|
<g fill="#181818">
|
||||||
|
<path d="M147.486878,0 C147.486878,0 217.568251,175.780074 217.568251,175.780074 C217.568251,175.780074 256,175.780074 256,175.780074 C256,175.780074 185.918621,0 185.918621,0 C185.918621,0 147.486878,0 147.486878,0 C147.486878,0 147.486878,0 147.486878,0 Z"></path>
|
||||||
|
<path d="M66.1828124,106.221191 C66.1828124,106.221191 90.1624677,44.4471185 90.1624677,44.4471185 C90.1624677,44.4471185 114.142128,106.221191 114.142128,106.221191 C114.142128,106.221191 66.1828124,106.221191 66.1828124,106.221191 C66.1828124,106.221191 66.1828124,106.221191 66.1828124,106.221191 Z M70.0705318,0 C70.0705318,0 0,175.780074 0,175.780074 C0,175.780074 39.179211,175.780074 39.179211,175.780074 C39.179211,175.780074 53.5097704,138.86606 53.5097704,138.86606 C53.5097704,138.86606 126.817544,138.86606 126.817544,138.86606 C126.817544,138.86606 141.145724,175.780074 141.145724,175.780074 C141.145724,175.780074 180.324935,175.780074 180.324935,175.780074 C180.324935,175.780074 110.254409,0 110.254409,0 C110.254409,0 70.0705318,0 70.0705318,0 C70.0705318,0 70.0705318,0 70.0705318,0 Z"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
BIN
web/public/Azure.png
Normal file
BIN
web/public/Azure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 288 KiB |
BIN
web/public/OpenSource.png
Normal file
BIN
web/public/OpenSource.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.4 KiB |
1
web/public/Openai.svg
Normal file
1
web/public/Openai.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg"><path d="m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -20,11 +20,12 @@ import Link from "next/link";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import {
|
||||||
BooleanFormField,
|
BooleanFormField,
|
||||||
|
Label,
|
||||||
SelectorFormField,
|
SelectorFormField,
|
||||||
TextFormField,
|
TextFormField,
|
||||||
} from "@/components/admin/connectors/Field";
|
} from "@/components/admin/connectors/Field";
|
||||||
import { HidableSection } from "./HidableSection";
|
import CollapsibleSection from "./CollapsibleSection";
|
||||||
import { FiPlus, FiX } from "react-icons/fi";
|
import { FiInfo, FiPlus, FiX } from "react-icons/fi";
|
||||||
import { useUserGroups } from "@/lib/hooks";
|
import { useUserGroups } from "@/lib/hooks";
|
||||||
import { Bubble } from "@/components/Bubble";
|
import { Bubble } from "@/components/Bubble";
|
||||||
import { GroupsIcon } from "@/components/icons/icons";
|
import { GroupsIcon } from "@/components/icons/icons";
|
||||||
@ -36,8 +37,13 @@ import { ToolSnapshot } from "@/lib/tools/interfaces";
|
|||||||
import { checkUserIsNoAuthUser } from "@/lib/user";
|
import { checkUserIsNoAuthUser } from "@/lib/user";
|
||||||
import { addAssistantToList } from "@/lib/assistants/updateAssistantPreferences";
|
import { addAssistantToList } from "@/lib/assistants/updateAssistantPreferences";
|
||||||
import { checkLLMSupportsImageInput } from "@/lib/llm/utils";
|
import { checkLLMSupportsImageInput } from "@/lib/llm/utils";
|
||||||
import { SettingsContext } from "@/components/settings/SettingsProvider";
|
|
||||||
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
import { usePaidEnterpriseFeaturesEnabled } from "@/components/settings/usePaidEnterpriseFeaturesEnabled";
|
||||||
|
import {
|
||||||
|
TooltipProvider,
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@radix-ui/react-tooltip";
|
||||||
|
|
||||||
function findSearchTool(tools: ToolSnapshot[]) {
|
function findSearchTool(tools: ToolSnapshot[]) {
|
||||||
return tools.find((tool) => tool.in_code_tool_id === "SearchTool");
|
return tools.find((tool) => tool.in_code_tool_id === "SearchTool");
|
||||||
@ -47,12 +53,6 @@ function findImageGenerationTool(tools: ToolSnapshot[]) {
|
|||||||
return tools.find((tool) => tool.in_code_tool_id === "ImageGenerationTool");
|
return tools.find((tool) => tool.in_code_tool_id === "ImageGenerationTool");
|
||||||
}
|
}
|
||||||
|
|
||||||
function Label({ children }: { children: string | JSX.Element }) {
|
|
||||||
return (
|
|
||||||
<div className="block font-medium text-base text-emphasis">{children}</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SubLabel({ children }: { children: string | JSX.Element }) {
|
function SubLabel({ children }: { children: string | JSX.Element }) {
|
||||||
return <div className="text-sm text-subtle mb-2">{children}</div>;
|
return <div className="text-sm text-subtle mb-2">{children}</div>;
|
||||||
}
|
}
|
||||||
@ -202,9 +202,11 @@ export function AssistantEditor({
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
validationSchema={Yup.object()
|
validationSchema={Yup.object()
|
||||||
.shape({
|
.shape({
|
||||||
name: Yup.string().required("Must give the Assistant a name!"),
|
name: Yup.string().required(
|
||||||
|
"Must provide a name for the Assistant"
|
||||||
|
),
|
||||||
description: Yup.string().required(
|
description: Yup.string().required(
|
||||||
"Must give the Assistant a description!"
|
"Must provide a description for the Assistant"
|
||||||
),
|
),
|
||||||
system_prompt: Yup.string(),
|
system_prompt: Yup.string(),
|
||||||
task_prompt: Yup.string(),
|
task_prompt: Yup.string(),
|
||||||
@ -227,29 +229,29 @@ export function AssistantEditor({
|
|||||||
})
|
})
|
||||||
.test(
|
.test(
|
||||||
"system-prompt-or-task-prompt",
|
"system-prompt-or-task-prompt",
|
||||||
"Must provide at least one of System Prompt or Task Prompt",
|
"Must provide either System Prompt or Additional Instructions",
|
||||||
(values) => {
|
function (values) {
|
||||||
const systemPromptSpecified = values.system_prompt
|
const systemPromptSpecified =
|
||||||
? values.system_prompt.length > 0
|
values.system_prompt && values.system_prompt.trim().length > 0;
|
||||||
: false;
|
const taskPromptSpecified =
|
||||||
const taskPromptSpecified = values.task_prompt
|
values.task_prompt && values.task_prompt.trim().length > 0;
|
||||||
? values.task_prompt.length > 0
|
|
||||||
: false;
|
|
||||||
if (systemPromptSpecified || taskPromptSpecified) {
|
|
||||||
setFinalPromptError("");
|
|
||||||
return true;
|
|
||||||
} // Return true if at least one field has a value
|
|
||||||
|
|
||||||
setFinalPromptError(
|
if (systemPromptSpecified || taskPromptSpecified) {
|
||||||
"Must provide at least one of System Prompt or Task Prompt"
|
return true;
|
||||||
);
|
}
|
||||||
|
|
||||||
|
return this.createError({
|
||||||
|
path: "system_prompt",
|
||||||
|
message:
|
||||||
|
"Must provide either System Prompt or Additional Instructions",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
onSubmit={async (values, formikHelpers) => {
|
onSubmit={async (values, formikHelpers) => {
|
||||||
if (finalPromptError) {
|
if (finalPromptError) {
|
||||||
setPopup({
|
setPopup({
|
||||||
type: "error",
|
type: "error",
|
||||||
message: "Cannot submit while there are errors in the form!",
|
message: "Cannot submit while there are errors in the form",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -392,28 +394,26 @@ export function AssistantEditor({
|
|||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<div className="pb-6">
|
<div className="pb-6">
|
||||||
<HidableSection sectionTitle="Basics">
|
|
||||||
<>
|
|
||||||
<TextFormField
|
<TextFormField
|
||||||
name="name"
|
name="name"
|
||||||
|
tooltip="Used to identify the Assistant in the UI."
|
||||||
label="Name"
|
label="Name"
|
||||||
disabled={isUpdate}
|
disabled={isUpdate}
|
||||||
subtext="Users will be able to select this Assistant based on this name."
|
placeholder="e.g. 'Email Assistant'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextFormField
|
<TextFormField
|
||||||
|
tooltip="Used for identifying assistants and their use cases."
|
||||||
name="description"
|
name="description"
|
||||||
label="Description"
|
label="Description"
|
||||||
subtext="Provide a short descriptions which gives users a hint as to what they should use this Assistant for."
|
placeholder="e.g. 'Use this Assistant to help draft professional emails'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextFormField
|
<TextFormField
|
||||||
|
tooltip="Gives your assistant a prime directive"
|
||||||
name="system_prompt"
|
name="system_prompt"
|
||||||
label="System Prompt"
|
label="System Prompt"
|
||||||
isTextArea={true}
|
isTextArea={true}
|
||||||
subtext={
|
placeholder="e.g. 'You are a professional email writing assistant that always uses a polite enthusiastic tone, emphasizes action items, and leaves blanks for the human to fill in when you have unknowns'"
|
||||||
'Give general info about what the Assistant is about. For example, "You are an assistant for On-Call engineers. Your goal is to read the provided context documents and give recommendations as to how to resolve the issue."'
|
//
|
||||||
}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setFieldValue("system_prompt", e.target.value);
|
setFieldValue("system_prompt", e.target.value);
|
||||||
triggerFinalPromptUpdate(
|
triggerFinalPromptUpdate(
|
||||||
@ -425,46 +425,91 @@ export function AssistantEditor({
|
|||||||
error={finalPromptError}
|
error={finalPromptError}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextFormField
|
<div className="mb-6">
|
||||||
name="task_prompt"
|
<div className="flex gap-x-2 items-center">
|
||||||
label="Task Prompt (Optional)"
|
<div className="block font-medium text-base">
|
||||||
isTextArea={true}
|
LLM Provider{" "}
|
||||||
subtext={`Give specific instructions as to what to do with the user query.
|
</div>
|
||||||
For example, "Find any relevant sections from the provided documents that can
|
<TooltipProvider delayDuration={50}>
|
||||||
help the user resolve their issue and explain how they are relevant."`}
|
<Tooltip>
|
||||||
onChange={(e) => {
|
<TooltipTrigger>
|
||||||
setFieldValue("task_prompt", e.target.value);
|
<FiInfo size={12} />
|
||||||
triggerFinalPromptUpdate(
|
</TooltipTrigger>
|
||||||
values.system_prompt,
|
<TooltipContent side="top" align="center">
|
||||||
e.target.value,
|
<p className="bg-neutral-900 max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-white">
|
||||||
searchToolEnabled()
|
Select a Large Language Model (Generative AI model)
|
||||||
|
to power this Assistant
|
||||||
|
</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</div>
|
||||||
|
<div className="mb-2 flex items-starts">
|
||||||
|
<div className="w-96">
|
||||||
|
<SelectorFormField
|
||||||
|
defaultValue={`Default (${defaultModelName})`}
|
||||||
|
name="llm_model_provider_override"
|
||||||
|
options={llmProviders.map((llmProvider) => ({
|
||||||
|
name: llmProvider.name,
|
||||||
|
value: llmProvider.name,
|
||||||
|
icon: llmProvider.icon,
|
||||||
|
}))}
|
||||||
|
includeDefault={true}
|
||||||
|
onSelect={(selected) => {
|
||||||
|
if (selected !== values.llm_model_provider_override) {
|
||||||
|
setFieldValue("llm_model_version_override", null);
|
||||||
|
}
|
||||||
|
setFieldValue(
|
||||||
|
"llm_model_provider_override",
|
||||||
|
selected
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
error={finalPromptError}
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Label>Final Prompt</Label>
|
{values.llm_model_provider_override && (
|
||||||
|
<div className="w-96 ml-4">
|
||||||
{finalPrompt ? (
|
<SelectorFormField
|
||||||
<pre className="text-sm mt-2 whitespace-pre-wrap">
|
name="llm_model_version_override"
|
||||||
{finalPrompt}
|
options={
|
||||||
</pre>
|
modelOptionsByProvider.get(
|
||||||
) : (
|
values.llm_model_provider_override
|
||||||
"-"
|
) || []
|
||||||
|
}
|
||||||
|
maxHeight="max-h-72"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
</HidableSection>
|
|
||||||
|
|
||||||
<Divider />
|
<div className="ml-1">
|
||||||
|
{imageGenerationTool &&
|
||||||
|
checkLLMSupportsImageInput(
|
||||||
|
providerDisplayNameToProviderName.get(
|
||||||
|
values.llm_model_provider_override || ""
|
||||||
|
) ||
|
||||||
|
defaultProviderName ||
|
||||||
|
"",
|
||||||
|
values.llm_model_version_override ||
|
||||||
|
defaultModelName ||
|
||||||
|
""
|
||||||
|
) && (
|
||||||
|
<BooleanFormField
|
||||||
|
noPadding
|
||||||
|
name={`enabled_tools_map.${imageGenerationTool.id}`}
|
||||||
|
label="Image Generation Tool"
|
||||||
|
onChange={() => {
|
||||||
|
toggleToolInValues(imageGenerationTool.id);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<HidableSection sectionTitle="Tools">
|
|
||||||
<>
|
|
||||||
{ccPairs.length > 0 && searchTool && (
|
{ccPairs.length > 0 && searchTool && (
|
||||||
<>
|
<>
|
||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
name={`enabled_tools_map.${searchTool.id}`}
|
name={`enabled_tools_map.${searchTool.id}`}
|
||||||
label="Search Tool"
|
label="Search Tool"
|
||||||
subtext={`The Search Tool allows the Assistant to search through connected knowledge to help build an answer.`}
|
noPadding
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setFieldValue("num_chunks", null);
|
setFieldValue("num_chunks", null);
|
||||||
toggleToolInValues(searchTool.id);
|
toggleToolInValues(searchTool.id);
|
||||||
@ -472,10 +517,11 @@ export function AssistantEditor({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{searchToolEnabled() && (
|
{searchToolEnabled() && (
|
||||||
<div className="pl-4 border-l-2 ml-4 border-border">
|
<CollapsibleSection prompt="Configure Search">
|
||||||
|
<div className=" ">
|
||||||
{ccPairs.length > 0 && (
|
{ccPairs.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Label>Document Sets</Label>
|
<Label small>Document Sets</Label>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<SubLabel>
|
<SubLabel>
|
||||||
@ -492,10 +538,11 @@ export function AssistantEditor({
|
|||||||
) : (
|
) : (
|
||||||
"Document Sets"
|
"Document Sets"
|
||||||
)}{" "}
|
)}{" "}
|
||||||
that this Assistant should search through.
|
that this Assistant should search
|
||||||
If none are specified, the Assistant will
|
through. If none are specified, the
|
||||||
search through all available documents in
|
Assistant will search through all
|
||||||
order to try and respond to queries.
|
available documents in order to try and
|
||||||
|
respond to queries.
|
||||||
</>
|
</>
|
||||||
</SubLabel>
|
</SubLabel>
|
||||||
</div>
|
</div>
|
||||||
@ -546,21 +593,15 @@ export function AssistantEditor({
|
|||||||
</Italic>
|
</Italic>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<>
|
<div className="mt-6">
|
||||||
<TextFormField
|
<TextFormField
|
||||||
|
small={true}
|
||||||
name="num_chunks"
|
name="num_chunks"
|
||||||
label="Number of Chunks"
|
label="Number of Chunks"
|
||||||
placeholder="If unspecified, will use 10 chunks."
|
tooltip="How many chunks to feed the LLM"
|
||||||
subtext={
|
placeholder="Defaults to 10 chunks."
|
||||||
<div>
|
|
||||||
How many chunks should we feed into the
|
|
||||||
LLM when generating the final response?
|
|
||||||
Each chunk is ~400 words long.
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
// Allow only integer values
|
|
||||||
if (
|
if (
|
||||||
value === "" ||
|
value === "" ||
|
||||||
/^[0-9]+$/.test(value)
|
/^[0-9]+$/.test(value)
|
||||||
@ -570,9 +611,10 @@ export function AssistantEditor({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Label>Misc</Label>
|
|
||||||
|
|
||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
|
small
|
||||||
|
noPadding
|
||||||
|
alignTop
|
||||||
name="llm_relevance_filter"
|
name="llm_relevance_filter"
|
||||||
label="Apply LLM Relevance Filter"
|
label="Apply LLM Relevance Filter"
|
||||||
subtext={
|
subtext={
|
||||||
@ -581,6 +623,9 @@ export function AssistantEditor({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
|
small
|
||||||
|
noPadding
|
||||||
|
alignTop
|
||||||
name="include_citations"
|
name="include_citations"
|
||||||
label="Include Citations"
|
label="Include Citations"
|
||||||
subtext={`
|
subtext={`
|
||||||
@ -589,35 +634,15 @@ export function AssistantEditor({
|
|||||||
the same technique used by the default Assistants. In general, we recommend
|
the same technique used by the default Assistants. In general, we recommend
|
||||||
to leave this enabled in order to increase trust in the LLM answer.`}
|
to leave this enabled in order to increase trust in the LLM answer.`}
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</CollapsibleSection>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{imageGenerationTool &&
|
|
||||||
checkLLMSupportsImageInput(
|
|
||||||
providerDisplayNameToProviderName.get(
|
|
||||||
values.llm_model_provider_override || ""
|
|
||||||
) ||
|
|
||||||
defaultProviderName ||
|
|
||||||
"",
|
|
||||||
values.llm_model_version_override ||
|
|
||||||
defaultModelName ||
|
|
||||||
""
|
|
||||||
) && (
|
|
||||||
<BooleanFormField
|
|
||||||
name={`enabled_tools_map.${imageGenerationTool.id}`}
|
|
||||||
label="Image Generation Tool"
|
|
||||||
subtext="The Image Generation Tool allows the assistant to use DALL-E 3 to generate images. The tool will be used when the user asks the assistant to generate an image."
|
|
||||||
onChange={() => {
|
|
||||||
toggleToolInValues(imageGenerationTool.id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{customTools.length > 0 && (
|
{customTools.length > 0 && (
|
||||||
<>
|
<>
|
||||||
{customTools.map((tool) => (
|
{customTools.map((tool) => (
|
||||||
@ -633,106 +658,40 @@ export function AssistantEditor({
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</div>
|
||||||
</HidableSection>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{llmProviders.length > 0 && (
|
{llmProviders.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<HidableSection
|
<Divider />
|
||||||
sectionTitle="[Advanced] Model Selection"
|
|
||||||
defaultHidden
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<Text>
|
|
||||||
Pick which LLM to use for this Assistant. If left as
|
|
||||||
Default, will use{" "}
|
|
||||||
<b className="italic">{defaultModelName}</b>
|
|
||||||
.
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
For more information on the different LLMs, checkout
|
|
||||||
the{" "}
|
|
||||||
<a
|
|
||||||
href="https://platform.openai.com/docs/models"
|
|
||||||
target="_blank"
|
|
||||||
className="text-blue-500"
|
|
||||||
>
|
|
||||||
OpenAI docs
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<div className="flex mt-6">
|
<TextFormField
|
||||||
<div className="w-96">
|
name="task_prompt"
|
||||||
<SubLabel>LLM Provider</SubLabel>
|
label="Additional instructions (Optional)"
|
||||||
<SelectorFormField
|
isTextArea={true}
|
||||||
name="llm_model_provider_override"
|
placeholder="e.g. 'Remember to reference all of the points mentioned in my message to you and focus on identifying action items that can move things forward'"
|
||||||
options={llmProviders.map((llmProvider) => ({
|
onChange={(e) => {
|
||||||
name: llmProvider.name,
|
setFieldValue("task_prompt", e.target.value);
|
||||||
value: llmProvider.name,
|
triggerFinalPromptUpdate(
|
||||||
}))}
|
values.system_prompt,
|
||||||
includeDefault={true}
|
e.target.value,
|
||||||
onSelect={(selected) => {
|
searchToolEnabled()
|
||||||
if (
|
|
||||||
selected !==
|
|
||||||
values.llm_model_provider_override
|
|
||||||
) {
|
|
||||||
setFieldValue(
|
|
||||||
"llm_model_version_override",
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
setFieldValue(
|
|
||||||
"llm_model_provider_override",
|
|
||||||
selected
|
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
explanationText="Learn about prompting in our docs!"
|
||||||
|
explanationLink="https://docs.danswer.dev/guides/assistants"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
{values.llm_model_provider_override && (
|
|
||||||
<div className="w-96 ml-4">
|
|
||||||
<SubLabel>Model</SubLabel>
|
|
||||||
<SelectorFormField
|
|
||||||
name="llm_model_version_override"
|
|
||||||
options={
|
|
||||||
modelOptionsByProvider.get(
|
|
||||||
values.llm_model_provider_override
|
|
||||||
) || []
|
|
||||||
}
|
|
||||||
maxHeight="max-h-72"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
</HidableSection>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<div className="mb-6">
|
||||||
<HidableSection
|
<div className="flex gap-x-2 items-center">
|
||||||
sectionTitle="[Advanced] Starter Messages"
|
<div className="block font-medium text-base">
|
||||||
defaultHidden
|
Add Starter Messages (Optional){" "}
|
||||||
>
|
</div>
|
||||||
<>
|
|
||||||
<div className="mb-4">
|
|
||||||
<SubLabel>
|
|
||||||
Starter Messages help guide users to use this Assistant.
|
|
||||||
They are shown to the user as clickable options when
|
|
||||||
they select this Assistant. When selected, the specified
|
|
||||||
message is sent to the LLM as the initial user message.
|
|
||||||
</SubLabel>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FieldArray
|
<FieldArray
|
||||||
name="starter_messages"
|
name="starter_messages"
|
||||||
render={(
|
render={(arrayHelpers: ArrayHelpers<StarterMessage[]>) => (
|
||||||
arrayHelpers: ArrayHelpers<StarterMessage[]>
|
|
||||||
) => (
|
|
||||||
<div>
|
<div>
|
||||||
{values.starter_messages &&
|
{values.starter_messages &&
|
||||||
values.starter_messages.length > 0 &&
|
values.starter_messages.length > 0 &&
|
||||||
@ -745,7 +704,7 @@ export function AssistantEditor({
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="w-full mr-6 border border-border p-3 rounded">
|
<div className="w-full mr-6 border border-border p-3 rounded">
|
||||||
<div>
|
<div>
|
||||||
<Label>Name</Label>
|
<Label small>Name</Label>
|
||||||
<SubLabel>
|
<SubLabel>
|
||||||
Shows up as the "title" for
|
Shows up as the "title" for
|
||||||
this Starter Message. For example,
|
this Starter Message. For example,
|
||||||
@ -773,13 +732,12 @@ export function AssistantEditor({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Label>Description</Label>
|
<Label small>Description</Label>
|
||||||
<SubLabel>
|
<SubLabel>
|
||||||
A description which tells the user
|
A description which tells the user what
|
||||||
what they might want to use this
|
they might want to use this Starter
|
||||||
Starter Message for. For example
|
Message for. For example "to a
|
||||||
"to a client about a new
|
client about a new feature"
|
||||||
feature"
|
|
||||||
</SubLabel>
|
</SubLabel>
|
||||||
<Field
|
<Field
|
||||||
name={`starter_messages.${index}.description`}
|
name={`starter_messages.${index}.description`}
|
||||||
@ -803,7 +761,7 @@ export function AssistantEditor({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Label>Message</Label>
|
<Label small>Message</Label>
|
||||||
<SubLabel>
|
<SubLabel>
|
||||||
The actual message to be sent as the
|
The actual message to be sent as the
|
||||||
initial user message if a user selects
|
initial user message if a user selects
|
||||||
@ -837,9 +795,7 @@ export function AssistantEditor({
|
|||||||
<div className="my-auto">
|
<div className="my-auto">
|
||||||
<FiX
|
<FiX
|
||||||
className="my-auto w-10 h-10 cursor-pointer hover:bg-hover rounded p-2"
|
className="my-auto w-10 h-10 cursor-pointer hover:bg-hover rounded p-2"
|
||||||
onClick={() =>
|
onClick={() => arrayHelpers.remove(index)}
|
||||||
arrayHelpers.remove(index)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -866,18 +822,18 @@ export function AssistantEditor({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</div>
|
||||||
</HidableSection>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{isPaidEnterpriseFeaturesEnabled &&
|
{isPaidEnterpriseFeaturesEnabled &&
|
||||||
userGroups &&
|
userGroups &&
|
||||||
(!user || user.role === "admin") && (
|
(!user || user.role === "admin") && (
|
||||||
<>
|
<>
|
||||||
<HidableSection sectionTitle="Access">
|
<Divider />
|
||||||
<>
|
|
||||||
<BooleanFormField
|
<BooleanFormField
|
||||||
|
small
|
||||||
|
noPadding
|
||||||
|
alignTop
|
||||||
name="is_public"
|
name="is_public"
|
||||||
label="Is Public?"
|
label="Is Public?"
|
||||||
subtext="If set, this Assistant will be available to all users. If not, only the specified User Groups will be able to access it."
|
subtext="If set, this Assistant will be available to all users. If not, only the specified User Groups will be able to access it."
|
||||||
@ -929,9 +885,6 @@ export function AssistantEditor({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</HidableSection>
|
|
||||||
<Divider />
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
|
55
web/src/app/admin/assistants/CollapsibleSection.tsx
Normal file
55
web/src/app/admin/assistants/CollapsibleSection.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"use client";
|
||||||
|
import { Button } from "@tremor/react";
|
||||||
|
import React, { ReactNode, useState } from "react";
|
||||||
|
import { FiSettings } from "react-icons/fi";
|
||||||
|
|
||||||
|
interface CollapsibleSectionProps {
|
||||||
|
children: ReactNode;
|
||||||
|
prompt?: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CollapsibleSection: React.FC<CollapsibleSectionProps> = ({
|
||||||
|
children,
|
||||||
|
prompt,
|
||||||
|
className = "",
|
||||||
|
}) => {
|
||||||
|
const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const toggleCollapse = (e?: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
// Only toggle if the click is on the border or plus sign
|
||||||
|
if (
|
||||||
|
!e ||
|
||||||
|
e.currentTarget === e.target ||
|
||||||
|
(e.target as HTMLElement).classList.contains("collapse-toggle")
|
||||||
|
) {
|
||||||
|
setIsCollapsed(!isCollapsed);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`relative ${isCollapsed ? "h-6" : ""} ${className}`}
|
||||||
|
style={{ transition: "height 0.3s ease-out" }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
cursor-pointer
|
||||||
|
${isCollapsed ? "h-6" : "pl-4 border-l-2 border-border"}
|
||||||
|
`}
|
||||||
|
onClick={toggleCollapse}
|
||||||
|
>
|
||||||
|
{isCollapsed ? (
|
||||||
|
<span className="collapse-toggle text-lg absolute left-0 top-0 text-sm flex items-center gap-x-3 cursor-pointer">
|
||||||
|
<FiSettings className="pointer-events-none my-auto" size={16} />
|
||||||
|
{prompt}{" "}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<>{children}</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollapsibleSection;
|
@ -44,7 +44,7 @@ export function HidableSection({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!isHidden && <div className="mx-2 mt-2">{children}</div>}
|
{!isHidden && <div className="mx-2 gap-y-2 mt-2">{children}</div>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,10 @@ export default async function Page() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<BackButton />
|
<BackButton />
|
||||||
|
|
||||||
<AdminPageTitle
|
<AdminPageTitle
|
||||||
title="Create a New Persona"
|
title="Create a New Assistant"
|
||||||
icon={<RobotIcon size={32} />}
|
icon={<RobotIcon size={32} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{body}
|
{body}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -34,6 +34,7 @@ export interface FullLLMProvider extends LLMProvider {
|
|||||||
id: number;
|
id: number;
|
||||||
is_default_provider: boolean | null;
|
is_default_provider: boolean | null;
|
||||||
model_names: string[];
|
model_names: string[];
|
||||||
|
icon?: React.FC<{ size?: number; className?: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LLMProviderDescriptor {
|
export interface LLMProviderDescriptor {
|
||||||
|
@ -8,6 +8,7 @@ export interface Option<T> {
|
|||||||
value: T;
|
value: T;
|
||||||
description?: string;
|
description?: string;
|
||||||
metadata?: { [key: string]: any };
|
metadata?: { [key: string]: any };
|
||||||
|
icon?: React.FC<{ size?: number; className?: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StringOrNumberOption = Option<string | number>;
|
export type StringOrNumberOption = Option<string | number>;
|
||||||
@ -24,9 +25,7 @@ function StandardDropdownOption<T>({
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSelect(option)}
|
onClick={() => handleSelect(option)}
|
||||||
className={`w-full text-left block px-4 py-2.5 text-sm hover:bg-gray-800 ${
|
className={`w-full text-left block px-4 py-2.5 text-sm hover:bg-gray-800 ${index !== 0 ? " border-t-2 border-gray-600" : ""}`}
|
||||||
index !== 0 ? " border-t-2 border-gray-600" : ""
|
|
||||||
}`}
|
|
||||||
role="menuitem"
|
role="menuitem"
|
||||||
>
|
>
|
||||||
<p className="font-medium">{option.name}</p>
|
<p className="font-medium">{option.name}</p>
|
||||||
@ -216,9 +215,7 @@ export const CustomDropdown = ({
|
|||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div
|
<div
|
||||||
onClick={() => setIsOpen(!isOpen)}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
className={`absolute ${
|
className={`absolute ${direction === "up" ? "bottom-full pb-2" : "pt-2"} w-full z-30 box-shadow`}
|
||||||
direction === "up" ? "bottom-full pb-2" : "pt-2 "
|
|
||||||
} w-full z-30 box-shadow`}
|
|
||||||
>
|
>
|
||||||
{dropdown}
|
{dropdown}
|
||||||
</div>
|
</div>
|
||||||
@ -269,7 +266,7 @@ export function DefaultDropdownElement({
|
|||||||
onChange={() => null}
|
onChange={() => null}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{icon && icon({ size: 16, className: "mr-2 my-auto" })}
|
{icon && icon({ size: 16, className: "mr-2 h-4 w-4 my-auto" })}
|
||||||
{name}
|
{name}
|
||||||
</div>
|
</div>
|
||||||
{description && <div className="text-xs">{description}</div>}
|
{description && <div className="text-xs">{description}</div>}
|
||||||
@ -290,11 +287,13 @@ export function DefaultDropdown({
|
|||||||
includeDefault = false,
|
includeDefault = false,
|
||||||
side,
|
side,
|
||||||
maxHeight,
|
maxHeight,
|
||||||
|
defaultValue,
|
||||||
}: {
|
}: {
|
||||||
options: StringOrNumberOption[];
|
options: StringOrNumberOption[];
|
||||||
selected: string | null;
|
selected: string | null;
|
||||||
onSelect: (value: string | number | null) => void;
|
onSelect: (value: string | number | null) => void;
|
||||||
includeDefault?: boolean;
|
includeDefault?: boolean;
|
||||||
|
defaultValue?: string;
|
||||||
side?: "top" | "right" | "bottom" | "left";
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
maxHeight?: string;
|
maxHeight?: string;
|
||||||
}) {
|
}) {
|
||||||
@ -316,7 +315,7 @@ export function DefaultDropdown({
|
|||||||
>
|
>
|
||||||
<p className="line-clamp-1">
|
<p className="line-clamp-1">
|
||||||
{selectedOption?.name ||
|
{selectedOption?.name ||
|
||||||
(includeDefault ? "Default" : "Select an option...")}
|
(includeDefault ? defaultValue ?? "Default" : "Select an option...")}
|
||||||
</p>
|
</p>
|
||||||
<FiChevronDown className="my-auto ml-auto" />
|
<FiChevronDown className="my-auto ml-auto" />
|
||||||
</div>
|
</div>
|
||||||
@ -354,6 +353,7 @@ export function DefaultDropdown({
|
|||||||
description={option.description}
|
description={option.description}
|
||||||
onSelect={() => onSelect(option.value)}
|
onSelect={() => onSelect(option.value)}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
|
icon={option.icon}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
@ -10,7 +10,13 @@ import {
|
|||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { FormBodyBuilder } from "./types";
|
import { FormBodyBuilder } from "./types";
|
||||||
import { DefaultDropdown, StringOrNumberOption } from "@/components/Dropdown";
|
import { DefaultDropdown, StringOrNumberOption } from "@/components/Dropdown";
|
||||||
import { FiPlus, FiX } from "react-icons/fi";
|
import { FiInfo, FiPlus, FiX } from "react-icons/fi";
|
||||||
|
import {
|
||||||
|
TooltipProvider,
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@radix-ui/react-tooltip";
|
||||||
|
|
||||||
export function SectionHeader({
|
export function SectionHeader({
|
||||||
children,
|
children,
|
||||||
@ -20,8 +26,20 @@ export function SectionHeader({
|
|||||||
return <div className="mb-4 font-bold text-lg">{children}</div>;
|
return <div className="mb-4 font-bold text-lg">{children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Label({ children }: { children: string | JSX.Element }) {
|
export function Label({
|
||||||
return <div className="block font-medium text-base">{children}</div>;
|
children,
|
||||||
|
small,
|
||||||
|
}: {
|
||||||
|
children: string | JSX.Element;
|
||||||
|
small?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`block font-medium base ${small ? "text-sm" : "text-base"}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SubLabel({ children }: { children: string | JSX.Element }) {
|
export function SubLabel({ children }: { children: string | JSX.Element }) {
|
||||||
@ -29,7 +47,48 @@ export function SubLabel({ children }: { children: string | JSX.Element }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ManualErrorMessage({ children }: { children: string }) {
|
export function ManualErrorMessage({ children }: { children: string }) {
|
||||||
return <div className="text-error text-sm mt-1">{children}</div>;
|
return <div className="text-error text-sm">{children}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExplanationText({
|
||||||
|
text,
|
||||||
|
link,
|
||||||
|
}: {
|
||||||
|
text: string;
|
||||||
|
link?: string;
|
||||||
|
}) {
|
||||||
|
return link ? (
|
||||||
|
<a
|
||||||
|
className="underline cursor-pointer text-sm font-medium"
|
||||||
|
target="_blank"
|
||||||
|
href={link}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<div className="text-sm font-semibold">{text}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ToolTipDetails({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: string | JSX.Element;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<TooltipProvider delayDuration={50}>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<FiInfo size={12} />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent side="top" align="center">
|
||||||
|
<p className="bg-background-dark max-w-[200px] mb-1 text-sm rounded-lg p-1.5 text-inverted">
|
||||||
|
{children}
|
||||||
|
</p>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TextFormField({
|
export function TextFormField({
|
||||||
@ -47,6 +106,10 @@ export function TextFormField({
|
|||||||
isCode = false,
|
isCode = false,
|
||||||
fontSize,
|
fontSize,
|
||||||
hideError,
|
hideError,
|
||||||
|
tooltip,
|
||||||
|
explanationText,
|
||||||
|
explanationLink,
|
||||||
|
small,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
label: string;
|
label: string;
|
||||||
@ -62,6 +125,10 @@ export function TextFormField({
|
|||||||
isCode?: boolean;
|
isCode?: boolean;
|
||||||
fontSize?: "text-sm" | "text-base" | "text-lg";
|
fontSize?: "text-sm" | "text-base" | "text-lg";
|
||||||
hideError?: boolean;
|
hideError?: boolean;
|
||||||
|
tooltip?: string;
|
||||||
|
explanationText?: string;
|
||||||
|
explanationLink?: string;
|
||||||
|
small?: boolean;
|
||||||
}) {
|
}) {
|
||||||
let heightString = defaultHeight || "";
|
let heightString = defaultHeight || "";
|
||||||
if (isTextArea && !heightString) {
|
if (isTextArea && !heightString) {
|
||||||
@ -69,8 +136,25 @@ export function TextFormField({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-4">
|
<div className="mb-6">
|
||||||
<Label>{label}</Label>
|
<div className="flex gap-x-2 items-center">
|
||||||
|
<Label small={small}>{label}</Label>
|
||||||
|
|
||||||
|
{tooltip && <ToolTipDetails>{tooltip}</ToolTipDetails>}
|
||||||
|
|
||||||
|
{error ? (
|
||||||
|
<ManualErrorMessage>{error}</ManualErrorMessage>
|
||||||
|
) : (
|
||||||
|
!hideError && (
|
||||||
|
<ErrorMessage
|
||||||
|
name={name}
|
||||||
|
component="div"
|
||||||
|
className="text-error my-auto text-sm"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{subtext && <SubLabel>{subtext}</SubLabel>}
|
{subtext && <SubLabel>{subtext}</SubLabel>}
|
||||||
<Field
|
<Field
|
||||||
as={isTextArea ? "textarea" : "input"}
|
as={isTextArea ? "textarea" : "input"}
|
||||||
@ -78,6 +162,7 @@ export function TextFormField({
|
|||||||
name={name}
|
name={name}
|
||||||
id={name}
|
id={name}
|
||||||
className={`
|
className={`
|
||||||
|
${small && "text-sm"}
|
||||||
border
|
border
|
||||||
border-border
|
border-border
|
||||||
rounded
|
rounded
|
||||||
@ -95,16 +180,8 @@ export function TextFormField({
|
|||||||
autoComplete={autoCompleteDisabled ? "off" : undefined}
|
autoComplete={autoCompleteDisabled ? "off" : undefined}
|
||||||
{...(onChange ? { onChange } : {})}
|
{...(onChange ? { onChange } : {})}
|
||||||
/>
|
/>
|
||||||
{error ? (
|
{explanationText && (
|
||||||
<ManualErrorMessage>{error}</ManualErrorMessage>
|
<ExplanationText link={explanationLink} text={explanationText} />
|
||||||
) : (
|
|
||||||
!hideError && (
|
|
||||||
<ErrorMessage
|
|
||||||
name={name}
|
|
||||||
component="div"
|
|
||||||
className="text-red-500 text-sm mt-1"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -115,6 +192,9 @@ interface BooleanFormFieldProps {
|
|||||||
label: string;
|
label: string;
|
||||||
subtext?: string | JSX.Element;
|
subtext?: string | JSX.Element;
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
noPadding?: boolean;
|
||||||
|
small?: boolean;
|
||||||
|
alignTop?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BooleanFormField = ({
|
export const BooleanFormField = ({
|
||||||
@ -122,6 +202,9 @@ export const BooleanFormField = ({
|
|||||||
label,
|
label,
|
||||||
subtext,
|
subtext,
|
||||||
onChange,
|
onChange,
|
||||||
|
noPadding,
|
||||||
|
small,
|
||||||
|
alignTop,
|
||||||
}: BooleanFormFieldProps) => {
|
}: BooleanFormFieldProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
@ -129,19 +212,18 @@ export const BooleanFormField = ({
|
|||||||
<Field
|
<Field
|
||||||
name={name}
|
name={name}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="mx-3 px-5 w-3.5 h-3.5 my-auto"
|
className={`${noPadding ? "mr-3" : "mx-3"} px-5 w-3.5 h-3.5 ${alignTop ? "mt-1" : "my-auto"}`}
|
||||||
{...(onChange ? { onChange } : {})}
|
{...(onChange ? { onChange } : {})}
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<Label>{label}</Label>
|
<Label small={small}>{label}</Label>
|
||||||
{subtext && <SubLabel>{subtext}</SubLabel>}
|
{subtext && <SubLabel>{subtext}</SubLabel>}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name={name}
|
name={name}
|
||||||
component="div"
|
component="div"
|
||||||
className="text-red-500 text-sm mt-1"
|
className="text-error text-sm mt-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -252,6 +334,7 @@ interface SelectorFormFieldProps {
|
|||||||
side?: "top" | "right" | "bottom" | "left";
|
side?: "top" | "right" | "bottom" | "left";
|
||||||
maxHeight?: string;
|
maxHeight?: string;
|
||||||
onSelect?: (selected: string | number | null) => void;
|
onSelect?: (selected: string | number | null) => void;
|
||||||
|
defaultValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SelectorFormField({
|
export function SelectorFormField({
|
||||||
@ -263,6 +346,7 @@ export function SelectorFormField({
|
|||||||
side = "bottom",
|
side = "bottom",
|
||||||
maxHeight,
|
maxHeight,
|
||||||
onSelect,
|
onSelect,
|
||||||
|
defaultValue,
|
||||||
}: SelectorFormFieldProps) {
|
}: SelectorFormFieldProps) {
|
||||||
const [field] = useField<string>(name);
|
const [field] = useField<string>(name);
|
||||||
const { setFieldValue } = useFormikContext();
|
const { setFieldValue } = useFormikContext();
|
||||||
@ -280,13 +364,14 @@ export function SelectorFormField({
|
|||||||
includeDefault={includeDefault}
|
includeDefault={includeDefault}
|
||||||
side={side}
|
side={side}
|
||||||
maxHeight={maxHeight}
|
maxHeight={maxHeight}
|
||||||
|
defaultValue={defaultValue}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
name={name}
|
name={name}
|
||||||
component="div"
|
component="div"
|
||||||
className="text-red-500 text-sm mt-1"
|
className="text-error text-sm mt-1"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -44,6 +44,14 @@ import { SiBookstack } from "react-icons/si";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import jiraSVG from "../../../public/Jira.svg";
|
import jiraSVG from "../../../public/Jira.svg";
|
||||||
import confluenceSVG from "../../../public/Confluence.svg";
|
import confluenceSVG from "../../../public/Confluence.svg";
|
||||||
|
import openAISVG from "../../../public/Openai.svg";
|
||||||
|
import openSourceIcon from "../../../public/OpenSource.png";
|
||||||
|
|
||||||
|
import awsWEBP from "../../../public/Amazon.webp";
|
||||||
|
import azureIcon from "../../../public/Azure.png";
|
||||||
|
|
||||||
|
import anthropicSVG from "../../../public/Anthropic.svg";
|
||||||
|
|
||||||
import OCIStorageSVG from "../../../public/OCI.svg";
|
import OCIStorageSVG from "../../../public/OCI.svg";
|
||||||
import googleCloudStorageIcon from "../../../public/GoogleCloudStorage.png";
|
import googleCloudStorageIcon from "../../../public/GoogleCloudStorage.png";
|
||||||
import guruIcon from "../../../public/Guru.svg";
|
import guruIcon from "../../../public/Guru.svg";
|
||||||
@ -75,6 +83,48 @@ interface IconProps {
|
|||||||
export const defaultTailwindCSS = "my-auto flex flex-shrink-0 text-default";
|
export const defaultTailwindCSS = "my-auto flex flex-shrink-0 text-default";
|
||||||
export const defaultTailwindCSSBlue = "my-auto flex flex-shrink-0 text-link";
|
export const defaultTailwindCSSBlue = "my-auto flex flex-shrink-0 text-link";
|
||||||
|
|
||||||
|
export const OpenAIIcon = ({
|
||||||
|
size = 16,
|
||||||
|
className = defaultTailwindCSS,
|
||||||
|
}: IconProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ width: `${size + 4}px`, height: `${size + 4}px` }}
|
||||||
|
className={`w-[${size + 4}px] h-[${size + 4}px] -m-0.5 ` + className}
|
||||||
|
>
|
||||||
|
<Image src={openAISVG} alt="Logo" width="96" height="96" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OpenSourceIcon = ({
|
||||||
|
size = 16,
|
||||||
|
className = defaultTailwindCSS,
|
||||||
|
}: IconProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ width: `${size + 4}px`, height: `${size + 4}px` }}
|
||||||
|
className={`w-[${size + 4}px] h-[${size + 4}px] -m-0.5 ` + className}
|
||||||
|
>
|
||||||
|
<Image src={openSourceIcon} alt="Logo" width="96" height="96" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AnthropicIcon = ({
|
||||||
|
size = 16,
|
||||||
|
className = defaultTailwindCSS,
|
||||||
|
}: IconProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ width: `${size + 4}px`, height: `${size + 4}px` }}
|
||||||
|
className={`w-[${size + 4}px] h-[${size + 4}px] -m-0.5 ` + className}
|
||||||
|
>
|
||||||
|
<Image src={anthropicSVG} alt="Logo" width="96" height="96" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const PlugIcon = ({
|
export const PlugIcon = ({
|
||||||
size = 16,
|
size = 16,
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
@ -498,6 +548,36 @@ export const ProductboardIcon = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const AWSIcon = ({
|
||||||
|
size = 16,
|
||||||
|
className = defaultTailwindCSS,
|
||||||
|
}: IconProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
// Linear Icon has a bit more surrounding whitespace than other icons, which is why we need to adjust it here
|
||||||
|
style={{ width: `${size + 4}px`, height: `${size + 4}px` }}
|
||||||
|
className={`w-[${size + 4}px] h-[${size + 4}px] -m-0.5 ` + className}
|
||||||
|
>
|
||||||
|
<Image src={awsWEBP} alt="Logo" width="96" height="96" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AzureIcon = ({
|
||||||
|
size = 16,
|
||||||
|
className = defaultTailwindCSS,
|
||||||
|
}: IconProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
// Linear Icon has a bit more surrounding whitespace than other icons, which is why we need to adjust it here
|
||||||
|
style={{ width: `${size + 4}px`, height: `${size + 4}px` }}
|
||||||
|
className={`w-[${size + 4}px] h-[${size + 4}px] -m-0.5 ` + className}
|
||||||
|
>
|
||||||
|
<Image src={azureIcon} alt="Logo" width="96" height="96" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const LinearIcon = ({
|
export const LinearIcon = ({
|
||||||
size = 16,
|
size = 16,
|
||||||
className = defaultTailwindCSS,
|
className = defaultTailwindCSS,
|
||||||
|
@ -5,6 +5,14 @@ import { fetchSS } from "../utilsSS";
|
|||||||
import { FullLLMProvider } from "@/app/admin/models/llm/interfaces";
|
import { FullLLMProvider } from "@/app/admin/models/llm/interfaces";
|
||||||
import { ToolSnapshot } from "../tools/interfaces";
|
import { ToolSnapshot } from "../tools/interfaces";
|
||||||
import { fetchToolsSS } from "../tools/fetchTools";
|
import { fetchToolsSS } from "../tools/fetchTools";
|
||||||
|
import { IconManifestType } from "react-icons/lib";
|
||||||
|
import {
|
||||||
|
OpenAIIcon,
|
||||||
|
AnthropicIcon,
|
||||||
|
AWSIcon,
|
||||||
|
AzureIcon,
|
||||||
|
OpenSourceIcon,
|
||||||
|
} from "@/components/icons/icons";
|
||||||
|
|
||||||
export async function fetchAssistantEditorInfoSS(
|
export async function fetchAssistantEditorInfoSS(
|
||||||
personaId?: number | string
|
personaId?: number | string
|
||||||
@ -79,11 +87,27 @@ export async function fetchAssistantEditorInfoSS(
|
|||||||
`Failed to fetch LLM providers - ${await llmProvidersResponse.text()}`,
|
`Failed to fetch LLM providers - ${await llmProvidersResponse.text()}`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const llmProviders = (await llmProvidersResponse.json()) as FullLLMProvider[];
|
const llmProviders = (await llmProvidersResponse.json()) as FullLLMProvider[];
|
||||||
|
|
||||||
if (personaId && personaResponse && !personaResponse.ok) {
|
if (personaId && personaResponse && !personaResponse.ok) {
|
||||||
return [null, `Failed to fetch Persona - ${await personaResponse.text()}`];
|
return [null, `Failed to fetch Persona - ${await personaResponse.text()}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const provider of llmProviders) {
|
||||||
|
if (provider.provider == "openai") {
|
||||||
|
provider.icon = OpenAIIcon;
|
||||||
|
} else if (provider.provider == "anthropic") {
|
||||||
|
provider.icon = AnthropicIcon;
|
||||||
|
} else if (provider.provider == "bedrock") {
|
||||||
|
provider.icon = AzureIcon;
|
||||||
|
} else if (provider.provider == "azure") {
|
||||||
|
provider.icon = AWSIcon;
|
||||||
|
} else {
|
||||||
|
provider.icon = OpenSourceIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const existingPersona = personaResponse
|
const existingPersona = personaResponse
|
||||||
? ((await personaResponse.json()) as Persona)
|
? ((await personaResponse.json()) as Persona)
|
||||||
: null;
|
: null;
|
||||||
|
Reference in New Issue
Block a user