mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-30 15:18:50 +01:00
🚧 chore: merge latest dev build (#4288)
* fix: agent initialization, add `collectedUsage` handling * style: improve side panel styling * refactor(loadAgent): Optimize order agent project ID retrieval * feat: code execution * fix: typing issues * feat: ExecuteCode content part * refactor: use local state for default collapsed state of analysis content parts * fix: code parsing in ExecuteCode component * chore: bump agents package, export loadAuthValues * refactor: Update handleTools.js to use EnvVar for code execution tool authentication * WIP * feat: download code outputs * fix(useEventHandlers): type issues * feat: backend handling for code outputs * Refactor: Remove console.log statement in Part.tsx * refactor: add attachments to TMessage/messageSchema * WIP: prelim handling for code outputs * feat: attachments rendering * refactor: improve attachments rendering * fix: attachments, nullish edge case, handle attachments from event stream, bump agents package * fix filename download * fix: tool assignment for 'run code' on agent creation * fix: image handling by adding attachments * refactor: prevent agent creation without provider/model * refactor: remove unnecessary space in agent creation success message * refactor: select first model if selecting provider from empty on form * fix: Agent avatar bug * fix: `defaultAgentFormValues` causing boolean typing issue and typeerror * fix: capabilities counting as tools, causing duplication of them * fix: formatted messages edge case where consecutive content text type parts with the latter having tool_call_ids would cause consecutive AI messages to be created. furthermore, content could not be an array for tool_use messages (anthropic limitation) * chore: bump @librechat/agents dependency to version 1.6.9 * feat: bedrock agents * feat: new Agents icon * feat: agent titling * feat: agent landing * refactor: allow sharing agent globally only if user is admin or author * feat: initial AgentPanelSkeleton * feat: AgentPanelSkeleton * feat: collaborative agents * chore: add potential authorName as part of schema * chore: Remove unnecessary console.log statement * WIP: agent model parameters * chore: ToolsDialog typing and tool related localization chnages * refactor: update tool instance type (latest langchain class), and rename google tool to 'google' proper * chore: add back tools * feat: Agent knowledge files upload * refactor: better verbiage for disabled knowledge * chore: debug logs for file deletions * chore: debug logs for file deletions * feat: upload/delete agent knowledge/file-search files * feat: file search UI for agents * feat: first pass, file search tool * chore: update default agent capabilities and info
This commit is contained in:
parent
f33e75e2ee
commit
ad74350036
123 changed files with 3611 additions and 1541 deletions
|
|
@ -1,17 +1,16 @@
|
|||
import { Capabilities } from 'librechat-data-provider';
|
||||
import { AgentCapabilities } from 'librechat-data-provider';
|
||||
import type { Agent, AgentProvider, AgentModelParameters } from 'librechat-data-provider';
|
||||
import type { OptionWithIcon, ExtendedFile } from './types';
|
||||
|
||||
export type TAgentOption = OptionWithIcon &
|
||||
Agent & {
|
||||
files?: Array<[string, ExtendedFile]>;
|
||||
knowledge_files?: Array<[string, ExtendedFile]>;
|
||||
code_files?: Array<[string, ExtendedFile]>;
|
||||
};
|
||||
|
||||
export type AgentCapabilities = {
|
||||
[Capabilities.code_interpreter]: boolean;
|
||||
[Capabilities.image_vision]: boolean;
|
||||
[Capabilities.retrieval]: boolean;
|
||||
export type TAgentCapabilities = {
|
||||
[AgentCapabilities.execute_code]: boolean;
|
||||
[AgentCapabilities.file_search]: boolean;
|
||||
};
|
||||
|
||||
export type AgentForm = {
|
||||
|
|
@ -24,4 +23,4 @@ export type AgentForm = {
|
|||
model_parameters: AgentModelParameters;
|
||||
tools?: string[];
|
||||
provider?: AgentProvider | OptionWithIcon;
|
||||
} & AgentCapabilities;
|
||||
} & TAgentCapabilities;
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ export type AgentModelPanelProps = {
|
|||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
providers: Option[];
|
||||
models: Record<string, string[]>;
|
||||
agent_id?: string;
|
||||
};
|
||||
|
||||
export type AugmentedColumnDef<TData, TValue> = ColumnDef<TData, TValue> & DataColumnMeta;
|
||||
|
|
@ -430,6 +431,8 @@ export type Option = Record<string, unknown> & {
|
|||
value: string | number | null;
|
||||
};
|
||||
|
||||
export type StringOption = Option & { value: string | null };
|
||||
|
||||
export type VoiceOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
|
|
@ -545,4 +548,4 @@ declare global {
|
|||
interface Window {
|
||||
google_tag_manager?: unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,14 +11,13 @@ export default function FileRow({
|
|||
setFiles,
|
||||
setFilesLoading,
|
||||
assistant_id,
|
||||
// TODO: Agent file handling
|
||||
agent_id,
|
||||
tool_resource,
|
||||
fileFilter,
|
||||
isRTL,
|
||||
isRTL = false,
|
||||
Wrapper,
|
||||
}: {
|
||||
files: Map<string, ExtendedFile>;
|
||||
files: Map<string, ExtendedFile> | undefined;
|
||||
setFiles: React.Dispatch<React.SetStateAction<Map<string, ExtendedFile>>>;
|
||||
setFilesLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
fileFilter?: (file: ExtendedFile) => boolean;
|
||||
|
|
@ -28,13 +27,18 @@ export default function FileRow({
|
|||
isRTL?: boolean;
|
||||
Wrapper?: React.FC<{ children: React.ReactNode }>;
|
||||
}) {
|
||||
const files = Array.from(_files.values()).filter((file) =>
|
||||
const files = Array.from(_files?.values() ?? []).filter((file) =>
|
||||
fileFilter ? fileFilter(file) : true,
|
||||
);
|
||||
|
||||
const { mutateAsync } = useDeleteFilesMutation({
|
||||
onMutate: async () =>
|
||||
console.log('Deleting files: assistant_id, tool_resource', assistant_id, tool_resource),
|
||||
console.log(
|
||||
'Deleting files: agent_id, assistant_id, tool_resource',
|
||||
agent_id,
|
||||
assistant_id,
|
||||
tool_resource,
|
||||
),
|
||||
onSuccess: () => {
|
||||
console.log('Files deleted');
|
||||
},
|
||||
|
|
@ -43,13 +47,9 @@ export default function FileRow({
|
|||
},
|
||||
});
|
||||
|
||||
const { deleteFile } = useFileDeletion({ mutateAsync, assistant_id, tool_resource });
|
||||
const { deleteFile } = useFileDeletion({ mutateAsync, agent_id, assistant_id, tool_resource });
|
||||
|
||||
useEffect(() => {
|
||||
if (!files) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -87,11 +87,12 @@ export default function FileRow({
|
|||
)
|
||||
.uniqueFiles.map((file: ExtendedFile, index: number) => {
|
||||
const handleDelete = () => deleteFile({ file, setFiles });
|
||||
if (file.type?.startsWith('image')) {
|
||||
const isImage = file.type?.startsWith('image') ?? false;
|
||||
if (isImage) {
|
||||
return (
|
||||
<Image
|
||||
key={index}
|
||||
url={file.preview || file.filepath}
|
||||
url={file.preview ?? file.filepath}
|
||||
onDelete={handleDelete}
|
||||
progress={file.progress}
|
||||
source={file.source}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
import { useMemo } from 'react';
|
||||
import { EModelEndpoint, isAssistantsEndpoint, Constants } from 'librechat-data-provider';
|
||||
import { EModelEndpoint, Constants } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { useGetAssistantDocsQuery } from '~/data-provider';
|
||||
import ConvoIcon from '~/components/Endpoints/ConvoIcon';
|
||||
import { getIconEndpoint, getEntity, cn } from '~/utils';
|
||||
import { useLocalize, useSubmitMessage } from '~/hooks';
|
||||
import { TooltipAnchor } from '~/components/ui';
|
||||
import { BirthdayIcon } from '~/components/svg';
|
||||
import { getIconEndpoint, cn } from '~/utils';
|
||||
import ConvoStarter from './ConvoStarter';
|
||||
|
||||
export default function Landing({ Header }: { Header?: ReactNode }) {
|
||||
const { conversation } = useChatContext();
|
||||
const agentsMap = useAgentsMapContext();
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
const { data: startupConfig } = useGetStartupConfig();
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
|
|
@ -20,7 +22,6 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
const localize = useLocalize();
|
||||
|
||||
let { endpoint = '' } = conversation ?? {};
|
||||
const { assistant_id = null } = conversation ?? {};
|
||||
|
||||
if (
|
||||
endpoint === EModelEndpoint.chatGPTBrowser ||
|
||||
|
|
@ -36,20 +37,32 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
select: (data) => new Map(data.map((dbA) => [dbA.assistant_id, dbA])),
|
||||
});
|
||||
|
||||
const isAssistant = isAssistantsEndpoint(endpoint);
|
||||
const assistant = isAssistant ? assistantMap?.[endpoint][assistant_id ?? ''] : undefined;
|
||||
const assistantName = assistant?.name ?? '';
|
||||
const assistantDesc = assistant?.description ?? '';
|
||||
const avatar = assistant?.metadata?.avatar ?? '';
|
||||
const { entity, isAgent, isAssistant } = getEntity({
|
||||
endpoint,
|
||||
agentsMap,
|
||||
assistantMap,
|
||||
agent_id: conversation?.agent_id,
|
||||
assistant_id: conversation?.assistant_id,
|
||||
});
|
||||
|
||||
const name = entity?.name ?? '';
|
||||
const description = entity?.description ?? '';
|
||||
const avatar = isAgent
|
||||
? (entity as t.Agent | undefined)?.avatar?.filepath ?? ''
|
||||
: ((entity as t.Assistant | undefined)?.metadata?.avatar as string | undefined) ?? '';
|
||||
const conversation_starters = useMemo(() => {
|
||||
/* The user made updates, use client-side cache, */
|
||||
if (assistant?.conversation_starters) {
|
||||
return assistant.conversation_starters;
|
||||
/* The user made updates, use client-side cache, or they exist in an Agent */
|
||||
if (entity && (entity.conversation_starters?.length ?? 0) > 0) {
|
||||
return entity.conversation_starters;
|
||||
}
|
||||
if (isAgent) {
|
||||
return entity?.conversation_starters ?? [];
|
||||
}
|
||||
|
||||
/* If none in cache, we use the latest assistant docs */
|
||||
const assistantDocs = documentsMap.get(assistant_id ?? '');
|
||||
return assistantDocs?.conversation_starters ?? [];
|
||||
}, [documentsMap, assistant_id, assistant?.conversation_starters]);
|
||||
const entityDocs = documentsMap.get(entity?.id ?? '');
|
||||
return entityDocs?.conversation_starters ?? [];
|
||||
}, [documentsMap, isAgent, entity]);
|
||||
|
||||
const containerClassName =
|
||||
'shadow-stroke relative flex h-full items-center justify-center rounded-full bg-white text-black';
|
||||
|
|
@ -57,14 +70,32 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
const { submitMessage } = useSubmitMessage();
|
||||
const sendConversationStarter = (text: string) => submitMessage({ text });
|
||||
|
||||
const getWelcomeMessage = () => {
|
||||
const greeting = conversation?.greeting ?? '';
|
||||
if (greeting) {
|
||||
return greeting;
|
||||
}
|
||||
|
||||
if (isAssistant) {
|
||||
return localize('com_nav_welcome_assistant');
|
||||
}
|
||||
|
||||
if (isAgent) {
|
||||
return localize('com_nav_welcome_agent');
|
||||
}
|
||||
|
||||
return localize('com_nav_welcome_message');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative h-full">
|
||||
<div className="absolute left-0 right-0">{Header != null ? Header : null}</div>
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<div className={cn('relative h-12 w-12', assistantName && avatar ? 'mb-0' : 'mb-3')}>
|
||||
<div className={cn('relative h-12 w-12', name && avatar ? 'mb-0' : 'mb-3')}>
|
||||
<ConvoIcon
|
||||
conversation={conversation}
|
||||
agentsMap={agentsMap}
|
||||
assistantMap={assistantMap}
|
||||
conversation={conversation}
|
||||
endpointsConfig={endpointsConfig}
|
||||
containerClassName={containerClassName}
|
||||
context="landing"
|
||||
|
|
@ -80,11 +111,11 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
</TooltipAnchor>
|
||||
) : null}
|
||||
</div>
|
||||
{assistantName ? (
|
||||
{name ? (
|
||||
<div className="flex flex-col items-center gap-0 p-2">
|
||||
<div className="text-center text-2xl font-medium dark:text-white">{assistantName}</div>
|
||||
<div className="text-center text-2xl font-medium dark:text-white">{name}</div>
|
||||
<div className="max-w-md text-center text-sm font-normal text-text-primary ">
|
||||
{assistantDesc ? assistantDesc : localize('com_nav_welcome_message')}
|
||||
{description ? description : localize('com_nav_welcome_message')}
|
||||
</div>
|
||||
{/* <div className="mt-1 flex items-center gap-1 text-token-text-tertiary">
|
||||
<div className="text-sm text-token-text-tertiary">By Daniel Avila</div>
|
||||
|
|
@ -92,16 +123,14 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
</div>
|
||||
) : (
|
||||
<h2 className="mb-5 max-w-[75vh] px-12 text-center text-lg font-medium dark:text-white md:px-0 md:text-2xl">
|
||||
{isAssistant
|
||||
? conversation?.greeting ?? localize('com_nav_welcome_assistant')
|
||||
: conversation?.greeting ?? localize('com_nav_welcome_message')}
|
||||
{getWelcomeMessage()}
|
||||
</h2>
|
||||
)}
|
||||
<div className="mt-8 flex flex-wrap justify-center gap-3 px-4">
|
||||
{conversation_starters.length > 0 &&
|
||||
conversation_starters
|
||||
.slice(0, Constants.MAX_CONVO_STARTERS)
|
||||
.map((text, index) => (
|
||||
.map((text: string, index: number) => (
|
||||
<ConvoStarter
|
||||
key={index}
|
||||
text={text}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { IconMapProps, AgentIconMapProps } from '~/common';
|
||||
import { BrainCircuit } from 'lucide-react';
|
||||
import { Feather } from 'lucide-react';
|
||||
import {
|
||||
MinimalPlugin,
|
||||
GPTIcon,
|
||||
|
|
@ -17,7 +17,13 @@ import {
|
|||
import UnknownIcon from './UnknownIcon';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AssistantAvatar = ({ className = '', assistantName, avatar, size }: IconMapProps) => {
|
||||
const AssistantAvatar = ({
|
||||
className = '',
|
||||
assistantName = '',
|
||||
avatar = '',
|
||||
context,
|
||||
size,
|
||||
}: IconMapProps) => {
|
||||
if (assistantName && avatar) {
|
||||
return (
|
||||
<img
|
||||
|
|
@ -32,10 +38,10 @@ const AssistantAvatar = ({ className = '', assistantName, avatar, size }: IconMa
|
|||
return <AssistantIcon className={cn('text-token-secondary', className)} size={size} />;
|
||||
}
|
||||
|
||||
return <Sparkles className={cn(assistantName === '' ? 'icon-2xl' : '', className)} />;
|
||||
return <Sparkles className={cn(context === 'landing' ? 'icon-2xl' : '', className)} />;
|
||||
};
|
||||
|
||||
const AgentAvatar = ({ className = '', agentName, avatar, size }: AgentIconMapProps) => {
|
||||
const AgentAvatar = ({ className = '', avatar = '', agentName, size }: AgentIconMapProps) => {
|
||||
if (agentName && avatar) {
|
||||
return (
|
||||
<img
|
||||
|
|
@ -46,11 +52,9 @@ const AgentAvatar = ({ className = '', agentName, avatar, size }: AgentIconMapPr
|
|||
height="80"
|
||||
/>
|
||||
);
|
||||
} else if (agentName) {
|
||||
return <AssistantIcon className={cn('text-token-secondary', className)} size={size} />;
|
||||
}
|
||||
|
||||
return <BrainCircuit className={cn(agentName === '' ? 'icon-2xl' : '', className)} />;
|
||||
return <Feather className={cn(agentName === '' ? 'icon-2xl' : '', className)} size={size} />;
|
||||
};
|
||||
|
||||
const Bedrock = ({ className = '' }: IconMapProps) => {
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ const MenuItem: FC<MenuItemProps> = ({
|
|||
<div className="flex grow items-center justify-between gap-2">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
{Icon && (
|
||||
{Icon != null && (
|
||||
<Icon
|
||||
size={18}
|
||||
endpoint={endpoint}
|
||||
|
|
|
|||
|
|
@ -1,31 +1,38 @@
|
|||
import { alternateName } from 'librechat-data-provider';
|
||||
import { Content, Portal, Root } from '@radix-ui/react-popover';
|
||||
import { alternateName, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { FC } from 'react';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { useChatContext, useAgentsMapContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { mapEndpoints, getEntity } from '~/utils';
|
||||
import EndpointItems from './Endpoints/MenuItems';
|
||||
import TitleButton from './UI/TitleButton';
|
||||
import { mapEndpoints } from '~/utils';
|
||||
|
||||
const EndpointsMenu: FC = () => {
|
||||
const { data: endpoints = [] } = useGetEndpointsQuery({
|
||||
select: mapEndpoints,
|
||||
});
|
||||
|
||||
const { conversation } = useChatContext();
|
||||
const { endpoint = '', assistant_id = null } = conversation ?? {};
|
||||
const agentsMap = useAgentsMapContext();
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
|
||||
const assistant =
|
||||
isAssistantsEndpoint(endpoint) && assistantMap?.[endpoint ?? '']?.[assistant_id ?? ''];
|
||||
const assistantName = (assistant && assistant?.name) || 'Assistant';
|
||||
const { conversation } = useChatContext();
|
||||
const { endpoint = '' } = conversation ?? {};
|
||||
|
||||
if (!endpoint) {
|
||||
console.warn('No endpoint selected');
|
||||
return null;
|
||||
}
|
||||
|
||||
const primaryText = assistant ? assistantName : (alternateName[endpoint] ?? endpoint ?? '') + ' ';
|
||||
const { entity } = getEntity({
|
||||
endpoint,
|
||||
agentsMap,
|
||||
assistantMap,
|
||||
agent_id: conversation?.agent_id,
|
||||
assistant_id: conversation?.assistant_id,
|
||||
});
|
||||
|
||||
const primaryText = entity
|
||||
? entity.name
|
||||
: (alternateName[endpoint] as string | undefined) ?? endpoint;
|
||||
|
||||
return (
|
||||
<Root>
|
||||
|
|
@ -44,7 +51,7 @@ const EndpointsMenu: FC = () => {
|
|||
<Content
|
||||
side="bottom"
|
||||
align="start"
|
||||
className="mt-2 max-h-[65vh] min-w-[340px] overflow-y-auto rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-700 dark:text-white lg:max-h-[75vh]"
|
||||
className="mt-2 max-h-[65vh] min-w-[340px] overflow-y-auto rounded-lg border border-border-light bg-header-primary text-text-primary shadow-lg lg:max-h-[75vh]"
|
||||
>
|
||||
<EndpointItems endpoints={endpoints} selected={endpoint} />
|
||||
</Content>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import ProgressCircle from './ProgressCircle';
|
||||
import CancelledIcon from './CancelledIcon';
|
||||
import { CodeInProgress } from './Parts/CodeProgress';
|
||||
import { useProgress, useLocalize } from '~/hooks';
|
||||
import ProgressText from './ProgressText';
|
||||
import FinishedIcon from './FinishedIcon';
|
||||
import MarkdownLite from './MarkdownLite';
|
||||
import { useProgress } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
export default function CodeAnalyze({
|
||||
|
|
@ -19,9 +18,11 @@ export default function CodeAnalyze({
|
|||
outputs: Record<string, unknown>[];
|
||||
isSubmitting: boolean;
|
||||
}) {
|
||||
const showCodeDefault = useRecoilValue(store.showCode);
|
||||
const [showCode, setShowCode] = useState(showCodeDefault);
|
||||
const localize = useLocalize();
|
||||
const progress = useProgress(initialProgress);
|
||||
const showAnalysisCode = useRecoilValue(store.showCode);
|
||||
const [showCode, setShowCode] = useState(showAnalysisCode);
|
||||
|
||||
const radius = 56.08695652173913;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const offset = circumference - progress * circumference;
|
||||
|
|
@ -62,7 +63,7 @@ export default function CodeAnalyze({
|
|||
<MarkdownLite content={code ? `\`\`\`python\n${code}\n\`\`\`` : ''} />
|
||||
{logs && (
|
||||
<div className="bg-gray-700 p-4 text-xs">
|
||||
<div className="mb-1 text-gray-400">Result</div>
|
||||
<div className="mb-1 text-gray-400">{localize('com_ui_result')}</div>
|
||||
<div
|
||||
className="prose flex flex-col-reverse text-white"
|
||||
style={{
|
||||
|
|
@ -78,91 +79,3 @@ export default function CodeAnalyze({
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const CodeInProgress = ({
|
||||
offset,
|
||||
circumference,
|
||||
radius,
|
||||
isSubmitting,
|
||||
progress,
|
||||
}: {
|
||||
progress: number;
|
||||
offset: number;
|
||||
circumference: number;
|
||||
radius: number;
|
||||
isSubmitting: boolean;
|
||||
}) => {
|
||||
if (progress < 1 && !isSubmitting) {
|
||||
return <CancelledIcon />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center rounded-full bg-transparent text-white"
|
||||
style={{ opacity: 1, transform: 'none' }}
|
||||
data-projection-id="77"
|
||||
>
|
||||
<div className="absolute bottom-[1.5px] right-[1.5px]">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
height="20"
|
||||
style={{ transform: 'translate3d(0px, 0px, 0px)' }}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="__lottie_element_11">
|
||||
<rect width="20" height="20" x="0" y="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clipPath="url(#__lottie_element_11)">
|
||||
<g
|
||||
style={{ display: 'block', transform: 'matrix(1,0,0,1,-2,-2)', opacity: 1 }}
|
||||
className="slide-from-left"
|
||||
>
|
||||
<g opacity="1" transform="matrix(1,0,0,1,7.026679992675781,8.834091186523438)">
|
||||
<path
|
||||
fill="rgb(177,98,253)"
|
||||
fillOpacity="1"
|
||||
d=" M1.2870399951934814,0.2207774966955185 C0.992609977722168,-0.07359249889850616 0.5152599811553955,-0.07359249889850616 0.22082999348640442,0.2207774966955185 C-0.07361000031232834,0.5151575207710266 -0.07361000031232834,0.992437481880188 0.22082999348640442,1.2868175506591797 C0.8473266959190369,1.9131841659545898 1.4738233089447021,2.53955078125 2.1003201007843018,3.16591739654541 C1.4738233089447021,3.7922842502593994 0.8473266959190369,4.4186506271362305 0.22082999348640442,5.045017719268799 C-0.07361000031232834,5.339417457580566 -0.07361000031232834,5.816617488861084 0.22082999348640442,6.11101770401001 C0.5152599811553955,6.405417442321777 0.992609977722168,6.405417442321777 1.2870399951934814,6.11101770401001 C2.091266632080078,5.306983947753906 2.895493268966675,4.502950668334961 3.6997199058532715,3.6989173889160156 C3.994119882583618,3.404517412185669 3.994119882583618,2.927217483520508 3.6997199058532715,2.6329174041748047 C2.895493268966675,1.8288708925247192 2.091266632080078,1.0248241424560547 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fillOpacity="0"
|
||||
stroke="rgb(177,98,253)"
|
||||
strokeOpacity="1"
|
||||
strokeWidth="0.201031"
|
||||
d=" M1.2870399951934814,0.2207774966955185 C0.992609977722168,-0.07359249889850616 0.5152599811553955,-0.07359249889850616 0.22082999348640442,0.2207774966955185 C-0.07361000031232834,0.5151575207710266 -0.07361000031232834,0.992437481880188 0.22082999348640442,1.2868175506591797 C0.8473266959190369,1.9131841659545898 1.4738233089447021,2.53955078125 2.1003201007843018,3.16591739654541 C1.4738233089447021,3.7922842502593994 0.8473266959190369,4.4186506271362305 0.22082999348640442,5.045017719268799 C-0.07361000031232834,5.339417457580566 -0.07361000031232834,5.816617488861084 0.22082999348640442,6.11101770401001 C0.5152599811553955,6.405417442321777 0.992609977722168,6.405417442321777 1.2870399951934814,6.11101770401001 C2.091266632080078,5.306983947753906 2.895493268966675,4.502950668334961 3.6997199058532715,3.6989173889160156 C3.994119882583618,3.404517412185669 3.994119882583618,2.927217483520508 3.6997199058532715,2.6329174041748047 C2.895493268966675,1.8288708925247192 2.091266632080078,1.0248241424560547 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
style={{ display: 'block', transform: 'matrix(1,0,0,1,-2,-2)', opacity: 1 }}
|
||||
className="slide-to-down"
|
||||
>
|
||||
<g opacity="1" transform="matrix(1,0,0,1,11.79640007019043,13.512199401855469)">
|
||||
<path
|
||||
fill="rgb(177,98,253)"
|
||||
fillOpacity="1"
|
||||
d=" M4.3225998878479,0 C3.1498000621795654,0 1.9769999980926514,0 0.8041999936103821,0 C0.36010000109672546,0 0,0.36000001430511475 0,0.804099977016449 C0,1.2482000589370728 0.36010000109672546,1.6081000566482544 0.8041999936103821,1.6081000566482544 C1.9769999980926514,1.6081000566482544 3.1498000621795654,1.6081000566482544 4.3225998878479,1.6081000566482544 C4.7667999267578125,1.6081000566482544 5.126800060272217,1.2482000589370728 5.126800060272217,0.804099977016449 C5.126800060272217,0.36000001430511475 4.7667999267578125,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fillOpacity="0"
|
||||
stroke="rgb(177,98,253)"
|
||||
strokeOpacity="1"
|
||||
strokeWidth="0.100515"
|
||||
d=" M4.3225998878479,0 C3.1498000621795654,0 1.9769999980926514,0 0.8041999936103821,0 C0.36010000109672546,0 0,0.36000001430511475 0,0.804099977016449 C0,1.2482000589370728 0.36010000109672546,1.6081000566482544 0.8041999936103821,1.6081000566482544 C1.9769999980926514,1.6081000566482544 3.1498000621795654,1.6081000566482544 4.3225998878479,1.6081000566482544 C4.7667999267578125,1.6081000566482544 5.126800060272217,1.2482000589370728 5.126800060272217,0.804099977016449 C5.126800060272217,0.36000001430511475 4.7667999267578125,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<ProgressCircle radius={radius} circumference={circumference} offset={offset} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
import { memo } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ContentTypes } from 'librechat-data-provider';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageContentParts, TAttachment, Agents } from 'librechat-data-provider';
|
||||
import EditTextPart from './Parts/EditTextPart';
|
||||
import { mapAttachments } from '~/utils/map';
|
||||
import store from '~/store';
|
||||
import Part from './Part';
|
||||
|
||||
type ContentPartsProps = {
|
||||
content: Array<TMessageContentParts | undefined> | undefined;
|
||||
messageId: string;
|
||||
attachments?: TAttachment[];
|
||||
isCreatedByUser: boolean;
|
||||
isLast: boolean;
|
||||
isSubmitting: boolean;
|
||||
|
|
@ -23,6 +27,7 @@ const ContentParts = memo(
|
|||
({
|
||||
content,
|
||||
messageId,
|
||||
attachments,
|
||||
isCreatedByUser,
|
||||
isLast,
|
||||
isSubmitting,
|
||||
|
|
@ -31,6 +36,11 @@ const ContentParts = memo(
|
|||
siblingIdx,
|
||||
setSiblingIdx,
|
||||
}: ContentPartsProps) => {
|
||||
const messageAttachmentsMap = useRecoilValue(store.messageAttachmentsMap);
|
||||
const attachmentMap = useMemo(
|
||||
() => mapAttachments(attachments ?? messageAttachmentsMap[messageId] ?? []),
|
||||
[attachments, messageAttachmentsMap, messageId],
|
||||
);
|
||||
if (!content) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -58,20 +68,28 @@ const ContentParts = memo(
|
|||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{content
|
||||
.filter((part) => part)
|
||||
.map((part, idx) => (
|
||||
<Part
|
||||
key={`display-${messageId}-${idx}`}
|
||||
part={part}
|
||||
isSubmitting={isSubmitting}
|
||||
showCursor={idx === content.length - 1 && isLast}
|
||||
messageId={messageId}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
/>
|
||||
))}
|
||||
.map((part, idx) => {
|
||||
const toolCallId =
|
||||
(part?.[ContentTypes.TOOL_CALL] as Agents.ToolCall | undefined)?.id ?? '';
|
||||
const attachments = attachmentMap[toolCallId];
|
||||
|
||||
return (
|
||||
<Part
|
||||
part={part}
|
||||
isSubmitting={isSubmitting}
|
||||
attachments={attachments}
|
||||
key={`display-${messageId}-${idx}`}
|
||||
showCursor={idx === content.length - 1 && isLast}
|
||||
messageId={messageId}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import {
|
||||
ToolCallTypes,
|
||||
Tools,
|
||||
ContentTypes,
|
||||
ToolCallTypes,
|
||||
imageGenTools,
|
||||
isImageVisionTool,
|
||||
} from 'librechat-data-provider';
|
||||
import { memo } from 'react';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageContentParts, TAttachment } from 'librechat-data-provider';
|
||||
import { ErrorMessage } from './MessageContent';
|
||||
import ExecuteCode from './Parts/ExecuteCode';
|
||||
import RetrievalCall from './RetrievalCall';
|
||||
import CodeAnalyze from './CodeAnalyze';
|
||||
import Container from './Container';
|
||||
|
|
@ -21,125 +23,141 @@ type PartProps = {
|
|||
showCursor: boolean;
|
||||
messageId: string;
|
||||
isCreatedByUser: boolean;
|
||||
attachments?: TAttachment[];
|
||||
};
|
||||
|
||||
const Part = memo(({ part, isSubmitting, showCursor, messageId, isCreatedByUser }: PartProps) => {
|
||||
if (!part) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (part.type === ContentTypes.ERROR) {
|
||||
return <ErrorMessage text={part[ContentTypes.TEXT].value} className="my-2" />;
|
||||
} else if (part.type === ContentTypes.TEXT) {
|
||||
const text = typeof part.text === 'string' ? part.text : part.text.value;
|
||||
|
||||
if (typeof text !== 'string') {
|
||||
return null;
|
||||
}
|
||||
if (part.tool_call_ids != null && !text) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Container>
|
||||
<Text
|
||||
text={text}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
messageId={messageId}
|
||||
showCursor={showCursor}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
} else if (part.type === ContentTypes.TOOL_CALL) {
|
||||
const toolCall = part[ContentTypes.TOOL_CALL];
|
||||
|
||||
if (!toolCall) {
|
||||
const Part = memo(
|
||||
({ part, isSubmitting, attachments, showCursor, messageId, isCreatedByUser }: PartProps) => {
|
||||
attachments && console.log(attachments);
|
||||
if (!part) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ('args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL)) {
|
||||
if (part.type === ContentTypes.ERROR) {
|
||||
return <ErrorMessage text={part[ContentTypes.TEXT].value} className="my-2" />;
|
||||
} else if (part.type === ContentTypes.TEXT) {
|
||||
const text = typeof part.text === 'string' ? part.text : part.text.value;
|
||||
|
||||
if (typeof text !== 'string') {
|
||||
return null;
|
||||
}
|
||||
if (part.tool_call_ids != null && !text) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ToolCall
|
||||
args={toolCall.args ?? ''}
|
||||
name={toolCall.name ?? ''}
|
||||
output={toolCall.output ?? ''}
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
<Container>
|
||||
<Text
|
||||
text={text}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
messageId={messageId}
|
||||
showCursor={showCursor}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
} else if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) {
|
||||
const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER];
|
||||
return (
|
||||
<CodeAnalyze
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
code={code_interpreter.input}
|
||||
outputs={code_interpreter.outputs ?? []}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
toolCall.type === ToolCallTypes.RETRIEVAL ||
|
||||
toolCall.type === ToolCallTypes.FILE_SEARCH
|
||||
) {
|
||||
return (
|
||||
<RetrievalCall initialProgress={toolCall.progress ?? 0.1} isSubmitting={isSubmitting} />
|
||||
);
|
||||
} else if (
|
||||
toolCall.type === ToolCallTypes.FUNCTION &&
|
||||
ToolCallTypes.FUNCTION in toolCall &&
|
||||
imageGenTools.has(toolCall.function.name)
|
||||
) {
|
||||
return (
|
||||
<ImageGen
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
args={toolCall.function.arguments as string}
|
||||
/>
|
||||
);
|
||||
} else if (toolCall.type === ToolCallTypes.FUNCTION && ToolCallTypes.FUNCTION in toolCall) {
|
||||
if (isImageVisionTool(toolCall)) {
|
||||
if (isSubmitting && showCursor) {
|
||||
return (
|
||||
<Container>
|
||||
<Text
|
||||
text={''}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
messageId={messageId}
|
||||
showCursor={showCursor}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
} else if (part.type === ContentTypes.TOOL_CALL) {
|
||||
const toolCall = part[ContentTypes.TOOL_CALL];
|
||||
|
||||
if (!toolCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isToolCall =
|
||||
'args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL);
|
||||
if (isToolCall && toolCall.name === Tools.execute_code) {
|
||||
return (
|
||||
<ExecuteCode
|
||||
args={typeof toolCall.args === 'string' ? toolCall.args : ''}
|
||||
output={toolCall.output ?? ''}
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
isSubmitting={isSubmitting}
|
||||
attachments={attachments}
|
||||
/>
|
||||
);
|
||||
} else if (isToolCall) {
|
||||
return (
|
||||
<ToolCall
|
||||
args={toolCall.args ?? ''}
|
||||
name={toolCall.name ?? ''}
|
||||
output={toolCall.output ?? ''}
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
);
|
||||
} else if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) {
|
||||
const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER];
|
||||
return (
|
||||
<CodeAnalyze
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
code={code_interpreter.input}
|
||||
outputs={code_interpreter.outputs ?? []}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
toolCall.type === ToolCallTypes.RETRIEVAL ||
|
||||
toolCall.type === ToolCallTypes.FILE_SEARCH
|
||||
) {
|
||||
return (
|
||||
<RetrievalCall initialProgress={toolCall.progress ?? 0.1} isSubmitting={isSubmitting} />
|
||||
);
|
||||
} else if (
|
||||
toolCall.type === ToolCallTypes.FUNCTION &&
|
||||
ToolCallTypes.FUNCTION in toolCall &&
|
||||
imageGenTools.has(toolCall.function.name)
|
||||
) {
|
||||
return (
|
||||
<ImageGen
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
args={toolCall.function.arguments as string}
|
||||
/>
|
||||
);
|
||||
} else if (toolCall.type === ToolCallTypes.FUNCTION && ToolCallTypes.FUNCTION in toolCall) {
|
||||
if (isImageVisionTool(toolCall)) {
|
||||
if (isSubmitting && showCursor) {
|
||||
return (
|
||||
<Container>
|
||||
<Text
|
||||
text={''}
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
messageId={messageId}
|
||||
showCursor={showCursor}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolCall
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
isSubmitting={isSubmitting}
|
||||
args={toolCall.function.arguments as string}
|
||||
name={toolCall.function.name}
|
||||
output={toolCall.function.output}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (part.type === ContentTypes.IMAGE_FILE) {
|
||||
const imageFile = part[ContentTypes.IMAGE_FILE];
|
||||
const height = imageFile.height ?? 1920;
|
||||
const width = imageFile.width ?? 1080;
|
||||
return (
|
||||
<ToolCall
|
||||
initialProgress={toolCall.progress ?? 0.1}
|
||||
isSubmitting={isSubmitting}
|
||||
args={toolCall.function.arguments as string}
|
||||
name={toolCall.function.name}
|
||||
output={toolCall.function.output}
|
||||
<Image
|
||||
imagePath={imageFile.filepath}
|
||||
height={height}
|
||||
width={width}
|
||||
altText={imageFile.filename ?? 'Uploaded Image'}
|
||||
placeholderDimensions={{
|
||||
height: height + 'px',
|
||||
width: width + 'px',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (part.type === ContentTypes.IMAGE_FILE) {
|
||||
const imageFile = part[ContentTypes.IMAGE_FILE];
|
||||
const height = imageFile.height ?? 1920;
|
||||
const width = imageFile.width ?? 1080;
|
||||
return (
|
||||
<Image
|
||||
imagePath={imageFile.filepath}
|
||||
height={height}
|
||||
width={width}
|
||||
altText={imageFile.filename ?? 'Uploaded Image'}
|
||||
placeholderDimensions={{
|
||||
height: height + 'px',
|
||||
width: width + 'px',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
export default Part;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
import ProgressCircle from '~/components/Chat/Messages/Content/ProgressCircle';
|
||||
import CancelledIcon from '~/components/Chat/Messages/Content/CancelledIcon';
|
||||
|
||||
export const CodeInProgress = ({
|
||||
offset,
|
||||
circumference,
|
||||
radius,
|
||||
isSubmitting,
|
||||
progress,
|
||||
}: {
|
||||
progress: number;
|
||||
offset: number;
|
||||
circumference: number;
|
||||
radius: number;
|
||||
isSubmitting: boolean;
|
||||
}) => {
|
||||
if (progress < 1 && !isSubmitting) {
|
||||
return <CancelledIcon />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center rounded-full bg-transparent text-white"
|
||||
style={{ opacity: 1, transform: 'none' }}
|
||||
data-projection-id="77"
|
||||
>
|
||||
<div className="absolute bottom-[1.5px] right-[1.5px]">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
height="20"
|
||||
style={{ transform: 'translate3d(0px, 0px, 0px)' }}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<defs>
|
||||
<clipPath id="__lottie_element_11">
|
||||
<rect width="20" height="20" x="0" y="0" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clipPath="url(#__lottie_element_11)">
|
||||
<g
|
||||
style={{ display: 'block', transform: 'matrix(1,0,0,1,-2,-2)', opacity: 1 }}
|
||||
className="slide-from-left"
|
||||
>
|
||||
<g opacity="1" transform="matrix(1,0,0,1,7.026679992675781,8.834091186523438)">
|
||||
<path
|
||||
fill="rgb(177,98,253)"
|
||||
fillOpacity="1"
|
||||
d=" M1.2870399951934814,0.2207774966955185 C0.992609977722168,-0.07359249889850616 0.5152599811553955,-0.07359249889850616 0.22082999348640442,0.2207774966955185 C-0.07361000031232834,0.5151575207710266 -0.07361000031232834,0.992437481880188 0.22082999348640442,1.2868175506591797 C0.8473266959190369,1.9131841659545898 1.4738233089447021,2.53955078125 2.1003201007843018,3.16591739654541 C1.4738233089447021,3.7922842502593994 0.8473266959190369,4.4186506271362305 0.22082999348640442,5.045017719268799 C-0.07361000031232834,5.339417457580566 -0.07361000031232834,5.816617488861084 0.22082999348640442,6.11101770401001 C0.5152599811553955,6.405417442321777 0.992609977722168,6.405417442321777 1.2870399951934814,6.11101770401001 C2.091266632080078,5.306983947753906 2.895493268966675,4.502950668334961 3.6997199058532715,3.6989173889160156 C3.994119882583618,3.404517412185669 3.994119882583618,2.927217483520508 3.6997199058532715,2.6329174041748047 C2.895493268966675,1.8288708925247192 2.091266632080078,1.0248241424560547 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fillOpacity="0"
|
||||
stroke="rgb(177,98,253)"
|
||||
strokeOpacity="1"
|
||||
strokeWidth="0.201031"
|
||||
d=" M1.2870399951934814,0.2207774966955185 C0.992609977722168,-0.07359249889850616 0.5152599811553955,-0.07359249889850616 0.22082999348640442,0.2207774966955185 C-0.07361000031232834,0.5151575207710266 -0.07361000031232834,0.992437481880188 0.22082999348640442,1.2868175506591797 C0.8473266959190369,1.9131841659545898 1.4738233089447021,2.53955078125 2.1003201007843018,3.16591739654541 C1.4738233089447021,3.7922842502593994 0.8473266959190369,4.4186506271362305 0.22082999348640442,5.045017719268799 C-0.07361000031232834,5.339417457580566 -0.07361000031232834,5.816617488861084 0.22082999348640442,6.11101770401001 C0.5152599811553955,6.405417442321777 0.992609977722168,6.405417442321777 1.2870399951934814,6.11101770401001 C2.091266632080078,5.306983947753906 2.895493268966675,4.502950668334961 3.6997199058532715,3.6989173889160156 C3.994119882583618,3.404517412185669 3.994119882583618,2.927217483520508 3.6997199058532715,2.6329174041748047 C2.895493268966675,1.8288708925247192 2.091266632080078,1.0248241424560547 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 C1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185 1.2870399951934814,0.2207774966955185"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
style={{ display: 'block', transform: 'matrix(1,0,0,1,-2,-2)', opacity: 1 }}
|
||||
className="slide-to-down"
|
||||
>
|
||||
<g opacity="1" transform="matrix(1,0,0,1,11.79640007019043,13.512199401855469)">
|
||||
<path
|
||||
fill="rgb(177,98,253)"
|
||||
fillOpacity="1"
|
||||
d=" M4.3225998878479,0 C3.1498000621795654,0 1.9769999980926514,0 0.8041999936103821,0 C0.36010000109672546,0 0,0.36000001430511475 0,0.804099977016449 C0,1.2482000589370728 0.36010000109672546,1.6081000566482544 0.8041999936103821,1.6081000566482544 C1.9769999980926514,1.6081000566482544 3.1498000621795654,1.6081000566482544 4.3225998878479,1.6081000566482544 C4.7667999267578125,1.6081000566482544 5.126800060272217,1.2482000589370728 5.126800060272217,0.804099977016449 C5.126800060272217,0.36000001430511475 4.7667999267578125,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fillOpacity="0"
|
||||
stroke="rgb(177,98,253)"
|
||||
strokeOpacity="1"
|
||||
strokeWidth="0.100515"
|
||||
d=" M4.3225998878479,0 C3.1498000621795654,0 1.9769999980926514,0 0.8041999936103821,0 C0.36010000109672546,0 0,0.36000001430511475 0,0.804099977016449 C0,1.2482000589370728 0.36010000109672546,1.6081000566482544 0.8041999936103821,1.6081000566482544 C1.9769999980926514,1.6081000566482544 3.1498000621795654,1.6081000566482544 4.3225998878479,1.6081000566482544 C4.7667999267578125,1.6081000566482544 5.126800060272217,1.2482000589370728 5.126800060272217,0.804099977016449 C5.126800060272217,0.36000001430511475 4.7667999267578125,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0 C4.3225998878479,0 4.3225998878479,0 4.3225998878479,0"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<ProgressCircle radius={radius} circumference={circumference} offset={offset} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
import React, { useMemo, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { CodeInProgress } from './CodeProgress';
|
||||
import { imageExtRegex } from 'librechat-data-provider';
|
||||
import type { TFile, TAttachment, TAttachmentMetadata } from 'librechat-data-provider';
|
||||
import ProgressText from '~/components/Chat/Messages/Content/ProgressText';
|
||||
import FinishedIcon from '~/components/Chat/Messages/Content/FinishedIcon';
|
||||
import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
||||
import Image from '~/components/Chat/Messages/Content/Image';
|
||||
import LogContent from './LogContent';
|
||||
import { useProgress } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
interface ParsedArgs {
|
||||
lang: string;
|
||||
code: string;
|
||||
}
|
||||
|
||||
export function useParseArgs(args: string): ParsedArgs {
|
||||
return useMemo(() => {
|
||||
const langMatch = args.match(/"lang"\s*:\s*"(\w+)"/);
|
||||
const codeMatch = args.match(/"code"\s*:\s*"(.+?)(?="\s*,\s*"args"|$)/s);
|
||||
|
||||
let code = '';
|
||||
if (codeMatch) {
|
||||
code = codeMatch[1];
|
||||
if (code.endsWith('"}')) {
|
||||
code = code.slice(0, -2);
|
||||
}
|
||||
code = code.replace(/\\n/g, '\n').replace(/\\/g, '');
|
||||
}
|
||||
|
||||
return {
|
||||
lang: langMatch ? langMatch[1] : '',
|
||||
code,
|
||||
};
|
||||
}, [args]);
|
||||
}
|
||||
|
||||
export default function ExecuteCode({
|
||||
initialProgress = 0.1,
|
||||
args,
|
||||
output = '',
|
||||
isSubmitting,
|
||||
attachments,
|
||||
}: {
|
||||
initialProgress: number;
|
||||
args: string;
|
||||
output?: string;
|
||||
isSubmitting: boolean;
|
||||
attachments?: TAttachment[];
|
||||
}) {
|
||||
const showAnalysisCode = useRecoilValue(store.showCode);
|
||||
const [showCode, setShowCode] = useState(showAnalysisCode);
|
||||
|
||||
const { lang, code } = useParseArgs(args);
|
||||
const progress = useProgress(initialProgress);
|
||||
|
||||
const radius = 56.08695652173913;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const offset = circumference - progress * circumference;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-2.5 flex items-center gap-2.5">
|
||||
<div className="relative h-5 w-5 shrink-0">
|
||||
{progress < 1 ? (
|
||||
<CodeInProgress
|
||||
offset={offset}
|
||||
radius={radius}
|
||||
progress={progress}
|
||||
isSubmitting={isSubmitting}
|
||||
circumference={circumference}
|
||||
/>
|
||||
) : (
|
||||
<FinishedIcon />
|
||||
)}
|
||||
</div>
|
||||
<ProgressText
|
||||
progress={progress}
|
||||
onClick={() => setShowCode((prev) => !prev)}
|
||||
inProgressText="Analyzing"
|
||||
finishedText="Finished analyzing"
|
||||
hasInput={!!code.length}
|
||||
/>
|
||||
</div>
|
||||
{showCode && (
|
||||
<div className="code-analyze-block mb-3 mt-0.5 overflow-hidden rounded-xl bg-black">
|
||||
<MarkdownLite content={code ? `\`\`\`${lang}\n${code}\n\`\`\`` : ''} />
|
||||
{output.length > 0 && (
|
||||
<div className="bg-gray-700 p-4 text-xs">
|
||||
<div
|
||||
className="prose flex flex-col-reverse text-white"
|
||||
style={{
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
<pre className="shrink-0">
|
||||
<LogContent output={output} attachments={attachments} />
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{attachments?.map((attachment, index) => {
|
||||
const { width, height, filepath } = attachment as TFile & TAttachmentMetadata;
|
||||
const isImage =
|
||||
imageExtRegex.test(attachment.filename) &&
|
||||
width != null &&
|
||||
height != null &&
|
||||
filepath != null;
|
||||
if (isImage) {
|
||||
return (
|
||||
<Image
|
||||
key={index}
|
||||
altText={attachment.filename}
|
||||
imagePath={filepath}
|
||||
height={height}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { isAfter } from 'date-fns';
|
||||
import React, { useMemo } from 'react';
|
||||
import { imageExtRegex } from 'librechat-data-provider';
|
||||
import type { TAttachment } from 'librechat-data-provider';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import LogLink from './LogLink';
|
||||
|
||||
interface LogContentProps {
|
||||
output?: string;
|
||||
attachments?: TAttachment[];
|
||||
}
|
||||
|
||||
const LogContent: React.FC<LogContentProps> = ({ output = '', attachments }) => {
|
||||
const localize = useLocalize();
|
||||
const processedContent = useMemo(() => {
|
||||
if (!output) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const parts = output.split('Generated files:');
|
||||
return parts[0].trim();
|
||||
}, [output]);
|
||||
|
||||
const nonImageAttachments =
|
||||
attachments?.filter((file) => !imageExtRegex.test(file.filename)) || [];
|
||||
|
||||
const renderAttachment = (file: TAttachment) => {
|
||||
const now = new Date();
|
||||
const expiresAt = typeof file.expiresAt === 'number' ? new Date(file.expiresAt) : null;
|
||||
const isExpired = expiresAt ? isAfter(now, expiresAt) : false;
|
||||
|
||||
if (isExpired) {
|
||||
return `${file.filename} ${localize('com_download_expired')}`;
|
||||
}
|
||||
|
||||
// const expirationText = expiresAt
|
||||
// ? ` ${localize('com_download_expires', format(expiresAt, 'MM/dd/yy HH:mm'))}`
|
||||
// : ` ${localize('com_click_to_download')}`;
|
||||
|
||||
return (
|
||||
<LogLink href={file.filepath} filename={file.filename}>
|
||||
{'- '}
|
||||
{file.filename} {localize('com_click_to_download')}
|
||||
</LogLink>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{processedContent && <div>{processedContent}</div>}
|
||||
{nonImageAttachments.length > 0 && (
|
||||
<div>
|
||||
<p>{localize('com_generated_files')}</p>
|
||||
{nonImageAttachments.map((file, index) => (
|
||||
<React.Fragment key={file.filepath}>
|
||||
{renderAttachment(file)}
|
||||
{index < nonImageAttachments.length - 1 && ', '}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogContent;
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
import React from 'react';
|
||||
import { useCodeOutputDownload } from '~/data-provider';
|
||||
import { useToastContext } from '~/Providers';
|
||||
|
||||
interface LogLinkProps {
|
||||
href: string;
|
||||
filename: string;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const LogLink: React.FC<LogLinkProps> = ({ href, filename, children }) => {
|
||||
const { showToast } = useToastContext();
|
||||
const { refetch: downloadFile } = useCodeOutputDownload(href);
|
||||
|
||||
const handleDownload = async (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const stream = await downloadFile();
|
||||
if (stream.data == null || stream.data === '') {
|
||||
console.error('Error downloading file: No data found');
|
||||
showToast({
|
||||
status: 'error',
|
||||
message: 'Error downloading file',
|
||||
});
|
||||
return;
|
||||
}
|
||||
const link = document.createElement('a');
|
||||
link.href = stream.data;
|
||||
link.setAttribute('download', filename);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
window.URL.revokeObjectURL(stream.data);
|
||||
} catch (error) {
|
||||
console.error('Error downloading file:', error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
onClick={handleDownload}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="!text-blue-400 visited:!text-purple-400 hover:underline"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default LogLink;
|
||||
|
|
@ -8,7 +8,6 @@ import CancelledIcon from './CancelledIcon';
|
|||
import ProgressText from './ProgressText';
|
||||
import FinishedIcon from './FinishedIcon';
|
||||
import ToolPopover from './ToolPopover';
|
||||
// import ActionIcon from './ActionIcon';
|
||||
import WrenchIcon from './WrenchIcon';
|
||||
import { useProgress } from '~/hooks';
|
||||
import { logger } from '~/utils';
|
||||
|
|
@ -32,7 +31,7 @@ export default function ToolCall({
|
|||
const circumference = 2 * Math.PI * radius;
|
||||
const offset = circumference - progress * circumference;
|
||||
|
||||
const [function_name, _domain] = name.split(actionDelimiter);
|
||||
const [function_name, _domain] = name.split(actionDelimiter) as [string, string | undefined];
|
||||
const domain = _domain?.replaceAll(actionDomainSeparator, '.') ?? null;
|
||||
const error = output?.toLowerCase()?.includes('error processing tool');
|
||||
|
||||
|
|
@ -50,50 +49,60 @@ export default function ToolCall({
|
|||
);
|
||||
return '';
|
||||
}
|
||||
}, [_args]);
|
||||
}, [_args]) as string | undefined;
|
||||
|
||||
const hasInfo = useMemo(
|
||||
() => (args?.length || 0) > 0 || (output?.length || 0) > 0,
|
||||
() => (args?.length ?? 0) > 0 || (output?.length ?? 0) > 0,
|
||||
[args, output],
|
||||
);
|
||||
|
||||
const renderIcon = () => {
|
||||
if (progress < 1) {
|
||||
return (
|
||||
<InProgressCall progress={progress} isSubmitting={isSubmitting} error={error}>
|
||||
<div
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center rounded-full bg-transparent text-white"
|
||||
style={{ opacity: 1, transform: 'none' }}
|
||||
data-projection-id="849"
|
||||
>
|
||||
<div>
|
||||
<WrenchIcon />
|
||||
</div>
|
||||
<ProgressCircle radius={radius} circumference={circumference} offset={offset} />
|
||||
</div>
|
||||
</InProgressCall>
|
||||
);
|
||||
}
|
||||
|
||||
return error === true ? <CancelledIcon /> : <FinishedIcon />;
|
||||
};
|
||||
|
||||
const getFinishedText = () => {
|
||||
if (domain != null && domain && domain.length !== Constants.ENCODED_DOMAIN_LENGTH) {
|
||||
return localize('com_assistants_completed_action', domain);
|
||||
}
|
||||
return localize('com_assistants_completed_function', function_name);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover.Root>
|
||||
<div className="my-2.5 flex items-center gap-2.5">
|
||||
<div className="relative h-5 w-5 shrink-0">
|
||||
{progress < 1 ? (
|
||||
<InProgressCall progress={progress} isSubmitting={isSubmitting} error={error}>
|
||||
<div
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center rounded-full bg-transparent text-white"
|
||||
style={{ opacity: 1, transform: 'none' }}
|
||||
data-projection-id="849"
|
||||
>
|
||||
<div>
|
||||
<WrenchIcon />
|
||||
</div>
|
||||
<ProgressCircle radius={radius} circumference={circumference} offset={offset} />
|
||||
</div>
|
||||
</InProgressCall>
|
||||
) : error ? (
|
||||
<CancelledIcon />
|
||||
) : (
|
||||
<FinishedIcon />
|
||||
)}
|
||||
</div>
|
||||
<div className="relative h-5 w-5 shrink-0">{renderIcon()}</div>
|
||||
<ProgressText
|
||||
progress={progress}
|
||||
onClick={() => ({})}
|
||||
inProgressText={localize('com_assistants_running_action')}
|
||||
finishedText={
|
||||
domain && domain.length !== Constants.ENCODED_DOMAIN_LENGTH
|
||||
? localize('com_assistants_completed_action', domain)
|
||||
: localize('com_assistants_completed_function', function_name)
|
||||
}
|
||||
finishedText={getFinishedText()}
|
||||
hasInput={hasInfo}
|
||||
popover={true}
|
||||
/>
|
||||
{hasInfo && (
|
||||
<ToolPopover input={args} output={output} domain={domain} function_name={function_name} />
|
||||
<ToolPopover
|
||||
input={args ?? ''}
|
||||
output={output}
|
||||
domain={domain ?? ''}
|
||||
function_name={function_name}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Root>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type {
|
||||
TAssistantsMap,
|
||||
TConversation,
|
||||
TEndpointsConfig,
|
||||
TPreset,
|
||||
} from 'librechat-data-provider';
|
||||
import { getEndpointField, getIconKey, getIconEndpoint } from '~/utils';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import { getEndpointField, getIconKey, getEntity, getIconEndpoint } from '~/utils';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
|
||||
|
|
@ -14,61 +8,72 @@ export default function ConvoIcon({
|
|||
conversation,
|
||||
endpointsConfig,
|
||||
assistantMap,
|
||||
agentsMap,
|
||||
className = '',
|
||||
containerClassName = '',
|
||||
context,
|
||||
size,
|
||||
}: {
|
||||
conversation: TConversation | TPreset | null;
|
||||
endpointsConfig: TEndpointsConfig;
|
||||
assistantMap: TAssistantsMap | undefined;
|
||||
conversation: t.TConversation | t.TPreset | null;
|
||||
endpointsConfig: t.TEndpointsConfig;
|
||||
assistantMap: t.TAssistantsMap | undefined;
|
||||
agentsMap: t.TAgentsMap | undefined;
|
||||
containerClassName?: string;
|
||||
context?: 'message' | 'nav' | 'landing' | 'menu-item';
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
const iconURL = conversation?.iconURL;
|
||||
const iconURL = conversation?.iconURL ?? '';
|
||||
let endpoint = conversation?.endpoint;
|
||||
endpoint = getIconEndpoint({ endpointsConfig, iconURL, endpoint });
|
||||
const assistant = useMemo(() => {
|
||||
if (!isAssistantsEndpoint(conversation?.endpoint)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const endpointKey = conversation?.endpoint ?? '';
|
||||
const assistantId = conversation?.assistant_id ?? '';
|
||||
const { entity, isAgent } = useMemo(
|
||||
() =>
|
||||
getEntity({
|
||||
endpoint,
|
||||
agentsMap,
|
||||
assistantMap,
|
||||
agent_id: conversation?.agent_id,
|
||||
assistant_id: conversation?.assistant_id,
|
||||
}),
|
||||
[endpoint, conversation?.agent_id, conversation?.assistant_id, agentsMap, assistantMap],
|
||||
);
|
||||
|
||||
return assistantMap?.[endpointKey] ? assistantMap[endpointKey][assistantId] : undefined;
|
||||
}, [conversation?.endpoint, conversation?.assistant_id, assistantMap]);
|
||||
const assistantName = assistant && (assistant.name ?? '');
|
||||
const name = entity?.name ?? '';
|
||||
const avatar = isAgent
|
||||
? (entity as t.Agent | undefined)?.avatar?.filepath
|
||||
: ((entity as t.Assistant | undefined)?.metadata?.avatar as string);
|
||||
|
||||
const avatar = (assistant && (assistant.metadata?.avatar as string)) || '';
|
||||
const endpointIconURL = getEndpointField(endpointsConfig, endpoint, 'iconURL');
|
||||
const iconKey = getIconKey({ endpoint, endpointsConfig, endpointIconURL });
|
||||
const Icon = icons[iconKey];
|
||||
const Icon = icons[iconKey] ?? null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{iconURL && iconURL.includes('http') ? (
|
||||
<ConvoIconURL
|
||||
preset={conversation}
|
||||
endpointIconURL={endpointIconURL}
|
||||
assistantName={assistantName}
|
||||
assistantAvatar={avatar}
|
||||
assistantName={name}
|
||||
agentAvatar={avatar}
|
||||
agentName={name}
|
||||
context={context}
|
||||
/>
|
||||
) : (
|
||||
<div className={containerClassName}>
|
||||
{endpoint &&
|
||||
Icon &&
|
||||
Icon({
|
||||
size,
|
||||
context,
|
||||
className,
|
||||
iconURL: endpointIconURL,
|
||||
assistantName,
|
||||
endpoint,
|
||||
avatar,
|
||||
})}
|
||||
{endpoint && Icon != null && (
|
||||
<Icon
|
||||
size={size}
|
||||
context={context}
|
||||
endpoint={endpoint}
|
||||
className={className}
|
||||
iconURL={endpointIconURL}
|
||||
assistantName={name}
|
||||
agentName={name}
|
||||
avatar={avatar}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
|
|||
</div>
|
||||
);
|
||||
|
||||
return <Icon />;
|
||||
return <Icon context={context} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -73,10 +73,10 @@ const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
|
|||
size={41}
|
||||
context={context}
|
||||
className="h-2/3 w-2/3"
|
||||
agentName={agentName}
|
||||
iconURL={endpointIconURL}
|
||||
assistantName={assistantName}
|
||||
avatar={assistantAvatar || agentAvatar}
|
||||
agentName={agentName}
|
||||
avatar={assistantAvatar ?? agentAvatar}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { EModelEndpoint, isAssistantsEndpoint, alternateName } from 'librechat-data-provider';
|
||||
import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon';
|
||||
import { BrainCircuit } from 'lucide-react';
|
||||
import { Feather } from 'lucide-react';
|
||||
import {
|
||||
Plugin,
|
||||
GPTIcon,
|
||||
|
|
@ -109,7 +109,7 @@ const MessageEndpointIcon: React.FC<IconProps> = (props) => {
|
|||
) : (
|
||||
<div className="h-6 w-6">
|
||||
<div className="shadow-stroke flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<BrainCircuit className="h-2/3 w-2/3 text-gray-400" />
|
||||
<Feather className="h-2/3 w-2/3 text-gray-400" />
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Feather } from 'lucide-react';
|
||||
import { EModelEndpoint, alternateName } from 'librechat-data-provider';
|
||||
import { BrainCircuit } from 'lucide-react';
|
||||
import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon';
|
||||
import {
|
||||
AzureMinimalIcon,
|
||||
OpenAIMinimalIcon,
|
||||
|
|
@ -13,11 +12,12 @@ import {
|
|||
BedrockIcon,
|
||||
Sparkles,
|
||||
} from '~/components/svg';
|
||||
import { cn } from '~/utils';
|
||||
import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon';
|
||||
import { IconProps } from '~/common';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const MinimalIcon: React.FC<IconProps> = (props) => {
|
||||
const { size = 30, iconClassName, error } = props;
|
||||
const { size = 30, iconURL = '', iconClassName, error } = props;
|
||||
|
||||
let endpoint = 'default'; // Default value for endpoint
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ const MinimalIcon: React.FC<IconProps> = (props) => {
|
|||
[EModelEndpoint.assistants]: { icon: <Sparkles className="icon-sm" />, name: 'Assistant' },
|
||||
[EModelEndpoint.azureAssistants]: { icon: <Sparkles className="icon-sm" />, name: 'Assistant' },
|
||||
[EModelEndpoint.agents]: {
|
||||
icon: <BrainCircuit className="icon-sm" />,
|
||||
icon: <Feather className="icon-sm" />,
|
||||
name: props.modelLabel ?? alternateName[EModelEndpoint.agents],
|
||||
},
|
||||
[EModelEndpoint.bedrock]: {
|
||||
|
|
@ -57,21 +57,14 @@ const MinimalIcon: React.FC<IconProps> = (props) => {
|
|||
name: props.modelLabel ?? alternateName[EModelEndpoint.bedrock],
|
||||
},
|
||||
default: {
|
||||
icon: (
|
||||
<UnknownIcon
|
||||
iconURL={props.iconURL}
|
||||
endpoint={endpoint}
|
||||
className="icon-sm"
|
||||
context="nav"
|
||||
/>
|
||||
),
|
||||
icon: <UnknownIcon iconURL={iconURL} endpoint={endpoint} className="icon-sm" context="nav" />,
|
||||
name: endpoint,
|
||||
},
|
||||
};
|
||||
|
||||
let { icon, name } = endpointIcons[endpoint] ?? endpointIcons.default;
|
||||
if (props.iconURL && endpointIcons[props.iconURL]) {
|
||||
({ icon, name } = endpointIcons[props.iconURL]);
|
||||
if (iconURL && endpointIcons[iconURL] != null) {
|
||||
({ icon, name } = endpointIcons[iconURL]);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ const ContentRender = memo(
|
|||
enterEdit={enterEdit}
|
||||
siblingIdx={siblingIdx}
|
||||
setSiblingIdx={setSiblingIdx}
|
||||
attachments={msg.attachments}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { useLocalize } from '~/hooks';
|
|||
import { formatBytes } from '~/utils';
|
||||
|
||||
function Avatar({
|
||||
agent_id,
|
||||
agent_id = '',
|
||||
avatar,
|
||||
createMutation,
|
||||
}: {
|
||||
|
|
@ -31,9 +31,9 @@ function Avatar({
|
|||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [previewUrl, setPreviewUrl] = useState('');
|
||||
const [progress, setProgress] = useState<number>(1);
|
||||
const [input, setInput] = useState<File | null>(null);
|
||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||
const lastSeenCreatedId = useRef<string | null>(null);
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
|
|
@ -54,7 +54,8 @@ function Avatar({
|
|||
}
|
||||
|
||||
setInput(null);
|
||||
setPreviewUrl(data.avatar?.filepath as string | null);
|
||||
const newUrl = data.avatar?.filepath ?? '';
|
||||
setPreviewUrl(newUrl);
|
||||
|
||||
const res = queryClient.getQueryData<AgentListResponse>([
|
||||
QueryKeys.agents,
|
||||
|
|
@ -65,16 +66,15 @@ function Avatar({
|
|||
return;
|
||||
}
|
||||
|
||||
const agents =
|
||||
res.data.map((agent) => {
|
||||
if (agent.id === agent_id) {
|
||||
return {
|
||||
...agent,
|
||||
...data,
|
||||
};
|
||||
}
|
||||
return agent;
|
||||
}) ?? [];
|
||||
const agents = res.data.map((agent) => {
|
||||
if (agent.id === agent_id) {
|
||||
return {
|
||||
...agent,
|
||||
...data,
|
||||
};
|
||||
}
|
||||
return agent;
|
||||
});
|
||||
|
||||
queryClient.setQueryData<AgentListResponse>([QueryKeys.agents, defaultOrderQuery], {
|
||||
...res,
|
||||
|
|
@ -86,7 +86,7 @@ function Avatar({
|
|||
onError: (error) => {
|
||||
console.error('Error:', error);
|
||||
setInput(null);
|
||||
setPreviewUrl(null);
|
||||
setPreviewUrl('');
|
||||
showToast({ message: localize('com_ui_upload_error'), status: 'error' });
|
||||
setProgress(1);
|
||||
},
|
||||
|
|
@ -103,8 +103,10 @@ function Avatar({
|
|||
}, [input]);
|
||||
|
||||
useEffect(() => {
|
||||
if (avatar) {
|
||||
setPreviewUrl((avatar.filepath as string | undefined) ?? null);
|
||||
if (avatar && avatar.filepath) {
|
||||
setPreviewUrl(avatar.filepath);
|
||||
} else {
|
||||
setPreviewUrl('');
|
||||
}
|
||||
}, [avatar]);
|
||||
|
||||
|
|
@ -147,29 +149,31 @@ function Avatar({
|
|||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const file = event.target.files?.[0];
|
||||
const sizeLimit = fileConfig.avatarSizeLimit ?? 0;
|
||||
|
||||
if (fileConfig.avatarSizeLimit && file && file.size <= fileConfig.avatarSizeLimit) {
|
||||
if (sizeLimit && file && file.size <= sizeLimit) {
|
||||
setInput(file);
|
||||
setMenuOpen(false);
|
||||
|
||||
if (!agent_id) {
|
||||
const currentId = agent_id ?? '';
|
||||
if (!currentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file, file.name);
|
||||
formData.append('agent_id', agent_id);
|
||||
formData.append('agent_id', currentId);
|
||||
|
||||
if (typeof avatar === 'object') {
|
||||
formData.append('avatar', JSON.stringify(avatar));
|
||||
}
|
||||
|
||||
uploadAvatar({
|
||||
agent_id,
|
||||
agent_id: currentId,
|
||||
formData,
|
||||
});
|
||||
} else {
|
||||
const megabytes = fileConfig.avatarSizeLimit ? formatBytes(fileConfig.avatarSizeLimit) : 2;
|
||||
const megabytes = sizeLimit ? formatBytes(sizeLimit) : 2;
|
||||
showToast({
|
||||
message: localize('com_ui_upload_invalid_var', megabytes + ''),
|
||||
status: 'error',
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { Controller, useWatch, useFormContext } from 'react-hook-form';
|
||||
import { QueryKeys, Capabilities, EModelEndpoint } from 'librechat-data-provider';
|
||||
import { QueryKeys, AgentCapabilities, EModelEndpoint, SystemRoles } from 'librechat-data-provider';
|
||||
import type { TConfig, TPlugin } from 'librechat-data-provider';
|
||||
import type { AgentForm, AgentPanelProps } from '~/common';
|
||||
import { cn, defaultTextProps, removeFocusOutlines, getEndpointField, getIconKey } from '~/utils';
|
||||
import { useCreateAgentMutation, useUpdateAgentMutation } from '~/data-provider';
|
||||
import { useToastContext, useFileMapContext } from '~/Providers';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
import Action from '~/components/SidePanel/Builder/Action';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { ToolSelectDialog } from '~/components/Tools';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import { useLocalize, useAuthContext } from '~/hooks';
|
||||
import CapabilitiesForm from './CapabilitiesForm';
|
||||
import { processAgentOption } from '~/utils';
|
||||
import { Spinner } from '~/components/svg';
|
||||
import DeleteButton from './DeleteButton';
|
||||
import AgentAvatar from './AgentAvatar';
|
||||
import FileSearch from './FileSearch';
|
||||
import ShareAgent from './ShareAgent';
|
||||
import AgentTool from './AgentTool';
|
||||
import { Panel } from '~/common';
|
||||
|
|
@ -33,6 +36,8 @@ export default function AgentConfig({
|
|||
setActivePanel,
|
||||
setCurrentAgentId,
|
||||
}: AgentPanelProps & { agentsConfig?: TConfig | null }) {
|
||||
const { user } = useAuthContext();
|
||||
const fileMap = useFileMapContext();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const allTools = queryClient.getQueryData<TPlugin[]>([QueryKeys.tools]) ?? [];
|
||||
|
|
@ -51,21 +56,41 @@ export default function AgentConfig({
|
|||
const agent_id = useWatch({ control, name: 'id' });
|
||||
|
||||
const toolsEnabled = useMemo(
|
||||
() => agentsConfig?.capabilities?.includes(Capabilities.tools),
|
||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.tools),
|
||||
[agentsConfig],
|
||||
);
|
||||
const actionsEnabled = useMemo(
|
||||
() => agentsConfig?.capabilities?.includes(Capabilities.actions),
|
||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.actions),
|
||||
[agentsConfig],
|
||||
);
|
||||
// const retrievalEnabled = useMemo(
|
||||
// () => agentsConfig?.capabilities?.includes(Capabilities.retrieval),
|
||||
// [agentsConfig],
|
||||
// );
|
||||
// const codeEnabled = useMemo(
|
||||
// () => agentsConfig?.capabilities?.includes(Capabilities.code_interpreter),
|
||||
// [agentsConfig],
|
||||
// );
|
||||
const fileSearchEnabled = useMemo(
|
||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.file_search) ?? false,
|
||||
[agentsConfig],
|
||||
);
|
||||
const codeEnabled = useMemo(
|
||||
() => agentsConfig?.capabilities?.includes(AgentCapabilities.execute_code) ?? false,
|
||||
[agentsConfig],
|
||||
);
|
||||
|
||||
const knowledge_files = useMemo(() => {
|
||||
if (typeof agent === 'string') {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (agent?.id !== agent_id) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (agent.knowledge_files) {
|
||||
return agent.knowledge_files;
|
||||
}
|
||||
|
||||
const _agent = processAgentOption({
|
||||
agent,
|
||||
fileMap,
|
||||
});
|
||||
return _agent.knowledge_files ?? [];
|
||||
}, [agent, agent_id, fileMap]);
|
||||
|
||||
/* Mutations */
|
||||
const update = useUpdateAgentMutation({
|
||||
|
|
@ -118,8 +143,6 @@ export default function AgentConfig({
|
|||
setActivePanel(Panel.actions);
|
||||
}, [agent_id, setActivePanel, showToast, localize]);
|
||||
|
||||
// Provider Icon logic
|
||||
|
||||
const providerValue = typeof provider === 'string' ? provider : provider?.value;
|
||||
let endpointType: EModelEndpoint | undefined;
|
||||
let endpointIconURL: string | undefined;
|
||||
|
|
@ -280,10 +303,17 @@ export default function AgentConfig({
|
|||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<CapabilitiesForm
|
||||
codeEnabled={codeEnabled}
|
||||
agentsConfig={agentsConfig}
|
||||
retrievalEnabled={false}
|
||||
/>
|
||||
{/* File Search */}
|
||||
{fileSearchEnabled && <FileSearch agent_id={agent_id} files={knowledge_files} />}
|
||||
{/* Agent Tools & Actions */}
|
||||
<div className="mb-6">
|
||||
<label className={labelClass}>
|
||||
{`${toolsEnabled === true ? localize('com_assistants_tools') : ''}
|
||||
{`${toolsEnabled === true ? localize('com_ui_tools') : ''}
|
||||
${toolsEnabled === true && actionsEnabled === true ? ' + ' : ''}
|
||||
${actionsEnabled === true ? localize('com_assistants_actions') : ''}`}
|
||||
</label>
|
||||
|
|
@ -344,11 +374,14 @@ export default function AgentConfig({
|
|||
setCurrentAgentId={setCurrentAgentId}
|
||||
createMutation={create}
|
||||
/>
|
||||
<ShareAgent
|
||||
agent_id={agent_id}
|
||||
agentName={agent?.name ?? ''}
|
||||
projectIds={agent?.projectIds ?? []}
|
||||
/>
|
||||
{(agent?.author === user?.id || user?.role === SystemRoles.ADMIN) && (
|
||||
<ShareAgent
|
||||
agent_id={agent_id}
|
||||
agentName={agent?.name ?? ''}
|
||||
projectIds={agent?.projectIds ?? []}
|
||||
isCollaborative={agent?.isCollaborative}
|
||||
/>
|
||||
)}
|
||||
{/* Submit Button */}
|
||||
<button
|
||||
className="btn btn-primary focus:shadow-outline flex w-full items-center justify-center px-4 py-2 font-semibold text-white hover:bg-green-600 focus:border-green-500"
|
||||
|
|
|
|||
|
|
@ -3,15 +3,20 @@ import { useGetModelsQuery } from 'librechat-data-provider/react-query';
|
|||
import { Controller, useWatch, useForm, FormProvider } from 'react-hook-form';
|
||||
import {
|
||||
Tools,
|
||||
SystemRoles,
|
||||
EModelEndpoint,
|
||||
isAssistantsEndpoint,
|
||||
defaultAgentFormValues,
|
||||
} from 'librechat-data-provider';
|
||||
import type { TConfig } from 'librechat-data-provider';
|
||||
import type { AgentForm, AgentPanelProps, Option } from '~/common';
|
||||
import { useCreateAgentMutation, useUpdateAgentMutation } from '~/data-provider';
|
||||
import { useSelectAgent, useLocalize } from '~/hooks';
|
||||
// import CapabilitiesForm from './CapabilitiesForm';
|
||||
import type { AgentForm, AgentPanelProps, StringOption } from '~/common';
|
||||
import {
|
||||
useCreateAgentMutation,
|
||||
useUpdateAgentMutation,
|
||||
useGetAgentByIdQuery,
|
||||
} from '~/data-provider';
|
||||
import { useSelectAgent, useLocalize, useAuthContext } from '~/hooks';
|
||||
import AgentPanelSkeleton from './AgentPanelSkeleton';
|
||||
import { createProviderOption } from '~/utils';
|
||||
import { useToastContext } from '~/Providers';
|
||||
import AgentConfig from './AgentConfig';
|
||||
|
|
@ -29,11 +34,17 @@ export default function AgentPanel({
|
|||
agentsConfig,
|
||||
endpointsConfig,
|
||||
}: AgentPanelProps & { agentsConfig?: TConfig | null }) {
|
||||
const { onSelect: onSelectAgent } = useSelectAgent();
|
||||
const { showToast } = useToastContext();
|
||||
const localize = useLocalize();
|
||||
const { user } = useAuthContext();
|
||||
const { showToast } = useToastContext();
|
||||
|
||||
const { onSelect: onSelectAgent } = useSelectAgent();
|
||||
|
||||
const modelsQuery = useGetModelsQuery();
|
||||
const agentQuery = useGetAgentByIdQuery(current_agent_id ?? '', {
|
||||
enabled: !!(current_agent_id ?? ''),
|
||||
});
|
||||
|
||||
const models = useMemo(() => modelsQuery.data ?? {}, [modelsQuery.data]);
|
||||
const methods = useForm<AgentForm>({
|
||||
defaultValues: defaultAgentFormValues,
|
||||
|
|
@ -81,7 +92,7 @@ export default function AgentPanel({
|
|||
onSuccess: (data) => {
|
||||
setCurrentAgentId(data.id);
|
||||
showToast({
|
||||
message: `${localize('com_assistants_create_success ')} ${
|
||||
message: `${localize('com_assistants_create_success')} ${
|
||||
data.name ?? localize('com_ui_agent')
|
||||
}`,
|
||||
});
|
||||
|
|
@ -101,23 +112,25 @@ export default function AgentPanel({
|
|||
(data: AgentForm) => {
|
||||
const tools = data.tools ?? [];
|
||||
|
||||
if (data.code_interpreter) {
|
||||
tools.push(Tools.code_interpreter);
|
||||
if (data.execute_code === true) {
|
||||
tools.push(Tools.execute_code);
|
||||
}
|
||||
if (data.retrieval) {
|
||||
if (data.file_search === true) {
|
||||
tools.push(Tools.file_search);
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
model,
|
||||
model_parameters,
|
||||
provider: _provider,
|
||||
description,
|
||||
instructions,
|
||||
model: _model,
|
||||
model_parameters,
|
||||
provider: _provider,
|
||||
} = data;
|
||||
|
||||
const provider = typeof _provider === 'string' ? _provider : (_provider as Option).value;
|
||||
const model = _model ?? '';
|
||||
const provider =
|
||||
(typeof _provider === 'string' ? _provider : (_provider as StringOption).value) ?? '';
|
||||
|
||||
if (agent_id) {
|
||||
update.mutate({
|
||||
|
|
@ -135,6 +148,13 @@ export default function AgentPanel({
|
|||
return;
|
||||
}
|
||||
|
||||
if (!provider || !model) {
|
||||
return showToast({
|
||||
message: localize('com_agents_missing_provider_model'),
|
||||
status: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
create.mutate({
|
||||
name,
|
||||
description,
|
||||
|
|
@ -145,7 +165,7 @@ export default function AgentPanel({
|
|||
model_parameters,
|
||||
});
|
||||
},
|
||||
[agent_id, create, update],
|
||||
[agent_id, create, update, showToast, localize],
|
||||
);
|
||||
|
||||
const handleSelectAgent = useCallback(() => {
|
||||
|
|
@ -154,6 +174,15 @@ export default function AgentPanel({
|
|||
}
|
||||
}, [agent_id, onSelectAgent]);
|
||||
|
||||
if (agentQuery.isInitialLoading) {
|
||||
return <AgentPanelSkeleton />;
|
||||
}
|
||||
|
||||
const canEditAgent =
|
||||
agentQuery.data?.isCollaborative ?? false
|
||||
? true
|
||||
: agentQuery.data?.author === user?.id || user?.role === SystemRoles.ADMIN;
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<form
|
||||
|
|
@ -169,6 +198,7 @@ export default function AgentPanel({
|
|||
<AgentSelect
|
||||
reset={reset}
|
||||
value={field.value}
|
||||
agentQuery={agentQuery}
|
||||
setCurrentAgentId={setCurrentAgentId}
|
||||
selectedAgentId={current_agent_id ?? null}
|
||||
createMutation={create}
|
||||
|
|
@ -188,10 +218,25 @@ export default function AgentPanel({
|
|||
</button>
|
||||
)}
|
||||
</div>
|
||||
{activePanel === Panel.model ? (
|
||||
<ModelPanel setActivePanel={setActivePanel} providers={providers} models={models} />
|
||||
) : null}
|
||||
{activePanel === Panel.builder ? (
|
||||
{!canEditAgent && (
|
||||
<div className="flex h-[30vh] w-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h2 className="text-token-text-primary m-2 text-xl font-semibold">
|
||||
{localize('com_agents_not_available')}
|
||||
</h2>
|
||||
<p className="text-token-text-secondary">{localize('com_agents_no_access')}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{canEditAgent && activePanel === Panel.model && (
|
||||
<ModelPanel
|
||||
setActivePanel={setActivePanel}
|
||||
agent_id={agent_id}
|
||||
providers={providers}
|
||||
models={models}
|
||||
/>
|
||||
)}
|
||||
{canEditAgent && activePanel === Panel.builder && (
|
||||
<AgentConfig
|
||||
actions={actions}
|
||||
setAction={setAction}
|
||||
|
|
@ -200,7 +245,7 @@ export default function AgentPanel({
|
|||
endpointsConfig={endpointsConfig}
|
||||
setCurrentAgentId={setCurrentAgentId}
|
||||
/>
|
||||
) : null}
|
||||
)}
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
import React from 'react';
|
||||
import { Skeleton } from '~/components/ui';
|
||||
|
||||
export default function AgentPanelSkeleton() {
|
||||
return (
|
||||
<div className="scrollbar-gutter-stable h-auto w-full flex-shrink-0 overflow-x-hidden">
|
||||
{/* Agent Select and Button */}
|
||||
<div className="mt-1 flex w-full gap-2">
|
||||
<Skeleton className="h-[40px] w-3/4 rounded" />
|
||||
<Skeleton className="h-[40px] w-1/4 rounded" />
|
||||
</div>
|
||||
|
||||
<div className="h-auto bg-white px-4 pb-8 pt-3 dark:bg-transparent">
|
||||
{/* Avatar */}
|
||||
<div className="mb-4">
|
||||
<div className="flex w-full items-center justify-center gap-4">
|
||||
<Skeleton className="relative h-20 w-20 rounded-full" />
|
||||
</div>
|
||||
{/* Name */}
|
||||
<Skeleton className="mb-2 h-5 w-1/5 rounded" />
|
||||
<Skeleton className="mb-1 h-[40px] w-full rounded" />
|
||||
<Skeleton className="h-3 w-1/4 rounded" />
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="mb-4">
|
||||
<Skeleton className="mb-2 h-5 w-1/4 rounded" />
|
||||
<Skeleton className="h-[40px] w-full rounded" />
|
||||
</div>
|
||||
|
||||
{/* Instructions */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="mb-2 h-5 w-1/4 rounded" />
|
||||
<Skeleton className="h-[100px] w-full rounded" />
|
||||
</div>
|
||||
|
||||
{/* Model and Provider */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="mb-2 h-5 w-1/4 rounded" />
|
||||
<Skeleton className="h-[40px] w-full rounded" />
|
||||
</div>
|
||||
|
||||
{/* Capabilities */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="mb-2 h-5 w-1/4 rounded" />
|
||||
<Skeleton className="mb-2 h-[40px] w-full rounded" />
|
||||
<Skeleton className="h-[40px] w-full rounded" />
|
||||
</div>
|
||||
|
||||
{/* Tools & Actions */}
|
||||
<div className="mb-6">
|
||||
<Skeleton className="mb-2 h-5 w-1/4 rounded" />
|
||||
<Skeleton className="mb-2 h-[40px] w-full rounded" />
|
||||
<Skeleton className="mb-2 h-[40px] w-full rounded" />
|
||||
<div className="flex space-x-2">
|
||||
<Skeleton className="h-8 w-1/2 rounded" />
|
||||
<Skeleton className="h-8 w-1/2 rounded" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Buttons */}
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Skeleton className="h-[40px] w-[100px] rounded" />
|
||||
<Skeleton className="h-[40px] w-[100px] rounded" />
|
||||
<Skeleton className="h-[40px] w-[100px] rounded" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { ActionsEndpoint } from '~/common';
|
||||
import type { Action, TConfig, TEndpointsConfig } from 'librechat-data-provider';
|
||||
|
|
@ -18,19 +18,14 @@ export default function AgentPanelSwitch() {
|
|||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
|
||||
const agentsConfig = useMemo(
|
||||
() =>
|
||||
// endpointsConfig?.[EModelEndpoint.agents] ??
|
||||
({
|
||||
// for testing purposes
|
||||
capabilities: [Capabilities.tools, Capabilities.actions],
|
||||
} as TConfig),
|
||||
// [endpointsConfig]);
|
||||
[],
|
||||
() => endpointsConfig?.[EModelEndpoint.agents] ?? ({} as TConfig | null),
|
||||
[endpointsConfig],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (conversation?.agent_id) {
|
||||
setCurrentAgentId(conversation?.agent_id);
|
||||
const agent_id = conversation?.agent_id ?? '';
|
||||
if (agent_id) {
|
||||
setCurrentAgentId(agent_id);
|
||||
}
|
||||
}, [conversation?.agent_id]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import { Plus, EarthIcon } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||
import { Capabilities, defaultAgentFormValues } from 'librechat-data-provider';
|
||||
import { AgentCapabilities, defaultAgentFormValues } from 'librechat-data-provider';
|
||||
import type { UseMutationResult, QueryObserverResult } from '@tanstack/react-query';
|
||||
import type { Agent, AgentCreateParams } from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type { UseFormReset } from 'react-hook-form';
|
||||
import type { AgentCapabilities, AgentForm, TAgentOption } from '~/common';
|
||||
import type { TAgentCapabilities, AgentForm, TAgentOption } from '~/common';
|
||||
import { cn, createDropdownSetter, createProviderOption, processAgentOption } from '~/utils';
|
||||
import { useListAgentsQuery, useGetAgentByIdQuery } from '~/data-provider';
|
||||
import SelectDropDown from '~/components/ui/SelectDropDown';
|
||||
// import { useFileMapContext } from '~/Providers';
|
||||
import { useListAgentsQuery } from '~/data-provider';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
const keys = new Set(Object.keys(defaultAgentFormValues));
|
||||
|
||||
export default function AgentSelect({
|
||||
reset,
|
||||
agentQuery,
|
||||
value: currentAgentValue,
|
||||
selectedAgentId = null,
|
||||
setCurrentAgentId,
|
||||
|
|
@ -24,12 +24,11 @@ export default function AgentSelect({
|
|||
reset: UseFormReset<AgentForm>;
|
||||
value?: TAgentOption;
|
||||
selectedAgentId: string | null;
|
||||
agentQuery: QueryObserverResult<Agent>;
|
||||
setCurrentAgentId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
createMutation: UseMutationResult<Agent, Error, AgentCreateParams>;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
// TODO: file handling for agents
|
||||
// const fileMap = useFileMapContext();
|
||||
const lastSelectedAgent = useRef<string | null>(null);
|
||||
|
||||
const { data: startupConfig } = useGetStartupConfig();
|
||||
|
|
@ -39,15 +38,10 @@ export default function AgentSelect({
|
|||
processAgentOption({
|
||||
agent,
|
||||
instanceProjectId: startupConfig?.instanceProjectId,
|
||||
/* fileMap */
|
||||
}),
|
||||
),
|
||||
});
|
||||
|
||||
const agentQuery = useGetAgentByIdQuery(selectedAgentId ?? '', {
|
||||
enabled: !!(selectedAgentId ?? ''),
|
||||
});
|
||||
|
||||
const resetAgentForm = useCallback(
|
||||
(fullAgent: Agent) => {
|
||||
const { instanceProjectId } = startupConfig ?? {};
|
||||
|
|
@ -61,17 +55,26 @@ export default function AgentSelect({
|
|||
icon: isGlobal ? <EarthIcon className={'icon-lg text-green-400'} /> : null,
|
||||
};
|
||||
|
||||
const actions: AgentCapabilities = {
|
||||
[Capabilities.code_interpreter]: false,
|
||||
[Capabilities.image_vision]: false,
|
||||
[Capabilities.retrieval]: false,
|
||||
const capabilities: TAgentCapabilities = {
|
||||
[AgentCapabilities.execute_code]: false,
|
||||
[AgentCapabilities.file_search]: false,
|
||||
};
|
||||
|
||||
const formValues: Partial<AgentForm & AgentCapabilities> = {
|
||||
...actions,
|
||||
const agentTools: string[] = [];
|
||||
(fullAgent.tools ?? []).forEach((tool) => {
|
||||
if (capabilities[tool] !== undefined) {
|
||||
capabilities[tool] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
agentTools.push(tool);
|
||||
});
|
||||
|
||||
const formValues: Partial<AgentForm & TAgentCapabilities> = {
|
||||
...capabilities,
|
||||
agent: update,
|
||||
model: update.model,
|
||||
tools: update.tools ?? [],
|
||||
tools: agentTools,
|
||||
};
|
||||
|
||||
Object.entries(fullAgent).forEach(([name, value]) => {
|
||||
|
|
@ -91,7 +94,7 @@ export default function AgentSelect({
|
|||
|
||||
reset(formValues);
|
||||
},
|
||||
[reset],
|
||||
[reset, startupConfig],
|
||||
);
|
||||
|
||||
const onSelect = useCallback(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useMemo } from 'react';
|
||||
// import { Capabilities } from 'librechat-data-provider';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
// import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import type { TConfig } from 'librechat-data-provider';
|
||||
import type { AgentForm } from '~/common';
|
||||
// import type { AgentForm } from '~/common';
|
||||
// import ImageVision from './ImageVision';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import Retrieval from './Retrieval';
|
||||
import CodeFiles from './CodeFiles';
|
||||
// import CodeFiles from './CodeFiles';
|
||||
import Code from './Code';
|
||||
|
||||
export default function CapabilitiesForm({
|
||||
|
|
@ -20,25 +20,21 @@ export default function CapabilitiesForm({
|
|||
}) {
|
||||
const localize = useLocalize();
|
||||
|
||||
const methods = useFormContext<AgentForm>();
|
||||
const { control } = methods;
|
||||
const agent = useWatch({ control, name: 'agent' });
|
||||
const agent_id = useWatch({ control, name: 'id' });
|
||||
const files = useMemo(() => {
|
||||
if (typeof agent === 'string') {
|
||||
return [];
|
||||
}
|
||||
return agent?.code_files;
|
||||
}, [agent]);
|
||||
// const methods = useFormContext<AgentForm>();
|
||||
// const { control } = methods;
|
||||
// const agent = useWatch({ control, name: 'agent' });
|
||||
// const agent_id = useWatch({ control, name: 'id' });
|
||||
// const files = useMemo(() => {
|
||||
// if (typeof agent === 'string') {
|
||||
// return [];
|
||||
// }
|
||||
// return agent?.code_files;
|
||||
// }, [agent]);
|
||||
|
||||
const retrievalModels = useMemo(
|
||||
() => new Set(agentsConfig?.retrievalModels ?? []),
|
||||
[agentsConfig],
|
||||
);
|
||||
// const imageVisionEnabled = useMemo(
|
||||
// () => agentsConfig?.capabilities?.includes(Capabilities.image_vision),
|
||||
// [agentsConfig],
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className="mb-4">
|
||||
|
|
@ -50,10 +46,10 @@ export default function CapabilitiesForm({
|
|||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
{codeEnabled && <Code />}
|
||||
{retrievalEnabled && <Retrieval retrievalModels={retrievalModels} />}
|
||||
{codeEnabled === true && <Code />}
|
||||
{retrievalEnabled === true && <Retrieval retrievalModels={retrievalModels} />}
|
||||
{/* {imageVisionEnabled && version == 1 && <ImageVision />} */}
|
||||
{codeEnabled && <CodeFiles agent_id={agent_id} files={files} />}
|
||||
{/* {codeEnabled && <CodeFiles agent_id={agent_id} files={files} />} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Capabilities } from 'librechat-data-provider';
|
||||
import { AgentCapabilities } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import type { AgentForm } from '~/common';
|
||||
import {
|
||||
|
|
@ -22,7 +22,7 @@ export default function Code() {
|
|||
<HoverCard openDelay={50}>
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.code_interpreter}
|
||||
name={AgentCapabilities.execute_code}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
|
|
@ -30,30 +30,34 @@ export default function Code() {
|
|||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
value={field.value.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center space-x-2">
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() =>
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
setValue(AgentCapabilities.execute_code, !getValues(AgentCapabilities.execute_code), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.code_interpreter}
|
||||
onClick={() =>
|
||||
setValue(Capabilities.code_interpreter, !getValues(Capabilities.code_interpreter), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
htmlFor={AgentCapabilities.execute_code}
|
||||
>
|
||||
{localize('com_assistants_code_interpreter')}
|
||||
{localize('com_agents_execute_code')}
|
||||
</label>
|
||||
<HoverCardTrigger>
|
||||
<CircleHelpIcon className="h-5 w-5 text-gray-500" />
|
||||
</HoverCardTrigger>
|
||||
</div>
|
||||
</button>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={ESide.Top} className="w-80">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
||||
<p className="text-sm text-text-secondary">
|
||||
{/* // TODO: add a Code Interpreter description */}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
|||
120
client/src/components/SidePanel/Agents/FileSearch.tsx
Normal file
120
client/src/components/SidePanel/Agents/FileSearch.tsx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
import { useState, useRef, useEffect } from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import {
|
||||
EModelEndpoint,
|
||||
EToolResources,
|
||||
mergeFileConfig,
|
||||
AgentCapabilities,
|
||||
retrievalMimeTypes,
|
||||
fileConfig as defaultFileConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import type { ExtendedFile, AgentForm } from '~/common';
|
||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||
import FileSearchCheckbox from './FileSearchCheckbox';
|
||||
import { useGetFileConfig } from '~/data-provider';
|
||||
import { AttachmentIcon } from '~/components/svg';
|
||||
import { useFileHandling } from '~/hooks/Files';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { useChatContext } from '~/Providers';
|
||||
|
||||
export default function FileSearch({
|
||||
agent_id,
|
||||
files: _files,
|
||||
}: {
|
||||
agent_id: string;
|
||||
files?: [string, ExtendedFile][];
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { setFilesLoading } = useChatContext();
|
||||
const { watch } = useFormContext<AgentForm>();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [files, setFiles] = useState<Map<string, ExtendedFile>>(new Map());
|
||||
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
});
|
||||
|
||||
const { handleFileChange } = useFileHandling({
|
||||
overrideEndpoint: EModelEndpoint.agents,
|
||||
additionalMetadata: { agent_id, tool_resource: EToolResources.file_search },
|
||||
fileSetter: setFiles,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (_files) {
|
||||
setFiles(new Map(_files));
|
||||
}
|
||||
}, [_files]);
|
||||
|
||||
const fileSearchChecked = watch(AgentCapabilities.file_search);
|
||||
|
||||
const endpointFileConfig = fileConfig.endpoints[EModelEndpoint.agents];
|
||||
const disabled = endpointFileConfig.disabled ?? false;
|
||||
|
||||
if (disabled === true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleButtonClick = () => {
|
||||
// necessary to reset the input
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="mb-1.5 flex items-center gap-2">
|
||||
<span>
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_assistants_file_search')}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<FileSearchCheckbox />
|
||||
<div className="flex flex-col gap-2">
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!agent_id || fileSearchChecked === false}
|
||||
className="btn btn-neutral border-token-border-light relative h-8 rounded-lg font-medium"
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-1">
|
||||
<AttachmentIcon className="text-token-text-primary h-4 w-4" />
|
||||
<input
|
||||
multiple={true}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
ref={fileInputRef}
|
||||
disabled={!agent_id || fileSearchChecked === false}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
{localize('com_ui_upload_files')}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
{/* Disabled Message */}
|
||||
{agent_id ? null : (
|
||||
<div className="text-sm text-text-secondary">
|
||||
{localize('com_agents_file_search_disabled')}
|
||||
</div>
|
||||
)}
|
||||
{/* Knowledge Files */}
|
||||
<FileRow
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
setFilesLoading={setFilesLoading}
|
||||
agent_id={agent_id}
|
||||
tool_resource={EToolResources.file_search}
|
||||
fileFilter={(file: ExtendedFile) =>
|
||||
retrievalMimeTypes.some((regex) => regex.test(file.type ?? ''))
|
||||
}
|
||||
Wrapper={({ children }) => <div className="flex flex-wrap gap-2">{children}</div>}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import { AgentCapabilities } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import type { AgentForm } from '~/common';
|
||||
import {
|
||||
Checkbox,
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardPortal,
|
||||
HoverCardTrigger,
|
||||
} from '~/components/ui';
|
||||
import { CircleHelpIcon } from '~/components/svg';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
export default function FileSearchCheckbox() {
|
||||
const localize = useLocalize();
|
||||
const methods = useFormContext<AgentForm>();
|
||||
const { control, setValue, getValues } = methods;
|
||||
|
||||
return (
|
||||
<>
|
||||
<HoverCard openDelay={50}>
|
||||
<div className="my-2 flex items-center">
|
||||
<Controller
|
||||
name={AgentCapabilities.file_search}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field.value.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() =>
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
setValue(AgentCapabilities.file_search, !getValues(AgentCapabilities.file_search), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={AgentCapabilities.file_search}
|
||||
>
|
||||
{localize('com_agents_enable_file_search')}
|
||||
</label>
|
||||
<HoverCardTrigger>
|
||||
<CircleHelpIcon className="h-5 w-5 text-gray-500" />
|
||||
</HoverCardTrigger>
|
||||
</button>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={ESide.Top} className="w-80">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm text-text-secondary">
|
||||
{localize('com_agents_file_search_info')}
|
||||
</p>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
</div>
|
||||
</HoverCard>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,13 +1,18 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import type { AgentForm, AgentModelPanelProps } from '~/common';
|
||||
import { SelectDropDown, ModelParameters } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils';
|
||||
import { getSettingsKeys } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { AgentForm, AgentModelPanelProps, StringOption } from '~/common';
|
||||
import { componentMapping } from '~/components/SidePanel/Parameters/components';
|
||||
import { agentSettings } from '~/components/SidePanel/Parameters/settings';
|
||||
import { getEndpointField, cn, cardStyle } from '~/utils';
|
||||
import { SelectDropDown } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { Panel } from '~/common';
|
||||
|
||||
export default function ModelPanel({
|
||||
export default function Parameters({
|
||||
setActivePanel,
|
||||
providers,
|
||||
models: modelsData,
|
||||
|
|
@ -15,30 +20,56 @@ export default function ModelPanel({
|
|||
const localize = useLocalize();
|
||||
|
||||
const { control, setValue, watch } = useFormContext<AgentForm>();
|
||||
const model = watch('model');
|
||||
const modelParameters = watch('model_parameters');
|
||||
const providerOption = watch('provider');
|
||||
const model = watch('model');
|
||||
|
||||
const provider = useMemo(() => {
|
||||
if (!providerOption) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return typeof providerOption === 'string' ? providerOption : providerOption.value;
|
||||
const value =
|
||||
typeof providerOption === 'string'
|
||||
? providerOption
|
||||
: (providerOption as StringOption | undefined)?.value;
|
||||
return value ?? '';
|
||||
}, [providerOption]);
|
||||
const models = useMemo(() => (provider ? modelsData[provider] : []), [modelsData, provider]);
|
||||
|
||||
useEffect(() => {
|
||||
if (provider && model) {
|
||||
const modelExists = models.includes(model);
|
||||
const _model = model ?? '';
|
||||
if (provider && _model) {
|
||||
const modelExists = models.includes(_model);
|
||||
if (!modelExists) {
|
||||
const newModels = modelsData[provider];
|
||||
setValue('model', newModels[0] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
if (provider && !_model) {
|
||||
setValue('model', models[0] ?? '');
|
||||
}
|
||||
}, [provider, models, modelsData, setValue, model]);
|
||||
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
|
||||
const bedrockRegions = useMemo(() => {
|
||||
return endpointsConfig?.[provider]?.availableRegions ?? [];
|
||||
}, [endpointsConfig, provider]);
|
||||
|
||||
const endpointType = useMemo(
|
||||
() => getEndpointField(endpointsConfig, provider, 'type'),
|
||||
[provider, endpointsConfig],
|
||||
);
|
||||
|
||||
const parameters = useMemo(() => {
|
||||
const [combinedKey, endpointKey] = getSettingsKeys(endpointType ?? provider, model ?? '');
|
||||
return agentSettings[combinedKey] ?? agentSettings[endpointKey];
|
||||
}, [endpointType, model, provider]);
|
||||
|
||||
const setOption = (optionKey: keyof t.AgentModelParameters) => (value: t.AgentParameterValue) => {
|
||||
setValue(`model_parameters.${optionKey}`, value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-auto px-2 pb-12 text-sm">
|
||||
<div className="scrollbar-gutter-stable h-full min-h-[50vh] overflow-auto pb-12 text-sm">
|
||||
<div className="model-panel relative flex flex-col items-center px-16 py-6 text-center">
|
||||
<div className="absolute left-0 top-6">
|
||||
<button
|
||||
|
|
@ -56,228 +87,125 @@ export default function ModelPanel({
|
|||
|
||||
<div className="mb-2 mt-2 text-xl font-medium">{localize('com_ui_model_parameters')}</div>
|
||||
</div>
|
||||
{/* Endpoint aka Provider for Agents */}
|
||||
<div className="mb-4">
|
||||
<label
|
||||
className="text-token-text-primary model-panel-label mb-2 block font-medium"
|
||||
htmlFor="provider"
|
||||
>
|
||||
{localize('com_ui_provider')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Controller
|
||||
name="provider"
|
||||
control={control}
|
||||
rules={{ required: true, minLength: 1 }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
value={field.value ?? ''}
|
||||
placeholder={localize('com_ui_select_provider')}
|
||||
setValue={field.onChange}
|
||||
availableValues={providers}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4 hover:cursor-pointer',
|
||||
!field.value && 'border-2 border-yellow-400',
|
||||
<div className="p-2">
|
||||
{/* Endpoint aka Provider for Agents */}
|
||||
<div className="mb-4">
|
||||
<label
|
||||
className="text-token-text-primary model-panel-label mb-2 block font-medium"
|
||||
htmlFor="provider"
|
||||
>
|
||||
{localize('com_ui_provider')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Controller
|
||||
name="provider"
|
||||
control={control}
|
||||
rules={{ required: true, minLength: 1 }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
value={field.value ?? ''}
|
||||
placeholder={localize('com_ui_select_provider')}
|
||||
setValue={field.onChange}
|
||||
availableValues={providers}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4 hover:cursor-pointer',
|
||||
(field.value === undefined || field.value === '') &&
|
||||
'border-2 border-yellow-400',
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{error && (
|
||||
<span className="model-panel-error text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{error && (
|
||||
<span className="model-panel-error text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Model */}
|
||||
<div className="model-panel-section mb-6">
|
||||
<label
|
||||
className={cn(
|
||||
'text-token-text-primary model-panel-label mb-2 block font-medium',
|
||||
!provider && 'text-gray-500 dark:text-gray-400',
|
||||
)}
|
||||
htmlFor="model"
|
||||
>
|
||||
{localize('com_ui_model')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Controller
|
||||
name="model"
|
||||
control={control}
|
||||
rules={{ required: true, minLength: 1 }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
placeholder={
|
||||
provider
|
||||
? localize('com_ui_select_model')
|
||||
: localize('com_ui_select_provider_first')
|
||||
}
|
||||
value={field.value}
|
||||
setValue={field.onChange}
|
||||
availableValues={models}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
disabled={!provider}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4',
|
||||
!provider ? 'cursor-not-allowed bg-gray-200' : 'hover:cursor-pointer',
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Model */}
|
||||
<div className="model-panel-section mb-4">
|
||||
<label
|
||||
className={cn(
|
||||
'text-token-text-primary model-panel-label mb-2 block font-medium',
|
||||
!provider && 'text-gray-500 dark:text-gray-400',
|
||||
)}
|
||||
htmlFor="model"
|
||||
>
|
||||
{localize('com_ui_model')} <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Controller
|
||||
name="model"
|
||||
control={control}
|
||||
rules={{ required: true, minLength: 1 }}
|
||||
render={({ field, fieldState: { error } }) => (
|
||||
<>
|
||||
<SelectDropDown
|
||||
emptyTitle={true}
|
||||
placeholder={
|
||||
provider
|
||||
? localize('com_ui_select_model')
|
||||
: localize('com_ui_select_provider_first')
|
||||
}
|
||||
value={field.value}
|
||||
setValue={field.onChange}
|
||||
availableValues={models}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
disabled={!provider}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'flex h-[40px] w-full flex-none items-center justify-center border-none px-4',
|
||||
!provider ? 'cursor-not-allowed bg-gray-200' : 'hover:cursor-pointer',
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{provider && error && (
|
||||
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
containerClassName={cn('rounded-md', error ? 'border-red-500 border-2' : '')}
|
||||
/>
|
||||
{provider && error && (
|
||||
<span className="text-sm text-red-500 transition duration-300 ease-in-out">
|
||||
{localize('com_ui_field_required')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.temperature"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_temperature"
|
||||
ariaLabel="Temperature"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 1}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.max_context_tokens"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_max_output_tokens"
|
||||
ariaLabel="Max Context Tokens"
|
||||
min={0}
|
||||
max={4096}
|
||||
step={1}
|
||||
stepClick={1}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.max_output_tokens"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_context_tokens"
|
||||
ariaLabel="Max Context Tokens"
|
||||
min={0}
|
||||
max={4096}
|
||||
step={1}
|
||||
stepClick={1}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.top_p"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_top_p"
|
||||
ariaLabel="Top P"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 1}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.frequency_penalty"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_frequency_penalty"
|
||||
ariaLabel="Frequency Penalty"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<Controller
|
||||
name="model_parameters.presence_penalty"
|
||||
control={control}
|
||||
rules={{ required: false }}
|
||||
render={({ field }) => (
|
||||
<>
|
||||
<ModelParameters
|
||||
label="com_endpoint_presence_penalty"
|
||||
ariaLabel="Presence Penalty"
|
||||
min={-2}
|
||||
max={2}
|
||||
step={0.01}
|
||||
stepClick={0.01}
|
||||
initialValue={field.value ?? 0}
|
||||
onChange={field.onChange}
|
||||
showButtons={true}
|
||||
disabled={!provider}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* Model Parameters */}
|
||||
{parameters && (
|
||||
<div className="h-auto max-w-full overflow-x-hidden p-2">
|
||||
<div className="grid grid-cols-4 gap-6">
|
||||
{' '}
|
||||
{/* This is the parent element containing all settings */}
|
||||
{/* Below is an example of an applied dynamic setting, each be contained by a div with the column span specified */}
|
||||
{parameters.map((setting) => {
|
||||
const Component = componentMapping[setting.component];
|
||||
if (!Component) {
|
||||
return null;
|
||||
}
|
||||
const { key, default: defaultValue, ...rest } = setting;
|
||||
|
||||
if (key === 'region' && bedrockRegions.length) {
|
||||
rest.options = bedrockRegions;
|
||||
}
|
||||
|
||||
return (
|
||||
<Component
|
||||
key={key}
|
||||
settingKey={key}
|
||||
defaultValue={defaultValue}
|
||||
{...rest}
|
||||
setOption={setOption as t.TSetOption}
|
||||
conversation={modelParameters as Partial<t.TConversation>}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,16 +19,19 @@ import { useLocalize } from '~/hooks';
|
|||
|
||||
type FormValues = {
|
||||
[Permissions.SHARED_GLOBAL]: boolean;
|
||||
[Permissions.UPDATE]: boolean;
|
||||
};
|
||||
|
||||
export default function ShareAgent({
|
||||
agent_id = '',
|
||||
agentName,
|
||||
projectIds = [],
|
||||
isCollaborative = false,
|
||||
}: {
|
||||
agent_id?: string;
|
||||
agentName?: string;
|
||||
projectIds?: string[];
|
||||
isCollaborative?: boolean;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
|
|
@ -40,6 +43,7 @@ export default function ShareAgent({
|
|||
);
|
||||
|
||||
const {
|
||||
watch,
|
||||
control,
|
||||
setValue,
|
||||
getValues,
|
||||
|
|
@ -49,12 +53,22 @@ export default function ShareAgent({
|
|||
mode: 'onChange',
|
||||
defaultValues: {
|
||||
[Permissions.SHARED_GLOBAL]: agentIsGlobal,
|
||||
[Permissions.UPDATE]: isCollaborative,
|
||||
},
|
||||
});
|
||||
|
||||
const sharedGlobalValue = watch(Permissions.SHARED_GLOBAL);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sharedGlobalValue) {
|
||||
setValue(Permissions.UPDATE, false);
|
||||
}
|
||||
}, [sharedGlobalValue, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(Permissions.SHARED_GLOBAL, agentIsGlobal);
|
||||
}, [agentIsGlobal, setValue]);
|
||||
setValue(Permissions.UPDATE, isCollaborative);
|
||||
}, [agentIsGlobal, isCollaborative, setValue]);
|
||||
|
||||
const updateAgent = useUpdateAgentMutation({
|
||||
onSuccess: (data) => {
|
||||
|
|
@ -87,16 +101,30 @@ export default function ShareAgent({
|
|||
|
||||
const payload = {} as AgentUpdateParams;
|
||||
|
||||
if (data[Permissions.SHARED_GLOBAL]) {
|
||||
payload.projectIds = [startupConfig.instanceProjectId];
|
||||
} else {
|
||||
payload.removeProjectIds = [startupConfig.instanceProjectId];
|
||||
if (data[Permissions.UPDATE] !== isCollaborative) {
|
||||
payload.isCollaborative = data[Permissions.UPDATE];
|
||||
}
|
||||
|
||||
updateAgent.mutate({
|
||||
agent_id,
|
||||
data: payload,
|
||||
});
|
||||
if (data[Permissions.SHARED_GLOBAL] !== agentIsGlobal) {
|
||||
if (data[Permissions.SHARED_GLOBAL]) {
|
||||
payload.projectIds = [startupConfig.instanceProjectId];
|
||||
} else {
|
||||
payload.removeProjectIds = [startupConfig.instanceProjectId];
|
||||
payload.isCollaborative = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(payload).length > 0) {
|
||||
updateAgent.mutate({
|
||||
agent_id,
|
||||
data: payload,
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
message: localize('com_ui_no_changes'),
|
||||
status: 'info',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -113,12 +141,12 @@ export default function ShareAgent({
|
|||
)}
|
||||
type="button"
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2 text-blue-500">
|
||||
<div className="flex items-center justify-center gap-2 text-blue-500">
|
||||
<Share2Icon className="icon-md h-4 w-4" />
|
||||
</div>
|
||||
</button>
|
||||
</OGDialogTrigger>
|
||||
<OGDialogContent className="border-border-light bg-surface-primary-alt text-text-secondary">
|
||||
<OGDialogContent className="w-1/4 border-border-light bg-surface-primary-alt text-text-secondary">
|
||||
<OGDialogTitle>
|
||||
{localize(
|
||||
'com_ui_share_var',
|
||||
|
|
@ -133,11 +161,12 @@ export default function ShareAgent({
|
|||
handleSubmit(onSubmit)(e);
|
||||
}}
|
||||
>
|
||||
<div className="mb-4 flex items-center justify-between gap-2 py-4">
|
||||
<div className="flex items-center justify-between gap-2 py-2">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
className="mr-2 cursor-pointer"
|
||||
disabled={isFetching || updateAgent.isLoading || !instanceProjectId}
|
||||
onClick={() =>
|
||||
setValue(Permissions.SHARED_GLOBAL, !getValues(Permissions.SHARED_GLOBAL), {
|
||||
shouldDirty: true,
|
||||
|
|
@ -166,18 +195,54 @@ export default function ShareAgent({
|
|||
name={Permissions.SHARED_GLOBAL}
|
||||
control={control}
|
||||
disabled={isFetching || updateAgent.isLoading || !instanceProjectId}
|
||||
rules={{
|
||||
validate: (value) => {
|
||||
const isValid = !(value && agentIsGlobal);
|
||||
if (!isValid) {
|
||||
showToast({
|
||||
message: localize('com_ui_agent_already_shared_to_all'),
|
||||
status: 'warning',
|
||||
render={({ field }) => (
|
||||
<Switch
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
value={field.value.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4 flex items-center justify-between gap-2 py-2">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
className="mr-2 cursor-pointer"
|
||||
disabled={
|
||||
isFetching || updateAgent.isLoading || !instanceProjectId || !sharedGlobalValue
|
||||
}
|
||||
onClick={() =>
|
||||
setValue(Permissions.UPDATE, !getValues(Permissions.UPDATE), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
setValue(Permissions.UPDATE, !getValues(Permissions.UPDATE), {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}
|
||||
return isValid;
|
||||
},
|
||||
}}
|
||||
}}
|
||||
aria-checked={getValues(Permissions.UPDATE)}
|
||||
role="checkbox"
|
||||
>
|
||||
{localize('com_agents_allow_editing')}
|
||||
</button>
|
||||
{/* <label htmlFor={Permissions.UPDATE} className="select-none">
|
||||
{agentIsGlobal && (
|
||||
<span className="ml-2 text-xs">{localize('com_ui_agent_editing_allowed')}</span>
|
||||
)}
|
||||
</label> */}
|
||||
</div>
|
||||
<Controller
|
||||
name={Permissions.UPDATE}
|
||||
control={control}
|
||||
disabled={
|
||||
isFetching || updateAgent.isLoading || !instanceProjectId || !sharedGlobalValue
|
||||
}
|
||||
render={({ field }) => (
|
||||
<Switch
|
||||
{...field}
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ export default function AssistantPanel({
|
|||
{/* Tools */}
|
||||
<div className="mb-6">
|
||||
<label className={labelClass}>
|
||||
{`${toolsEnabled === true ? localize('com_assistants_tools') : ''}
|
||||
{`${toolsEnabled === true ? localize('com_ui_tools') : ''}
|
||||
${toolsEnabled === true && actionsEnabled === true ? ' + ' : ''}
|
||||
${actionsEnabled === true ? localize('com_assistants_actions') : ''}`}
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,9 @@ import { useState } from 'react';
|
|||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import type { NavLink, NavProps } from '~/common';
|
||||
import { Accordion, AccordionItem, AccordionContent } from '~/components/ui/Accordion';
|
||||
import { buttonVariants } from '~/components/ui/Button';
|
||||
import { TooltipAnchor, Button } from '~/components';
|
||||
import { cn, removeFocusOutlines } from '~/utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export default function Nav({ links, isCollapsed, resize, defaultActive }: NavProps) {
|
||||
const localize = useLocalize();
|
||||
|
|
@ -20,7 +19,7 @@ export default function Nav({ links, isCollapsed, resize, defaultActive }: NavPr
|
|||
return (
|
||||
<div
|
||||
data-collapsed={isCollapsed}
|
||||
className="bg-token-sidebar-surface-primary hide-scrollbar group flex-shrink-0 overflow-x-hidden py-2 data-[collapsed=true]:py-2"
|
||||
className="bg-token-sidebar-surface-primary hide-scrollbar group flex-shrink-0 overflow-x-hidden"
|
||||
>
|
||||
<div className="h-full">
|
||||
<div className="flex h-full min-h-0 flex-col">
|
||||
|
|
@ -76,12 +75,11 @@ export default function Nav({ links, isCollapsed, resize, defaultActive }: NavPr
|
|||
>
|
||||
<link.icon className="mr-2 h-4 w-4" />
|
||||
{localize(link.title)}
|
||||
{link.label && (
|
||||
{link.label != null && link.label && (
|
||||
<span
|
||||
className={cn(
|
||||
'ml-auto transition-all duration-300 ease-in-out',
|
||||
'ml-auto opacity-100 transition-all duration-300 ease-in-out',
|
||||
variant === 'default' ? 'text-background dark:text-white' : '',
|
||||
isCollapsed ? 'opacity-0' : 'opacity-100',
|
||||
)}
|
||||
>
|
||||
{link.label}
|
||||
|
|
|
|||
|
|
@ -572,3 +572,12 @@ export const presetSettings: Record<
|
|||
[`${EModelEndpoint.bedrock}-${BedrockProviders.AI21}`]: bedrockGeneralColumns,
|
||||
[`${EModelEndpoint.bedrock}-${BedrockProviders.Amazon}`]: bedrockGeneralColumns,
|
||||
};
|
||||
|
||||
export const agentSettings: Record<string, SettingsConfiguration | undefined> = Object.entries(
|
||||
presetSettings,
|
||||
).reduce((acc, [key, value]) => {
|
||||
if (value) {
|
||||
acc[key] = value.col2;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
|
|
|||
|
|
@ -1,33 +1,17 @@
|
|||
import { isAssistantsEndpoint, isAgentsEndpoint } from 'librechat-data-provider';
|
||||
import type { SwitcherProps } from '~/common';
|
||||
import { Separator } from '~/components/ui/Separator';
|
||||
import AssistantSwitcher from './AssistantSwitcher';
|
||||
import AgentSwitcher from './AgentSwitcher';
|
||||
import ModelSwitcher from './ModelSwitcher';
|
||||
|
||||
export default function Switcher(props: SwitcherProps) {
|
||||
if (isAssistantsEndpoint(props.endpoint) && props.endpointKeyProvided) {
|
||||
return (
|
||||
<>
|
||||
<AssistantSwitcher {...props} />
|
||||
<Separator className="max-w-[98%] bg-surface-tertiary" />
|
||||
</>
|
||||
);
|
||||
return <AssistantSwitcher {...props} />;
|
||||
} else if (isAgentsEndpoint(props.endpoint) && props.endpointKeyProvided) {
|
||||
return (
|
||||
<>
|
||||
<AgentSwitcher {...props} />
|
||||
<Separator className="bg-gray-100/50 dark:bg-gray-600" />
|
||||
</>
|
||||
);
|
||||
return <AgentSwitcher {...props} />;
|
||||
} else if (isAssistantsEndpoint(props.endpoint)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModelSwitcher {...props} />
|
||||
<Separator className="max-w-[98%] bg-surface-tertiary" />
|
||||
</>
|
||||
);
|
||||
return <ModelSwitcher {...props} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ import { useEffect } from 'react';
|
|||
import { Search, X } from 'lucide-react';
|
||||
import { Dialog, DialogPanel, DialogTitle, Description } from '@headlessui/react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { isAgentsEndpoint } from 'librechat-data-provider';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import type {
|
||||
AssistantsEndpoint,
|
||||
EModelEndpoint,
|
||||
TError,
|
||||
TPluginAction,
|
||||
TError,
|
||||
} from 'librechat-data-provider';
|
||||
import type { TPluginStoreDialogProps } from '~/common/types';
|
||||
import { PluginPagination, PluginAuthForm } from '~/components/Plugins/Store';
|
||||
|
|
@ -26,7 +27,8 @@ function ToolSelectDialog({
|
|||
}) {
|
||||
const localize = useLocalize();
|
||||
const { getValues, setValue } = useFormContext();
|
||||
const { data: tools = [] } = useAvailableToolsQuery(endpoint);
|
||||
const { data: tools } = useAvailableToolsQuery(endpoint);
|
||||
const isAgentTools = isAgentsEndpoint(endpoint);
|
||||
|
||||
const {
|
||||
maxPage,
|
||||
|
|
@ -54,8 +56,9 @@ function ToolSelectDialog({
|
|||
const updateUserPlugins = useUpdateUserPluginsMutation();
|
||||
const handleInstallError = (error: TError) => {
|
||||
setError(true);
|
||||
if (error.response?.data?.message) {
|
||||
setErrorMessage(error.response?.data?.message);
|
||||
const errorMessage = error.response?.data?.message ?? '';
|
||||
if (errorMessage) {
|
||||
setErrorMessage(errorMessage);
|
||||
}
|
||||
setTimeout(() => {
|
||||
setError(false);
|
||||
|
|
@ -105,7 +108,7 @@ function ToolSelectDialog({
|
|||
const getAvailablePluginFromKey = tools?.find((p) => p.pluginKey === pluginKey);
|
||||
setSelectedPlugin(getAvailablePluginFromKey);
|
||||
|
||||
const { authConfig, authenticated } = getAvailablePluginFromKey ?? {};
|
||||
const { authConfig, authenticated = false } = getAvailablePluginFromKey ?? {};
|
||||
|
||||
if (authConfig && authConfig.length > 0 && !authenticated) {
|
||||
setShowPluginAuthForm(true);
|
||||
|
|
@ -159,7 +162,9 @@ function ToolSelectDialog({
|
|||
<div className="flex items-center">
|
||||
<div className="text-center sm:text-left">
|
||||
<DialogTitle className="text-lg font-medium leading-6 text-gray-900 dark:text-gray-200">
|
||||
{localize('com_nav_tool_dialog')}
|
||||
{isAgentTools
|
||||
? localize('com_nav_tool_dialog_agents')
|
||||
: localize('com_nav_tool_dialog')}
|
||||
</DialogTitle>
|
||||
<Description className="text-sm text-gray-500 dark:text-gray-300">
|
||||
{localize('com_nav_tool_dialog_description')}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { cn } from '~/utils';
|
||||
|
||||
export default function Sparkles({ className = '' }) {
|
||||
export default function Sparkles({ className = '', size = 24 }) {
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
1
client/src/data-provider/Files/index.ts
Normal file
1
client/src/data-provider/Files/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './queries';
|
||||
84
client/src/data-provider/Files/queries.ts
Normal file
84
client/src/data-provider/Files/queries.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import { QueryKeys, dataService } from 'librechat-data-provider';
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import type { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query';
|
||||
import type t from 'librechat-data-provider';
|
||||
import { addFileToCache } from '~/utils';
|
||||
|
||||
export const useGetFiles = <TData = t.TFile[] | boolean>(
|
||||
config?: UseQueryOptions<t.TFile[], unknown, TData>,
|
||||
): QueryObserverResult<TData, unknown> => {
|
||||
return useQuery<t.TFile[], unknown, TData>([QueryKeys.files], () => dataService.getFiles(), {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
...config,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetFileConfig = <TData = t.FileConfig>(
|
||||
config?: UseQueryOptions<t.FileConfig, unknown, TData>,
|
||||
): QueryObserverResult<TData, unknown> => {
|
||||
return useQuery<t.FileConfig, unknown, TData>(
|
||||
[QueryKeys.fileConfig],
|
||||
() => dataService.getFileConfig(),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
...config,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const useFileDownload = (userId?: string, file_id?: string): QueryObserverResult<string> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
[QueryKeys.fileDownload, file_id],
|
||||
async () => {
|
||||
if (!userId || !file_id) {
|
||||
console.warn('No user ID provided for file download');
|
||||
return;
|
||||
}
|
||||
const response = await dataService.getFileDownload(userId, file_id);
|
||||
const blob = response.data;
|
||||
const downloadURL = window.URL.createObjectURL(blob);
|
||||
try {
|
||||
const metadata: t.TFile | undefined = JSON.parse(response.headers['x-file-metadata']);
|
||||
if (!metadata) {
|
||||
console.warn('No metadata found for file download', response.headers);
|
||||
return downloadURL;
|
||||
}
|
||||
|
||||
addFileToCache(queryClient, metadata);
|
||||
} catch (e) {
|
||||
console.error('Error parsing file metadata, skipped updating file query cache', e);
|
||||
}
|
||||
|
||||
return downloadURL;
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
retry: false,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
export const useCodeOutputDownload = (url = ''): QueryObserverResult<string> => {
|
||||
return useQuery(
|
||||
[QueryKeys.fileDownload, url],
|
||||
async () => {
|
||||
if (!url) {
|
||||
console.warn('No user ID provided for file download');
|
||||
return;
|
||||
}
|
||||
const response = await dataService.getCodeOutputDownload(url);
|
||||
const blob = response.data;
|
||||
const downloadURL = window.URL.createObjectURL(blob);
|
||||
return downloadURL;
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
retry: false,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from './Files';
|
||||
export * from './connection';
|
||||
export * from './mutations';
|
||||
export * from './prompts';
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
import { useSetRecoilState } from 'recoil';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { dataService, MutationKeys, QueryKeys, defaultOrderQuery } from 'librechat-data-provider';
|
||||
import type t from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { InfiniteData, UseMutationResult } from '@tanstack/react-query';
|
||||
import useUpdateTagsInConvo from '~/hooks/Conversations/useUpdateTagsInConvo';
|
||||
import { updateConversationTag } from '~/utils/conversationTags';
|
||||
|
|
@ -537,32 +537,33 @@ export const useDeleteConversationMutation = (
|
|||
(payload: t.TDeleteConversationRequest) => dataService.deleteConversation(payload),
|
||||
{
|
||||
onSuccess: (_data, vars, context) => {
|
||||
if (!vars.conversationId) {
|
||||
const conversationId = vars.conversationId ?? '';
|
||||
if (!conversationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const handleDelete = (convoData) => {
|
||||
const handleDelete = (convoData: t.ConversationData | undefined) => {
|
||||
if (!convoData) {
|
||||
return convoData;
|
||||
}
|
||||
return normalizeData(
|
||||
deleteConversation(convoData, vars.conversationId as string),
|
||||
deleteConversation(convoData, conversationId),
|
||||
'conversations',
|
||||
convoData.pages[0].pageSize,
|
||||
Number(convoData.pages[0].pageSize),
|
||||
);
|
||||
};
|
||||
|
||||
queryClient.setQueryData([QueryKeys.conversation, vars.conversationId], null);
|
||||
queryClient.setQueryData([QueryKeys.conversation, conversationId], null);
|
||||
queryClient.setQueryData<t.ConversationData>([QueryKeys.allConversations], handleDelete);
|
||||
queryClient.setQueryData<t.ConversationData>(
|
||||
[QueryKeys.archivedConversations],
|
||||
handleDelete,
|
||||
);
|
||||
const current = queryClient.getQueryData<t.ConversationData>([QueryKeys.allConversations]);
|
||||
refetch({ refetchPage: (page, index) => index === (current?.pages.length || 1) - 1 });
|
||||
refetch({ refetchPage: (page, index) => index === (current?.pages.length ?? 1) - 1 });
|
||||
onSuccess?.(_data, vars, context);
|
||||
},
|
||||
...(_options || {}),
|
||||
..._options,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
|
@ -593,7 +594,7 @@ export const useForkConvoMutation = (
|
|||
);
|
||||
onSuccess?.(data, vars, context);
|
||||
},
|
||||
...(_options || {}),
|
||||
..._options,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -642,7 +643,7 @@ export const useUploadFileMutation = (
|
|||
|
||||
return dataService.uploadFile(body);
|
||||
},
|
||||
...(options || {}),
|
||||
...options,
|
||||
onSuccess: (data, formData, context) => {
|
||||
queryClient.setQueryData<t.TFile[] | undefined>([QueryKeys.files], (_files) => [
|
||||
data,
|
||||
|
|
@ -650,11 +651,44 @@ export const useUploadFileMutation = (
|
|||
]);
|
||||
|
||||
const endpoint = formData.get('endpoint');
|
||||
const assistant_id = formData.get('assistant_id');
|
||||
const message_file = formData.get('message_file');
|
||||
const tool_resource = formData.get('tool_resource');
|
||||
const agent_id = (formData.get('agent_id') as string | undefined) ?? '';
|
||||
const assistant_id = (formData.get('assistant_id') as string | undefined) ?? '';
|
||||
const tool_resource = (formData.get('tool_resource') as string | undefined) ?? '';
|
||||
|
||||
if (!assistant_id || message_file === 'true') {
|
||||
if (message_file === 'true') {
|
||||
onSuccess?.(data, formData, context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (agent_id && tool_resource) {
|
||||
queryClient.setQueryData<t.Agent>([QueryKeys.agent, agent_id], (agent) => {
|
||||
if (!agent) {
|
||||
return agent;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
const prevResources = agent.tool_resources ?? {};
|
||||
const prevResource: t.ExecuteCodeResource | t.AgentFileSearchResource = agent
|
||||
.tool_resources?.[tool_resource] ?? {
|
||||
file_ids: [],
|
||||
};
|
||||
if (!prevResource.file_ids) {
|
||||
prevResource.file_ids = [];
|
||||
}
|
||||
prevResource.file_ids.push(data.file_id);
|
||||
update['tool_resources'] = {
|
||||
...prevResources,
|
||||
[tool_resource]: prevResource,
|
||||
};
|
||||
return {
|
||||
...agent,
|
||||
...update,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
if (!assistant_id) {
|
||||
onSuccess?.(data, formData, context);
|
||||
return;
|
||||
}
|
||||
|
|
@ -679,13 +713,16 @@ export const useUploadFileMutation = (
|
|||
}
|
||||
if (tool_resource === EToolResources.code_interpreter) {
|
||||
const prevResources = assistant.tool_resources ?? {};
|
||||
const prevResource = assistant.tool_resources?.[tool_resource as string] ?? {
|
||||
const prevResource = assistant.tool_resources?.[tool_resource] ?? {
|
||||
file_ids: [],
|
||||
};
|
||||
if (!prevResource.file_ids) {
|
||||
prevResource.file_ids = [];
|
||||
}
|
||||
prevResource.file_ids.push(data.file_id);
|
||||
update['tool_resources'] = {
|
||||
...prevResources,
|
||||
[tool_resource as string]: prevResource,
|
||||
[tool_resource]: prevResource,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
|
@ -712,9 +749,8 @@ export const useDeleteFilesMutation = (
|
|||
const queryClient = useQueryClient();
|
||||
const { onSuccess, ...options } = _options || {};
|
||||
return useMutation([MutationKeys.fileDelete], {
|
||||
mutationFn: (body: t.DeleteFilesBody) =>
|
||||
dataService.deleteFiles(body.files, body.assistant_id, body.tool_resource),
|
||||
...(options || {}),
|
||||
mutationFn: (body: t.DeleteFilesBody) => dataService.deleteFiles(body),
|
||||
...options,
|
||||
onSuccess: (data, ...args) => {
|
||||
queryClient.setQueryData<t.TFile[] | undefined>([QueryKeys.files], (cachefiles) => {
|
||||
const { files: filesDeleted } = args[0];
|
||||
|
|
@ -1228,6 +1264,8 @@ export const useUpdateAgentMutation = (
|
|||
return agent;
|
||||
}),
|
||||
});
|
||||
|
||||
queryClient.setQueryData<t.Agent>([QueryKeys.agent, variables.agent_id], updatedAgent);
|
||||
return options?.onSuccess?.(updatedAgent, variables, context);
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,15 +10,12 @@ import type {
|
|||
UseInfiniteQueryOptions,
|
||||
QueryObserverResult,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
} from '@tanstack/react-query';
|
||||
import type t from 'librechat-data-provider';
|
||||
import type {
|
||||
Action,
|
||||
TPreset,
|
||||
TFile,
|
||||
TPlugin,
|
||||
FileConfig,
|
||||
ConversationListResponse,
|
||||
ConversationListParams,
|
||||
Assistant,
|
||||
|
|
@ -32,36 +29,8 @@ import type {
|
|||
TCheckUserKeyResponse,
|
||||
SharedLinkListParams,
|
||||
SharedLinksResponse,
|
||||
TUserTermsResponse,
|
||||
TAcceptTermsResponse,
|
||||
} from 'librechat-data-provider';
|
||||
import { findPageForConversation, addFileToCache } from '~/utils';
|
||||
|
||||
export const useGetFiles = <TData = TFile[] | boolean>(
|
||||
config?: UseQueryOptions<TFile[], unknown, TData>,
|
||||
): QueryObserverResult<TData, unknown> => {
|
||||
return useQuery<TFile[], unknown, TData>([QueryKeys.files], () => dataService.getFiles(), {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
...config,
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetFileConfig = <TData = FileConfig>(
|
||||
config?: UseQueryOptions<FileConfig, unknown, TData>,
|
||||
): QueryObserverResult<TData, unknown> => {
|
||||
return useQuery<FileConfig, unknown, TData>(
|
||||
[QueryKeys.fileConfig],
|
||||
() => dataService.getFileConfig(),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
...config,
|
||||
},
|
||||
);
|
||||
};
|
||||
import { findPageForConversation } from '~/utils';
|
||||
|
||||
export const useGetPresetsQuery = (
|
||||
config?: UseQueryOptions<TPreset[]>,
|
||||
|
|
@ -321,7 +290,7 @@ export const useGetAssistantByIdQuery = (
|
|||
const queryClient = useQueryClient();
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([QueryKeys.name, endpoint]);
|
||||
const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide;
|
||||
const userProvidesKey = endpointsConfig?.[endpoint]?.userProvide ?? false;
|
||||
const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true;
|
||||
const enabled = !!endpointsConfig?.[endpoint] && keyProvided;
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
|
|
@ -401,39 +370,6 @@ export const useGetAssistantDocsQuery = <TData = AssistantDocument[]>(
|
|||
);
|
||||
};
|
||||
|
||||
export const useFileDownload = (userId?: string, file_id?: string): QueryObserverResult<string> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
[QueryKeys.fileDownload, file_id],
|
||||
async () => {
|
||||
if (!userId || !file_id) {
|
||||
console.warn('No user ID provided for file download');
|
||||
return;
|
||||
}
|
||||
const response = await dataService.getFileDownload(userId, file_id);
|
||||
const blob = response.data;
|
||||
const downloadURL = window.URL.createObjectURL(blob);
|
||||
try {
|
||||
const metadata: TFile | undefined = JSON.parse(response.headers['x-file-metadata']);
|
||||
if (!metadata) {
|
||||
console.warn('No metadata found for file download', response.headers);
|
||||
return downloadURL;
|
||||
}
|
||||
|
||||
addFileToCache(queryClient, metadata);
|
||||
} catch (e) {
|
||||
console.error('Error parsing file metadata, skipped updating file query cache', e);
|
||||
}
|
||||
|
||||
return downloadURL;
|
||||
},
|
||||
{
|
||||
enabled: false,
|
||||
retry: false,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* AGENTS
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default function useSelectAgent() {
|
|||
);
|
||||
|
||||
const agentQuery = useGetAgentByIdQuery(selectedAgentId ?? '', {
|
||||
enabled: !!selectedAgentId,
|
||||
enabled: !!(selectedAgentId ?? ''),
|
||||
});
|
||||
|
||||
const updateConversation = useCallback(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,7 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
import { FileSources, EToolResources } from 'librechat-data-provider';
|
||||
import { FileSources, EToolResources, removeNullishValues } from 'librechat-data-provider';
|
||||
import { useCallback, useState, useEffect } from 'react';
|
||||
import type {
|
||||
BatchFile,
|
||||
TFile,
|
||||
DeleteFilesResponse,
|
||||
DeleteFilesBody,
|
||||
} from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { UseMutateAsyncFunction } from '@tanstack/react-query';
|
||||
import type { ExtendedFile, GenericSetter } from '~/common';
|
||||
import useSetFilesToDelete from './useSetFilesToDelete';
|
||||
|
|
@ -15,21 +10,38 @@ type FileMapSetter = GenericSetter<Map<string, ExtendedFile>>;
|
|||
|
||||
const useFileDeletion = ({
|
||||
mutateAsync,
|
||||
agent_id,
|
||||
assistant_id,
|
||||
tool_resource,
|
||||
}: {
|
||||
mutateAsync: UseMutateAsyncFunction<DeleteFilesResponse, unknown, DeleteFilesBody, unknown>;
|
||||
mutateAsync: UseMutateAsyncFunction<t.DeleteFilesResponse, unknown, t.DeleteFilesBody, unknown>;
|
||||
agent_id?: string;
|
||||
assistant_id?: string;
|
||||
tool_resource?: EToolResources;
|
||||
}) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_batch, setFileDeleteBatch] = useState<BatchFile[]>([]);
|
||||
const [_batch, setFileDeleteBatch] = useState<t.BatchFile[]>([]);
|
||||
const setFilesToDelete = useSetFilesToDelete();
|
||||
|
||||
const executeBatchDelete = useCallback(
|
||||
(filesToDelete: BatchFile[], assistant_id?: string, tool_resource?: EToolResources) => {
|
||||
console.log('Deleting files:', filesToDelete, assistant_id, tool_resource);
|
||||
mutateAsync({ files: filesToDelete, assistant_id, tool_resource });
|
||||
({
|
||||
filesToDelete,
|
||||
agent_id,
|
||||
assistant_id,
|
||||
tool_resource,
|
||||
}: {
|
||||
filesToDelete: t.BatchFile[];
|
||||
agent_id?: string;
|
||||
assistant_id?: string;
|
||||
tool_resource?: EToolResources;
|
||||
}) => {
|
||||
const payload = removeNullishValues({
|
||||
agent_id,
|
||||
assistant_id,
|
||||
tool_resource,
|
||||
});
|
||||
console.log('Deleting files:', filesToDelete, payload);
|
||||
mutateAsync({ files: filesToDelete, ...payload });
|
||||
setFileDeleteBatch([]);
|
||||
},
|
||||
[mutateAsync],
|
||||
|
|
@ -44,22 +56,22 @@ const useFileDeletion = ({
|
|||
}, [debouncedDelete]);
|
||||
|
||||
const deleteFile = useCallback(
|
||||
({ file: _file, setFiles }: { file: ExtendedFile | TFile; setFiles?: FileMapSetter }) => {
|
||||
({ file: _file, setFiles }: { file: ExtendedFile | t.TFile; setFiles?: FileMapSetter }) => {
|
||||
const {
|
||||
file_id,
|
||||
temp_file_id = '',
|
||||
filepath = '',
|
||||
source = FileSources.local,
|
||||
embedded,
|
||||
attached,
|
||||
} = _file as TFile & { attached?: boolean };
|
||||
attached = false,
|
||||
} = _file as t.TFile & { attached?: boolean };
|
||||
|
||||
const progress = _file['progress'] ?? 1;
|
||||
|
||||
if (progress < 1) {
|
||||
return;
|
||||
}
|
||||
const file: BatchFile = {
|
||||
const file: t.BatchFile = {
|
||||
file_id,
|
||||
embedded,
|
||||
filepath,
|
||||
|
|
@ -83,15 +95,20 @@ const useFileDeletion = ({
|
|||
|
||||
setFileDeleteBatch((prevBatch) => {
|
||||
const newBatch = [...prevBatch, file];
|
||||
debouncedDelete(newBatch, assistant_id, tool_resource);
|
||||
debouncedDelete({
|
||||
filesToDelete: newBatch,
|
||||
agent_id,
|
||||
assistant_id,
|
||||
tool_resource,
|
||||
});
|
||||
return newBatch;
|
||||
});
|
||||
},
|
||||
[debouncedDelete, setFilesToDelete, assistant_id, tool_resource],
|
||||
[debouncedDelete, setFilesToDelete, agent_id, assistant_id, tool_resource],
|
||||
);
|
||||
|
||||
const deleteFiles = useCallback(
|
||||
({ files, setFiles }: { files: ExtendedFile[] | TFile[]; setFiles?: FileMapSetter }) => {
|
||||
({ files, setFiles }: { files: ExtendedFile[] | t.TFile[]; setFiles?: FileMapSetter }) => {
|
||||
const batchFiles = files.map((_file) => {
|
||||
const { file_id, embedded, filepath = '', source = FileSources.local } = _file;
|
||||
|
||||
|
|
@ -117,11 +134,15 @@ const useFileDeletion = ({
|
|||
|
||||
setFileDeleteBatch((prevBatch) => {
|
||||
const newBatch = [...prevBatch, ...batchFiles];
|
||||
debouncedDelete(newBatch, assistant_id);
|
||||
debouncedDelete({
|
||||
filesToDelete: newBatch,
|
||||
agent_id,
|
||||
assistant_id,
|
||||
});
|
||||
return newBatch;
|
||||
});
|
||||
},
|
||||
[debouncedDelete, setFilesToDelete, assistant_id],
|
||||
[debouncedDelete, setFilesToDelete, agent_id, assistant_id],
|
||||
);
|
||||
|
||||
return { deleteFile, deleteFiles };
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
params?.fileSetter ?? setFiles,
|
||||
);
|
||||
|
||||
const agent_id = params?.additionalMetadata?.agent_id ?? '';
|
||||
const assistant_id = params?.additionalMetadata?.assistant_id ?? '';
|
||||
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
});
|
||||
|
|
@ -84,13 +87,17 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
onSuccess: (data) => {
|
||||
clearUploadTimer(data.temp_file_id);
|
||||
console.log('upload success', data);
|
||||
if (agent_id) {
|
||||
queryClient.refetchQueries([QueryKeys.agent, agent_id]);
|
||||
return;
|
||||
}
|
||||
updateFileById(
|
||||
data.temp_file_id,
|
||||
{
|
||||
progress: 0.9,
|
||||
filepath: data.filepath,
|
||||
},
|
||||
params?.additionalMetadata?.assistant_id ? true : false,
|
||||
assistant_id ? true : false,
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
|
|
@ -108,7 +115,7 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
source: data.source,
|
||||
embedded: data.embedded,
|
||||
},
|
||||
params?.additionalMetadata?.assistant_id ? true : false,
|
||||
assistant_id ? true : false,
|
||||
);
|
||||
}, 300);
|
||||
},
|
||||
|
|
@ -118,51 +125,45 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
clearUploadTimer(file_id as string);
|
||||
deleteFileById(file_id as string);
|
||||
setError(
|
||||
(error as TError)?.response?.data?.message ?? 'An error occurred while uploading the file.',
|
||||
(error as TError | undefined)?.response?.data?.message ??
|
||||
'An error occurred while uploading the file.',
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const startUpload = async (extendedFile: ExtendedFile) => {
|
||||
if (!endpoint) {
|
||||
setError('An error occurred while uploading the file: Endpoint is undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
startUploadTimer(extendedFile.file_id, extendedFile.file?.name || 'File', extendedFile.size);
|
||||
const filename = extendedFile.file?.name ?? 'File';
|
||||
startUploadTimer(extendedFile.file_id, filename, extendedFile.size);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append(
|
||||
'file',
|
||||
extendedFile.file as File,
|
||||
encodeURIComponent(extendedFile.file?.name || 'File'),
|
||||
);
|
||||
formData.append('file', extendedFile.file as File, encodeURIComponent(filename));
|
||||
formData.append('file_id', extendedFile.file_id);
|
||||
if (extendedFile.width) {
|
||||
formData.append('width', extendedFile.width?.toString());
|
||||
|
||||
const width = extendedFile.width ?? 0;
|
||||
const height = extendedFile.height ?? 0;
|
||||
if (width) {
|
||||
formData.append('width', width.toString());
|
||||
}
|
||||
if (extendedFile.height) {
|
||||
formData.append('height', extendedFile.height?.toString());
|
||||
if (height) {
|
||||
formData.append('height', height.toString());
|
||||
}
|
||||
|
||||
if (params?.additionalMetadata) {
|
||||
for (const [key, value] of Object.entries(params.additionalMetadata)) {
|
||||
for (const [key, value = ''] of Object.entries(params.additionalMetadata)) {
|
||||
if (value) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isAssistantsEndpoint(endpoint) &&
|
||||
!formData.get('assistant_id') &&
|
||||
conversation?.assistant_id
|
||||
) {
|
||||
const convoAssistantId = conversation?.assistant_id ?? '';
|
||||
const convoModel = conversation?.model ?? '';
|
||||
if (isAssistantsEndpoint(endpoint) && !formData.get('assistant_id') && convoAssistantId) {
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
formData.append('version', version);
|
||||
formData.append('assistant_id', conversation.assistant_id);
|
||||
formData.append('model', conversation?.model ?? '');
|
||||
formData.append('assistant_id', convoAssistantId);
|
||||
formData.append('model', convoModel);
|
||||
formData.append('message_file', 'true');
|
||||
}
|
||||
if (isAssistantsEndpoint(endpoint) && !formData.get('version')) {
|
||||
|
|
@ -238,7 +239,10 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
(file) =>
|
||||
`${file.file?.name ?? file.filename}-${file.size}-${file.type?.split('/')[0] ?? 'file'}`,
|
||||
),
|
||||
...fileList.map((file) => `${file.name}-${file.size}-${file.type?.split('/')[0] ?? 'file'}`),
|
||||
...fileList.map(
|
||||
(file: File | undefined) =>
|
||||
`${file?.name}-${file?.size}-${file?.type.split('/')[0] ?? 'file'}`,
|
||||
),
|
||||
];
|
||||
|
||||
const uniqueFilesSet = new Set(combinedFilesInfo);
|
||||
|
|
@ -300,7 +304,7 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
|
||||
addFile(extendedFile);
|
||||
|
||||
if (originalFile.type?.split('/')[0] === 'image') {
|
||||
if (originalFile.type.split('/')[0] === 'image') {
|
||||
loadImage(extendedFile, preview);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,7 @@ export default function useUpdateFiles(setFiles: FileSetter) {
|
|||
});
|
||||
};
|
||||
|
||||
const updateFileById = (
|
||||
fileId: string,
|
||||
updates: Partial<ExtendedFile>,
|
||||
isAssistantFile?: boolean,
|
||||
) => {
|
||||
const updateFileById = (fileId: string, updates: Partial<ExtendedFile>, isEntityFile = false) => {
|
||||
setFiles((currentFiles) => {
|
||||
if (!currentFiles.has(fileId)) {
|
||||
console.warn(`File with id ${fileId} not found.`);
|
||||
|
|
@ -38,8 +34,8 @@ export default function useUpdateFiles(setFiles: FileSetter) {
|
|||
return currentFiles;
|
||||
}
|
||||
updatedFiles.set(fileId, { ...currentFile, ...updates });
|
||||
|
||||
if (updates['filepath'] && updates['progress'] !== 1 && !isAssistantFile) {
|
||||
const filepath = updates['filepath'] ?? '';
|
||||
if (filepath && updates['progress'] !== 1 && !isEntityFile) {
|
||||
const files = Object.fromEntries(updatedFiles);
|
||||
setFilesToDelete(files);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export { default as useSSE } from './useSSE';
|
||||
export { default as useStepHandler } from './useStepHandler';
|
||||
export { default as useContentHandler } from './useContentHandler';
|
||||
export { default as useAttachmentHandler } from './useAttachmentHandler';
|
||||
|
|
|
|||
20
client/src/hooks/SSE/useAttachmentHandler.ts
Normal file
20
client/src/hooks/SSE/useAttachmentHandler.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { useSetRecoilState } from 'recoil';
|
||||
import type { TAttachment, EventSubmission } from 'librechat-data-provider';
|
||||
import store from '~/store';
|
||||
|
||||
export default function useAttachmentHandler() {
|
||||
const setAttachmentsMap = useSetRecoilState(store.messageAttachmentsMap);
|
||||
|
||||
return ({ data }: { data: TAttachment; submission: EventSubmission }) => {
|
||||
const { messageId } = data;
|
||||
|
||||
setAttachmentsMap((prevMap) => {
|
||||
const messageAttachments =
|
||||
(prevMap as Record<string, TAttachment[] | undefined>)[messageId] || [];
|
||||
return {
|
||||
...prevMap,
|
||||
[messageId]: [...messageAttachments, data],
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ import {
|
|||
updateConversation,
|
||||
getConversationById,
|
||||
} from '~/utils';
|
||||
import useAttachmentHandler from '~/hooks/SSE/useAttachmentHandler';
|
||||
import useContentHandler from '~/hooks/SSE/useContentHandler';
|
||||
import useStepHandler from '~/hooks/SSE/useStepHandler';
|
||||
import { useAuthContext } from '~/hooks/AuthContext';
|
||||
|
|
@ -62,7 +63,7 @@ export default function useEventHandlers({
|
|||
setMessages,
|
||||
getMessages,
|
||||
setCompleted,
|
||||
isAddedRequest,
|
||||
isAddedRequest = false,
|
||||
setConversation,
|
||||
setIsSubmitting,
|
||||
newConversation,
|
||||
|
|
@ -85,6 +86,7 @@ export default function useEventHandlers({
|
|||
setIsSubmitting,
|
||||
lastAnnouncementTimeRef,
|
||||
});
|
||||
const attachmentHandler = useAttachmentHandler();
|
||||
|
||||
const messageHandler = useCallback(
|
||||
(data: string | undefined, submission: EventSubmission) => {
|
||||
|
|
@ -138,15 +140,20 @@ export default function useEventHandlers({
|
|||
const { requestMessage, responseMessage, conversation } = data;
|
||||
const { messages, isRegenerate = false } = submission;
|
||||
|
||||
const convoUpdate = conversation ?? submission.conversation;
|
||||
const convoUpdate =
|
||||
(conversation as TConversation | null) ?? (submission.conversation as TConversation);
|
||||
|
||||
// update the messages
|
||||
if (isRegenerate) {
|
||||
const messagesUpdate = [...messages, responseMessage].filter((msg) => msg);
|
||||
setMessages(messagesUpdate);
|
||||
const messagesUpdate = (
|
||||
[...messages, responseMessage] as Array<TMessage | undefined>
|
||||
).filter((msg) => msg);
|
||||
setMessages(messagesUpdate as TMessage[]);
|
||||
} else {
|
||||
const messagesUpdate = [...messages, requestMessage, responseMessage].filter((msg) => msg);
|
||||
setMessages(messagesUpdate);
|
||||
const messagesUpdate = (
|
||||
[...messages, requestMessage, responseMessage] as Array<TMessage | undefined>
|
||||
).filter((msg) => msg);
|
||||
setMessages(messagesUpdate as TMessage[]);
|
||||
}
|
||||
|
||||
const isNewConvo = conversation.conversationId !== submission.conversation.conversationId;
|
||||
|
|
@ -208,7 +215,10 @@ export default function useEventHandlers({
|
|||
setConversation((prevState) => {
|
||||
let title = prevState?.title;
|
||||
const parentId = requestMessage.parentMessageId;
|
||||
if (parentId !== Constants.NO_PARENT && title?.toLowerCase()?.includes('new chat')) {
|
||||
if (
|
||||
parentId !== Constants.NO_PARENT &&
|
||||
(title?.toLowerCase()?.includes('new chat') ?? false)
|
||||
) {
|
||||
const convos = queryClient.getQueryData<ConversationData>([QueryKeys.allConversations]);
|
||||
const cachedConvo = getConversationById(convos, conversationId);
|
||||
title = cachedConvo?.title;
|
||||
|
|
@ -289,7 +299,10 @@ export default function useEventHandlers({
|
|||
setConversation((prevState) => {
|
||||
let title = prevState?.title;
|
||||
const parentId = isRegenerate ? userMessage.overrideParentMessageId : parentMessageId;
|
||||
if (parentId !== Constants.NO_PARENT && title?.toLowerCase()?.includes('new chat')) {
|
||||
if (
|
||||
parentId !== Constants.NO_PARENT &&
|
||||
(title?.toLowerCase()?.includes('new chat') ?? false)
|
||||
) {
|
||||
const convos = queryClient.getQueryData<ConversationData>([QueryKeys.allConversations]);
|
||||
const cachedConvo = getConversationById(convos, conversationId);
|
||||
title = cachedConvo?.title;
|
||||
|
|
@ -437,7 +450,7 @@ export default function useEventHandlers({
|
|||
|
||||
setCompleted((prev) => new Set(prev.add(initialResponse.messageId)));
|
||||
|
||||
const conversationId = userMessage.conversationId ?? submission.conversationId;
|
||||
const conversationId = userMessage.conversationId ?? submission.conversationId ?? '';
|
||||
|
||||
const parseErrorResponse = (data: TResData | Partial<TMessage>) => {
|
||||
const metadata = data['responseMessage'] ?? data;
|
||||
|
|
@ -456,7 +469,7 @@ export default function useEventHandlers({
|
|||
};
|
||||
|
||||
if (!data) {
|
||||
const convoId = conversationId ?? v4();
|
||||
const convoId = conversationId || v4();
|
||||
const errorResponse = parseErrorResponse({
|
||||
text: 'Error connecting to server, try refreshing the page.',
|
||||
...submission,
|
||||
|
|
@ -473,7 +486,8 @@ export default function useEventHandlers({
|
|||
return;
|
||||
}
|
||||
|
||||
if (!conversationId && !data.conversationId) {
|
||||
const receivedConvoId = data.conversationId ?? '';
|
||||
if (!conversationId && !receivedConvoId) {
|
||||
const convoId = v4();
|
||||
const errorResponse = parseErrorResponse(data);
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
|
|
@ -485,7 +499,7 @@ export default function useEventHandlers({
|
|||
}
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
} else if (!data.conversationId) {
|
||||
} else if (!receivedConvoId) {
|
||||
const errorResponse = parseErrorResponse(data);
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
setIsSubmitting(false);
|
||||
|
|
@ -500,9 +514,9 @@ export default function useEventHandlers({
|
|||
});
|
||||
|
||||
setMessages([...messages, userMessage, errorResponse]);
|
||||
if (data.conversationId && paramId === 'new' && newConversation) {
|
||||
if (receivedConvoId && paramId === 'new' && newConversation) {
|
||||
newConversation({
|
||||
template: { conversationId: data.conversationId },
|
||||
template: { conversationId: receivedConvoId },
|
||||
preset: tPresetSchema.parse(submission.conversation),
|
||||
});
|
||||
}
|
||||
|
|
@ -517,7 +531,8 @@ export default function useEventHandlers({
|
|||
async (conversationId = '', submission: EventSubmission, messages?: TMessage[]) => {
|
||||
const runAbortKey = `${conversationId}:${messages?.[messages.length - 1]?.messageId ?? ''}`;
|
||||
console.log({ conversationId, submission, messages, runAbortKey });
|
||||
const { endpoint: _endpoint, endpointType } = submission.conversation || {};
|
||||
const { endpoint: _endpoint, endpointType } =
|
||||
(submission.conversation as TConversation | null) ?? {};
|
||||
const endpoint = endpointType ?? _endpoint;
|
||||
try {
|
||||
const response = await fetch(`${EndpointURLs[endpoint ?? '']}/abort`, {
|
||||
|
|
@ -541,7 +556,7 @@ export default function useEventHandlers({
|
|||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
if (data.final) {
|
||||
if (data.final === true) {
|
||||
finalHandler(data, submission);
|
||||
} else {
|
||||
cancelHandler(data, submission);
|
||||
|
|
@ -569,13 +584,13 @@ export default function useEventHandlers({
|
|||
} catch (error) {
|
||||
console.error('Error cancelling request');
|
||||
console.error(error);
|
||||
const convoId = conversationId ?? v4();
|
||||
const convoId = conversationId || v4();
|
||||
const text =
|
||||
submission.initialResponse.text.length > 45 ? submission.initialResponse.text : '';
|
||||
const errorMessage = {
|
||||
...submission,
|
||||
...submission.initialResponse,
|
||||
text: text ?? (error as Error).message ?? 'Error cancelling request',
|
||||
text: (text || (error as Error | undefined)?.message) ?? 'Error cancelling request',
|
||||
unfinished: !!text.length,
|
||||
error: true,
|
||||
};
|
||||
|
|
@ -601,6 +616,7 @@ export default function useEventHandlers({
|
|||
messageHandler,
|
||||
contentHandler,
|
||||
createdHandler,
|
||||
attachmentHandler,
|
||||
abortConversation,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||
import type { TMessage, TSubmission, EventSubmission } from 'librechat-data-provider';
|
||||
import type { TMessage, TSubmission, TPayload, EventSubmission } from 'librechat-data-provider';
|
||||
import type { EventHandlerParams } from './useEventHandlers';
|
||||
import type { TResData } from '~/common';
|
||||
import { useGenTitleMutation } from '~/data-provider';
|
||||
|
|
@ -59,6 +59,7 @@ export default function useSSE(
|
|||
messageHandler,
|
||||
contentHandler,
|
||||
createdHandler,
|
||||
attachmentHandler,
|
||||
abortConversation,
|
||||
} = useEventHandlers({
|
||||
genTitle,
|
||||
|
|
@ -88,7 +89,7 @@ export default function useSSE(
|
|||
const payloadData = createPayload(submission);
|
||||
let { payload } = payloadData;
|
||||
if (isAssistantsEndpoint(payload.endpoint) || isAgentsEndpoint(payload.endpoint)) {
|
||||
payload = removeNullishValues(payload);
|
||||
payload = removeNullishValues(payload) as TPayload;
|
||||
}
|
||||
|
||||
let textIndex = null;
|
||||
|
|
@ -98,6 +99,15 @@ export default function useSSE(
|
|||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
events.onattachment = (e: MessageEvent) => {
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
attachmentHandler({ data, submission: submission as EventSubmission });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
events.onmessage = (e: MessageEvent) => {
|
||||
const data = JSON.parse(e.data);
|
||||
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: 'الملفات التالية متاحة فقط لمفسر الشفرة:',
|
||||
com_assistants_retrieval: 'استرداد',
|
||||
com_assistants_search_name: 'البحث عن المساعدين بالاسم',
|
||||
com_assistants_tools: 'أدوات المساعدين',
|
||||
com_ui_tools: 'أدوات المساعدين',
|
||||
com_assistants_actions: 'إجراءات',
|
||||
com_assistants_add_tools: 'إضافة أدوات',
|
||||
com_assistants_add_actions: 'إضافة إجراءات',
|
||||
|
|
@ -1964,7 +1964,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'البحث عن المساعدين بالاسم',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'أدوات المساعدين',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,21 +6,29 @@ export default {
|
|||
com_nav_convo_menu_options: 'Opções do Menu de Conversa',
|
||||
com_ui_artifacts: 'Artefatos',
|
||||
com_ui_artifacts_toggle: 'Alternar UI de Artefatos',
|
||||
com_nav_info_code_artifacts: 'Habilita a exibição de artefatos de código experimental ao lado do chat',
|
||||
com_nav_info_code_artifacts:
|
||||
'Habilita a exibição de artefatos de código experimental ao lado do chat',
|
||||
com_ui_include_shadcnui: 'Incluir instruções de componentes shadcn/ui',
|
||||
com_nav_info_include_shadcnui: 'Quando habilitado, as instruções para usar componentes shadcn/ui serão incluídas. shadcn/ui é uma coleção de componentes reutilizáveis construídos usando Radix UI e Tailwind CSS. Nota: estas são instruções longas, você deve habilitar apenas se for importante informar o LLM sobre as importações e componentes corretos. Para mais informações sobre esses componentes, visite: https://ui.shadcn.com/',
|
||||
com_nav_info_include_shadcnui:
|
||||
'Quando habilitado, as instruções para usar componentes shadcn/ui serão incluídas. shadcn/ui é uma coleção de componentes reutilizáveis construídos usando Radix UI e Tailwind CSS. Nota: estas são instruções longas, você deve habilitar apenas se for importante informar o LLM sobre as importações e componentes corretos. Para mais informações sobre esses componentes, visite: https://ui.shadcn.com/',
|
||||
com_ui_custom_prompt_mode: 'Modo de Prompt Personalizado',
|
||||
com_nav_info_custom_prompt_mode: 'Quando habilitado, o prompt padrão do sistema de artefatos não será incluído. Todas as instruções de geração de artefatos devem ser fornecidas manualmente neste modo.',
|
||||
com_nav_info_custom_prompt_mode:
|
||||
'Quando habilitado, o prompt padrão do sistema de artefatos não será incluído. Todas as instruções de geração de artefatos devem ser fornecidas manualmente neste modo.',
|
||||
com_ui_artifact_click: 'Clique para abrir',
|
||||
com_a11y_start: 'A IA começou a responder.',
|
||||
com_a11y_ai_composing: 'A IA ainda está compondo.',
|
||||
com_a11y_end: 'A IA terminou de responder.',
|
||||
com_error_moderation: 'Parece que o conteúdo enviado foi sinalizado pelo nosso sistema de moderação por não estar alinhado com nossas diretrizes da comunidade. Não podemos prosseguir com este tópico específico. Se você tiver outras perguntas ou tópicos que gostaria de explorar, edite sua mensagem ou crie uma nova conversa.',
|
||||
com_error_no_user_key: 'Nenhuma chave encontrada. Por favor, forneça uma chave e tente novamente.',
|
||||
com_error_moderation:
|
||||
'Parece que o conteúdo enviado foi sinalizado pelo nosso sistema de moderação por não estar alinhado com nossas diretrizes da comunidade. Não podemos prosseguir com este tópico específico. Se você tiver outras perguntas ou tópicos que gostaria de explorar, edite sua mensagem ou crie uma nova conversa.',
|
||||
com_error_no_user_key:
|
||||
'Nenhuma chave encontrada. Por favor, forneça uma chave e tente novamente.',
|
||||
com_error_no_base_url: 'Nenhuma URL base encontrada. Por favor, forneça uma e tente novamente.',
|
||||
com_error_invalid_user_key: 'Chave fornecida inválida. Por favor, forneça uma chave válida e tente novamente.',
|
||||
com_error_expired_user_key: 'A chave fornecida para {0} expirou em {1}. Por favor, forneça uma nova chave e tente novamente.',
|
||||
com_error_input_length: 'A contagem de tokens da última mensagem é muito longa, excedendo o limite de tokens ({0} respectivamente). Por favor, encurte sua mensagem, ajuste o tamanho máximo do contexto nos parâmetros da conversa ou divida a conversa para continuar.',
|
||||
com_error_invalid_user_key:
|
||||
'Chave fornecida inválida. Por favor, forneça uma chave válida e tente novamente.',
|
||||
com_error_expired_user_key:
|
||||
'A chave fornecida para {0} expirou em {1}. Por favor, forneça uma nova chave e tente novamente.',
|
||||
com_error_input_length:
|
||||
'A contagem de tokens da última mensagem é muito longa, excedendo o limite de tokens ({0} respectivamente). Por favor, encurte sua mensagem, ajuste o tamanho máximo do contexto nos parâmetros da conversa ou divida a conversa para continuar.',
|
||||
com_files_no_results: 'Nenhum resultado.',
|
||||
com_files_filter: 'Filtrar arquivos...',
|
||||
com_files_number_selected: '{0} de {1} arquivo(s) selecionado(s)',
|
||||
|
|
@ -33,21 +41,27 @@ export default {
|
|||
com_sidepanel_conversation_tags: 'Marcadores',
|
||||
com_assistants_capabilities: 'Capacidades',
|
||||
com_assistants_file_search: 'Pesquisa de Arquivos',
|
||||
com_assistants_file_search_info: 'A pesquisa de arquivos permite que o assistente tenha conhecimento dos arquivos que você ou seus usuários carregam. Uma vez que um arquivo é carregado, o assistente decide automaticamente quando recuperar o conteúdo com base nas solicitações do usuário. Anexar armazenamentos vetoriais para Pesquisa de Arquivos ainda não é suportado. Você pode anexá-los no Playground do Provedor ou anexar arquivos às mensagens para pesquisa de arquivos em uma base de thread.',
|
||||
com_assistants_code_interpreter_info: 'O Interpretador de Código permite que o assistente escreva e execute código. Esta ferramenta pode processar arquivos com dados e formatações diversas, e gerar arquivos como gráficos.',
|
||||
com_assistants_file_search_info:
|
||||
'A pesquisa de arquivos permite que o assistente tenha conhecimento dos arquivos que você ou seus usuários carregam. Uma vez que um arquivo é carregado, o assistente decide automaticamente quando recuperar o conteúdo com base nas solicitações do usuário. Anexar armazenamentos vetoriais para Pesquisa de Arquivos ainda não é suportado. Você pode anexá-los no Playground do Provedor ou anexar arquivos às mensagens para pesquisa de arquivos em uma base de thread.',
|
||||
com_assistants_code_interpreter_info:
|
||||
'O Interpretador de Código permite que o assistente escreva e execute código. Esta ferramenta pode processar arquivos com dados e formatações diversas, e gerar arquivos como gráficos.',
|
||||
com_assistants_knowledge: 'Conhecimento',
|
||||
com_assistants_knowledge_info: 'Se você carregar arquivos em Conhecimento, as conversas com seu Assistente podem incluir o conteúdo dos arquivos.',
|
||||
com_assistants_knowledge_disabled: 'O assistente deve ser criado, e o Interpretador de Código ou Recuperação deve ser habilitado e salvo antes de carregar arquivos como Conhecimento.',
|
||||
com_assistants_knowledge_info:
|
||||
'Se você carregar arquivos em Conhecimento, as conversas com seu Assistente podem incluir o conteúdo dos arquivos.',
|
||||
com_assistants_knowledge_disabled:
|
||||
'O assistente deve ser criado, e o Interpretador de Código ou Recuperação deve ser habilitado e salvo antes de carregar arquivos como Conhecimento.',
|
||||
com_assistants_image_vision: 'Visão de Imagem',
|
||||
com_assistants_code_interpreter: 'Interpretador de Código',
|
||||
com_assistants_code_interpreter_files: 'Os arquivos abaixo são apenas para o Interpretador de Código:',
|
||||
com_assistants_code_interpreter_files:
|
||||
'Os arquivos abaixo são apenas para o Interpretador de Código:',
|
||||
com_assistants_retrieval: 'Recuperação',
|
||||
com_assistants_search_name: 'Pesquisar assistentes por nome',
|
||||
com_assistants_tools: 'Ferramentas',
|
||||
com_ui_tools: 'Ferramentas',
|
||||
com_assistants_actions: 'Ações',
|
||||
com_assistants_add_tools: 'Adicionar Ferramentas',
|
||||
com_assistants_add_actions: 'Adicionar Ações',
|
||||
com_assistants_non_retrieval_model: 'A pesquisa de arquivos não está habilitada neste modelo. Por favor, selecione outro modelo.',
|
||||
com_assistants_non_retrieval_model:
|
||||
'A pesquisa de arquivos não está habilitada neste modelo. Por favor, selecione outro modelo.',
|
||||
com_assistants_available_actions: 'Ações Disponíveis',
|
||||
com_assistants_running_action: 'Executando ação',
|
||||
com_assistants_completed_action: 'Conversou com {0}',
|
||||
|
|
@ -58,7 +72,8 @@ export default {
|
|||
com_assistants_update_actions_success: 'Ação criada ou atualizada com sucesso',
|
||||
com_assistants_update_actions_error: 'Houve um erro ao criar ou atualizar a ação.',
|
||||
com_assistants_delete_actions_error: 'Houve um erro ao excluir a ação.',
|
||||
com_assistants_actions_info: 'Permita que seu Assistente recupere informações ou execute ações via API\'s',
|
||||
com_assistants_actions_info:
|
||||
'Permita que seu Assistente recupere informações ou execute ações via API\'s',
|
||||
com_assistants_name_placeholder: 'Opcional: O nome do assistente',
|
||||
com_assistants_instructions_placeholder: 'As instruções do sistema que o assistente usa',
|
||||
com_assistants_description_placeholder: 'Opcional: Descreva seu Assistente aqui',
|
||||
|
|
@ -96,9 +111,11 @@ export default {
|
|||
com_ui_download_error: 'Erro ao baixar o arquivo. O arquivo pode ter sido excluído.',
|
||||
com_ui_attach_error_type: 'Tipo de arquivo não suportado para o endpoint:',
|
||||
com_ui_attach_error_openai: 'Não é possível anexar arquivos de Assistente a outros endpoints',
|
||||
com_ui_attach_warn_endpoint: 'Arquivos não compatíveis podem ser ignorados sem uma ferramenta compatível',
|
||||
com_ui_attach_warn_endpoint:
|
||||
'Arquivos não compatíveis podem ser ignorados sem uma ferramenta compatível',
|
||||
com_ui_attach_error_size: 'Limite de tamanho de arquivo excedido para o endpoint:',
|
||||
com_ui_attach_error: 'Não é possível anexar o arquivo. Crie ou selecione uma conversa, ou tente atualizar a página.',
|
||||
com_ui_attach_error:
|
||||
'Não é possível anexar o arquivo. Crie ou selecione uma conversa, ou tente atualizar a página.',
|
||||
com_ui_examples: 'Exemplos',
|
||||
com_ui_new_chat: 'Novo chat',
|
||||
com_ui_happy_birthday: 'É meu 1º aniversário!',
|
||||
|
|
@ -162,11 +179,14 @@ export default {
|
|||
com_ui_search_categories: 'Pesquisar Categorias',
|
||||
com_ui_manage: 'Gerenciar',
|
||||
com_ui_variables: 'Variáveis',
|
||||
com_ui_variables_info: 'Use chaves duplas no seu texto para criar variáveis, por exemplo, `{{exemplo de variável}}`, para preencher posteriormente ao usar o prompt.',
|
||||
com_ui_variables_info:
|
||||
'Use chaves duplas no seu texto para criar variáveis, por exemplo, `{{exemplo de variável}}`, para preencher posteriormente ao usar o prompt.',
|
||||
com_ui_special_variables: 'Variáveis especiais:',
|
||||
com_ui_special_variables_info: 'Use `{{current_date}}` para a data atual, e `{{current_user}}` para o nome da sua conta.',
|
||||
com_ui_special_variables_info:
|
||||
'Use `{{current_date}}` para a data atual, e `{{current_user}}` para o nome da sua conta.',
|
||||
com_ui_dropdown_variables: 'Variáveis de dropdown:',
|
||||
com_ui_dropdown_variables_info: 'Crie menus dropdown personalizados para seus prompts: `{{nome_da_variável:opção1|opção2|opção3}}`',
|
||||
com_ui_dropdown_variables_info:
|
||||
'Crie menus dropdown personalizados para seus prompts: `{{nome_da_variável:opção1|opção2|opção3}}`',
|
||||
com_ui_showing: 'Mostrando',
|
||||
com_ui_of: 'de',
|
||||
com_ui_entries: 'Entradas',
|
||||
|
|
@ -179,7 +199,8 @@ export default {
|
|||
com_ui_upload_success: 'Arquivo carregado com sucesso',
|
||||
com_ui_upload_error: 'Houve um erro ao carregar seu arquivo',
|
||||
com_ui_upload_invalid: 'Arquivo inválido para upload. Deve ser uma imagem não excedendo o limite',
|
||||
com_ui_upload_invalid_var: 'Arquivo inválido para upload. Deve ser uma imagem não excedendo {0} MB',
|
||||
com_ui_upload_invalid_var:
|
||||
'Arquivo inválido para upload. Deve ser uma imagem não excedendo {0} MB',
|
||||
com_ui_cancel: 'Cancelar',
|
||||
com_ui_save: 'Salvar',
|
||||
com_ui_renaming_var: 'Renomeando "{0}"',
|
||||
|
|
@ -192,13 +213,20 @@ export default {
|
|||
com_ui_copied_to_clipboard: 'Copiado para a área de transferência',
|
||||
com_ui_fork: 'Bifurcar',
|
||||
com_ui_fork_info_1: 'Use esta configuração para bifurcar mensagens com o comportamento desejado.',
|
||||
com_ui_fork_info_2: '"Bifurcação" refere-se à criação de uma nova conversa que começa/termina a partir de mensagens específicas na conversa atual, criando uma cópia de acordo com as opções selecionadas.',
|
||||
com_ui_fork_info_3: 'A "mensagem alvo" refere-se à mensagem da qual este popup foi aberto, ou, se você marcar "{0}", a última mensagem na conversa.',
|
||||
com_ui_fork_info_visible: 'Esta opção bifurca apenas as mensagens visíveis; em outras palavras, o caminho direto para a mensagem alvo, sem quaisquer ramificações.',
|
||||
com_ui_fork_info_branches: 'Esta opção bifurca as mensagens visíveis, junto com ramificações relacionadas; em outras palavras, o caminho direto para a mensagem alvo, incluindo ramificações ao longo do caminho.',
|
||||
com_ui_fork_info_target: 'Esta opção bifurca todas as mensagens até a mensagem alvo, incluindo seus vizinhos; em outras palavras, todos os ramos de mensagens, estejam ou não visíveis ou ao longo do mesmo caminho, estão incluídos.',
|
||||
com_ui_fork_info_start: 'Se marcado, a bifurcação começará desta mensagem até a última mensagem na conversa, de acordo com o comportamento selecionado acima.',
|
||||
com_ui_fork_info_remember: 'Marque isto para lembrar as opções que você seleciona para uso futuro, tornando mais rápido bifurcar conversas conforme preferido.',
|
||||
com_ui_fork_info_2:
|
||||
'"Bifurcação" refere-se à criação de uma nova conversa que começa/termina a partir de mensagens específicas na conversa atual, criando uma cópia de acordo com as opções selecionadas.',
|
||||
com_ui_fork_info_3:
|
||||
'A "mensagem alvo" refere-se à mensagem da qual este popup foi aberto, ou, se você marcar "{0}", a última mensagem na conversa.',
|
||||
com_ui_fork_info_visible:
|
||||
'Esta opção bifurca apenas as mensagens visíveis; em outras palavras, o caminho direto para a mensagem alvo, sem quaisquer ramificações.',
|
||||
com_ui_fork_info_branches:
|
||||
'Esta opção bifurca as mensagens visíveis, junto com ramificações relacionadas; em outras palavras, o caminho direto para a mensagem alvo, incluindo ramificações ao longo do caminho.',
|
||||
com_ui_fork_info_target:
|
||||
'Esta opção bifurca todas as mensagens até a mensagem alvo, incluindo seus vizinhos; em outras palavras, todos os ramos de mensagens, estejam ou não visíveis ou ao longo do mesmo caminho, estão incluídos.',
|
||||
com_ui_fork_info_start:
|
||||
'Se marcado, a bifurcação começará desta mensagem até a última mensagem na conversa, de acordo com o comportamento selecionado acima.',
|
||||
com_ui_fork_info_remember:
|
||||
'Marque isto para lembrar as opções que você seleciona para uso futuro, tornando mais rápido bifurcar conversas conforme preferido.',
|
||||
com_ui_fork_success: 'Conversa bifurcada com sucesso',
|
||||
com_ui_fork_processing: 'Bifurcando conversa...',
|
||||
com_ui_fork_error: 'Houve um erro ao bifurcar a conversa',
|
||||
|
|
@ -207,12 +235,14 @@ export default {
|
|||
com_ui_fork_remember: 'Lembrar',
|
||||
com_ui_fork_split_target_setting: 'Iniciar bifurcação a partir da mensagem alvo por padrão',
|
||||
com_ui_fork_split_target: 'Iniciar bifurcação aqui',
|
||||
com_ui_fork_remember_checked: 'Sua seleção será lembrada após o uso. Altere isso a qualquer momento nas configurações.',
|
||||
com_ui_fork_remember_checked:
|
||||
'Sua seleção será lembrada após o uso. Altere isso a qualquer momento nas configurações.',
|
||||
com_ui_fork_all_target: 'Incluir todos para/de aqui',
|
||||
com_ui_fork_branches: 'Incluir ramificações relacionadas',
|
||||
com_ui_fork_visible: 'Apenas mensagens visíveis',
|
||||
com_ui_fork_from_message: 'Selecione uma opção de bifurcação',
|
||||
com_ui_mention: 'Mencione um endpoint, assistente ou predefinição para alternar rapidamente para ele',
|
||||
com_ui_mention:
|
||||
'Mencione um endpoint, assistente ou predefinição para alternar rapidamente para ele',
|
||||
com_ui_add_model_preset: 'Adicionar um modelo ou predefinição para uma resposta adicional',
|
||||
com_assistants_max_starters_reached: 'Número máximo de iniciadores de conversa atingido',
|
||||
com_ui_regenerate: 'Regenerar',
|
||||
|
|
@ -280,10 +310,14 @@ export default {
|
|||
com_ui_share_error: 'Houve um erro ao compartilhar o link do chat',
|
||||
com_ui_share_retrieve_error: 'Houve um erro ao recuperar os links compartilhados',
|
||||
com_ui_share_delete_error: 'Houve um erro ao excluir o link compartilhado',
|
||||
com_ui_share_create_message: 'Seu nome e quaisquer mensagens que você adicionar após o compartilhamento permanecerão privadas.',
|
||||
com_ui_share_created_message: 'Um link compartilhado para o seu chat foi criado. Gerencie chats compartilhados anteriormente a qualquer momento via Configurações.',
|
||||
com_ui_share_update_message: 'Seu nome, instruções personalizadas e quaisquer mensagens que você adicionar após o compartilhamento permanecerão privadas.',
|
||||
com_ui_share_updated_message: 'Um link compartilhado para o seu chat foi atualizado. Gerencie chats compartilhados anteriormente a qualquer momento via Configurações.',
|
||||
com_ui_share_create_message:
|
||||
'Seu nome e quaisquer mensagens que você adicionar após o compartilhamento permanecerão privadas.',
|
||||
com_ui_share_created_message:
|
||||
'Um link compartilhado para o seu chat foi criado. Gerencie chats compartilhados anteriormente a qualquer momento via Configurações.',
|
||||
com_ui_share_update_message:
|
||||
'Seu nome, instruções personalizadas e quaisquer mensagens que você adicionar após o compartilhamento permanecerão privadas.',
|
||||
com_ui_share_updated_message:
|
||||
'Um link compartilhado para o seu chat foi atualizado. Gerencie chats compartilhados anteriormente a qualquer momento via Configurações.',
|
||||
com_ui_shared_link_not_found: 'Link compartilhado não encontrado',
|
||||
com_ui_delete_conversation: 'Excluir chat?',
|
||||
com_ui_delete_confirm: 'Isso excluirá',
|
||||
|
|
@ -291,8 +325,10 @@ export default {
|
|||
com_ui_delete_tool_confirm: 'Tem certeza de que deseja excluir esta ferramenta?',
|
||||
com_ui_delete_action: 'Excluir Ação',
|
||||
com_ui_delete_action_confirm: 'Tem certeza de que deseja excluir esta ação?',
|
||||
com_ui_delete_confirm_prompt_version_var: 'Isso excluirá a versão selecionada para "{0}". Se não houver outras versões, o prompt será excluído.',
|
||||
com_ui_delete_assistant_confirm: 'Tem certeza de que deseja excluir este Assistente? Isso não pode ser desfeito.',
|
||||
com_ui_delete_confirm_prompt_version_var:
|
||||
'Isso excluirá a versão selecionada para "{0}". Se não houver outras versões, o prompt será excluído.',
|
||||
com_ui_delete_assistant_confirm:
|
||||
'Tem certeza de que deseja excluir este Assistente? Isso não pode ser desfeito.',
|
||||
com_ui_rename: 'Renomear',
|
||||
com_ui_archive: 'Arquivar',
|
||||
com_ui_archive_error: 'Falha ao arquivar conversa',
|
||||
|
|
@ -303,7 +339,8 @@ export default {
|
|||
com_ui_upload: 'Carregar',
|
||||
com_ui_connect: 'Conectar',
|
||||
com_ui_locked: 'Bloqueado',
|
||||
com_ui_upload_delay: 'O upload de "{0}" está demorando mais do que o esperado. Por favor, aguarde enquanto o arquivo termina de ser indexado para recuperação.',
|
||||
com_ui_upload_delay:
|
||||
'O upload de "{0}" está demorando mais do que o esperado. Por favor, aguarde enquanto o arquivo termina de ser indexado para recuperação.',
|
||||
com_ui_privacy_policy: 'Política de Privacidade',
|
||||
com_ui_terms_of_service: 'Termos de Serviço',
|
||||
com_ui_use_micrphone: 'Usar microfone',
|
||||
|
|
@ -324,13 +361,19 @@ export default {
|
|||
com_ui_bookmarks_delete_error: 'Houve um erro ao excluir o favorito',
|
||||
com_ui_bookmarks_add_to_conversation: 'Adicionar à conversa atual',
|
||||
com_ui_bookmarks_filter: 'Filtrar favoritos...',
|
||||
com_ui_no_bookmarks: 'Parece que você ainda não tem favoritos. Clique em um chat e adicione um novo',
|
||||
com_ui_no_bookmarks:
|
||||
'Parece que você ainda não tem favoritos. Clique em um chat e adicione um novo',
|
||||
com_ui_no_conversation_id: 'Nenhum ID de conversa encontrado',
|
||||
com_auth_error_login: 'Não foi possível fazer login com as informações fornecidas. Por favor, verifique suas credenciais e tente novamente.',
|
||||
com_auth_error_login_rl: 'Muitas tentativas de login em um curto período de tempo. Por favor, tente novamente mais tarde.',
|
||||
com_auth_error_login_ban: 'Sua conta foi temporariamente banida devido a violações do nosso serviço.',
|
||||
com_auth_error_login_server: 'Houve um erro interno no servidor. Por favor, aguarde alguns momentos e tente novamente.',
|
||||
com_auth_error_login_unverified: 'Sua conta não foi verificada. Por favor, verifique seu e-mail para um link de verificação.',
|
||||
com_auth_error_login:
|
||||
'Não foi possível fazer login com as informações fornecidas. Por favor, verifique suas credenciais e tente novamente.',
|
||||
com_auth_error_login_rl:
|
||||
'Muitas tentativas de login em um curto período de tempo. Por favor, tente novamente mais tarde.',
|
||||
com_auth_error_login_ban:
|
||||
'Sua conta foi temporariamente banida devido a violações do nosso serviço.',
|
||||
com_auth_error_login_server:
|
||||
'Houve um erro interno no servidor. Por favor, aguarde alguns momentos e tente novamente.',
|
||||
com_auth_error_login_unverified:
|
||||
'Sua conta não foi verificada. Por favor, verifique seu e-mail para um link de verificação.',
|
||||
com_auth_no_account: 'Não tem uma conta?',
|
||||
com_auth_sign_up: 'Inscrever-se',
|
||||
com_auth_sign_in: 'Entrar',
|
||||
|
|
@ -365,14 +408,17 @@ export default {
|
|||
com_auth_already_have_account: 'Já tem uma conta?',
|
||||
com_auth_login: 'Entrar',
|
||||
com_auth_registration_success_insecure: 'Registro bem-sucedido.',
|
||||
com_auth_registration_success_generic: 'Por favor, verifique seu e-mail para verificar seu endereço de e-mail.',
|
||||
com_auth_registration_success_generic:
|
||||
'Por favor, verifique seu e-mail para verificar seu endereço de e-mail.',
|
||||
com_auth_reset_password: 'Redefinir sua senha',
|
||||
com_auth_click: 'Clique',
|
||||
com_auth_here: 'AQUI',
|
||||
com_auth_to_reset_your_password: 'para redefinir sua senha.',
|
||||
com_auth_reset_password_link_sent: 'E-mail enviado',
|
||||
com_auth_reset_password_if_email_exists: 'Se uma conta com esse e-mail existir, um e-mail com instruções para redefinir a senha foi enviado. Certifique-se de verificar sua pasta de spam.',
|
||||
com_auth_reset_password_email_sent: 'Se o usuário estiver registrado, um e-mail será enviado para a caixa de entrada.',
|
||||
com_auth_reset_password_if_email_exists:
|
||||
'Se uma conta com esse e-mail existir, um e-mail com instruções para redefinir a senha foi enviado. Certifique-se de verificar sua pasta de spam.',
|
||||
com_auth_reset_password_email_sent:
|
||||
'Se o usuário estiver registrado, um e-mail será enviado para a caixa de entrada.',
|
||||
com_auth_reset_password_success: 'Senha redefinida com sucesso',
|
||||
com_auth_login_with_new_password: 'Agora você pode fazer login com sua nova senha.',
|
||||
com_auth_error_invalid_reset_token: 'Este token de redefinição de senha não é mais válido.',
|
||||
|
|
@ -382,7 +428,8 @@ export default {
|
|||
com_auth_welcome_back: 'Bem-vindo de volta',
|
||||
com_auth_back_to_login: 'Voltar para Login',
|
||||
com_auth_email_verification_failed: 'Falha na verificação de e-mail',
|
||||
com_auth_email_verification_rate_limited: 'Muitas solicitações. Por favor, tente novamente mais tarde',
|
||||
com_auth_email_verification_rate_limited:
|
||||
'Muitas solicitações. Por favor, tente novamente mais tarde',
|
||||
com_auth_email_verification_success: 'E-mail verificado com sucesso',
|
||||
com_auth_email_resent_success: 'E-mail de verificação reenviado com sucesso',
|
||||
com_auth_email_resent_failed: 'Falha ao reenviar e-mail de verificação',
|
||||
|
|
@ -396,8 +443,10 @@ export default {
|
|||
com_endpoint_bing_enable_sydney: 'Habilitar Sydney',
|
||||
com_endpoint_bing_to_enable_sydney: 'Para habilitar Sydney',
|
||||
com_endpoint_bing_jailbreak: 'Jailbreak',
|
||||
com_endpoint_bing_context_placeholder: 'O Bing pode usar até 7k tokens para "contexto", que pode referenciar para a conversa. O limite específico não é conhecido, mas pode ocorrer erros ao exceder 7k tokens',
|
||||
com_endpoint_bing_system_message_placeholder: 'AVISO: O uso indevido deste recurso pode resultar em BANIMENTO do uso do Bing! Clique em "Mensagem do Sistema" para instruções completas e a mensagem padrão se omitida, que é o preset "Sydney" considerado seguro.',
|
||||
com_endpoint_bing_context_placeholder:
|
||||
'O Bing pode usar até 7k tokens para "contexto", que pode referenciar para a conversa. O limite específico não é conhecido, mas pode ocorrer erros ao exceder 7k tokens',
|
||||
com_endpoint_bing_system_message_placeholder:
|
||||
'AVISO: O uso indevido deste recurso pode resultar em BANIMENTO do uso do Bing! Clique em "Mensagem do Sistema" para instruções completas e a mensagem padrão se omitida, que é o preset "Sydney" considerado seguro.',
|
||||
com_endpoint_system_message: 'Mensagem do Sistema',
|
||||
com_endpoint_message: 'Mensagem',
|
||||
com_endpoint_message_not_appendable: 'Edite sua mensagem ou Regenerar.',
|
||||
|
|
@ -411,15 +460,23 @@ export default {
|
|||
com_endpoint_token_count: 'Contagem de Tokens',
|
||||
com_endpoint_output: 'Saída',
|
||||
com_endpoint_context_tokens: 'Máximo de Tokens de Contexto',
|
||||
com_endpoint_context_info: 'O número máximo de tokens que podem ser usados para contexto. Use isso para controlar quantos tokens são enviados por solicitação. Se não especificado, usará os padrões do sistema com base no tamanho do contexto dos modelos conhecidos. Definir valores mais altos pode resultar em erros e/ou maior custo de tokens.',
|
||||
com_endpoint_google_temp: 'Valores mais altos = mais aleatório, enquanto valores mais baixos = mais focado e determinístico. Recomendamos alterar isso ou Top P, mas não ambos.',
|
||||
com_endpoint_google_topp: 'Top-p altera como o modelo seleciona tokens para saída. Os tokens são selecionados dos mais prováveis (veja o parâmetro topK) até os menos prováveis até que a soma de suas probabilidades atinja o valor top-p.',
|
||||
com_endpoint_google_topk: 'Top-k altera como o modelo seleciona tokens para saída. Um top-k de 1 significa que o token selecionado é o mais provável entre todos os tokens no vocabulário do modelo (também chamado de decodificação gananciosa), enquanto um top-k de 3 significa que o próximo token é selecionado entre os 3 tokens mais prováveis (usando temperatura).',
|
||||
com_endpoint_google_maxoutputtokens: 'Número máximo de tokens que podem ser gerados na resposta. Especifique um valor mais baixo para respostas mais curtas e um valor mais alto para respostas mais longas. Nota: os modelos podem parar antes de atingir esse máximo.',
|
||||
com_endpoint_context_info:
|
||||
'O número máximo de tokens que podem ser usados para contexto. Use isso para controlar quantos tokens são enviados por solicitação. Se não especificado, usará os padrões do sistema com base no tamanho do contexto dos modelos conhecidos. Definir valores mais altos pode resultar em erros e/ou maior custo de tokens.',
|
||||
com_endpoint_google_temp:
|
||||
'Valores mais altos = mais aleatório, enquanto valores mais baixos = mais focado e determinístico. Recomendamos alterar isso ou Top P, mas não ambos.',
|
||||
com_endpoint_google_topp:
|
||||
'Top-p altera como o modelo seleciona tokens para saída. Os tokens são selecionados dos mais prováveis (veja o parâmetro topK) até os menos prováveis até que a soma de suas probabilidades atinja o valor top-p.',
|
||||
com_endpoint_google_topk:
|
||||
'Top-k altera como o modelo seleciona tokens para saída. Um top-k de 1 significa que o token selecionado é o mais provável entre todos os tokens no vocabulário do modelo (também chamado de decodificação gananciosa), enquanto um top-k de 3 significa que o próximo token é selecionado entre os 3 tokens mais prováveis (usando temperatura).',
|
||||
com_endpoint_google_maxoutputtokens:
|
||||
'Número máximo de tokens que podem ser gerados na resposta. Especifique um valor mais baixo para respostas mais curtas e um valor mais alto para respostas mais longas. Nota: os modelos podem parar antes de atingir esse máximo.',
|
||||
com_endpoint_google_custom_name_placeholder: 'Defina um nome personalizado para o Google',
|
||||
com_endpoint_prompt_prefix_placeholder: 'Defina instruções ou contexto personalizados. Ignorado se vazio.',
|
||||
com_endpoint_instructions_assistants_placeholder: 'Substitui as instruções do assistente. Isso é útil para modificar o comportamento em uma base por execução.',
|
||||
com_endpoint_prompt_prefix_assistants_placeholder: 'Defina instruções ou contexto adicionais além das instruções principais do Assistente. Ignorado se vazio.',
|
||||
com_endpoint_prompt_prefix_placeholder:
|
||||
'Defina instruções ou contexto personalizados. Ignorado se vazio.',
|
||||
com_endpoint_instructions_assistants_placeholder:
|
||||
'Substitui as instruções do assistente. Isso é útil para modificar o comportamento em uma base por execução.',
|
||||
com_endpoint_prompt_prefix_assistants_placeholder:
|
||||
'Defina instruções ou contexto adicionais além das instruções principais do Assistente. Ignorado se vazio.',
|
||||
com_endpoint_custom_name: 'Nome Personalizado',
|
||||
com_endpoint_prompt_prefix: 'Instruções Personalizadas',
|
||||
com_endpoint_prompt_prefix_assistants: 'Instruções Adicionais',
|
||||
|
|
@ -431,23 +488,38 @@ export default {
|
|||
com_endpoint_max_output_tokens: 'Máximo de Tokens de Saída',
|
||||
com_endpoint_stop: 'Sequências de Parada',
|
||||
com_endpoint_stop_placeholder: 'Separe os valores pressionando `Enter`',
|
||||
com_endpoint_openai_max_tokens: 'Campo opcional `max_tokens`, representando o número máximo de tokens que podem ser gerados na conclusão do chat. O comprimento total dos tokens de entrada e dos tokens gerados é limitado pelo comprimento do contexto dos modelos. Você pode experimentar erros se esse número exceder o máximo de tokens de contexto.',
|
||||
com_endpoint_openai_temp: 'Valores mais altos = mais aleatório, enquanto valores mais baixos = mais focado e determinístico. Recomendamos alterar isso ou Top P, mas não ambos.',
|
||||
com_endpoint_openai_max: 'O máximo de tokens para gerar. O comprimento total dos tokens de entrada e dos tokens gerados é limitado pelo comprimento do contexto do modelo.',
|
||||
com_endpoint_openai_topp: 'Uma alternativa à amostragem com temperatura, chamada amostragem de núcleo, onde o modelo considera os resultados dos tokens com massa de probabilidade top_p. Então, 0.1 significa que apenas os tokens que compreendem os 10% principais da massa de probabilidade são considerados. Recomendamos alterar isso ou a temperatura, mas não ambos.',
|
||||
com_endpoint_openai_freq: 'Número entre -2.0 e 2.0. Valores positivos penalizam novos tokens com base em sua frequência existente no texto até agora, diminuindo a probabilidade do modelo de repetir a mesma linha literalmente.',
|
||||
com_endpoint_openai_pres: 'Número entre -2.0 e 2.0. Valores positivos penalizam novos tokens com base em sua presença no texto até agora, aumentando a probabilidade do modelo de falar sobre novos tópicos.',
|
||||
com_endpoint_openai_resend: 'Reenviar todas as imagens anexadas anteriormente. Nota: isso pode aumentar significativamente o custo de tokens e você pode experimentar erros com muitos anexos de imagem.',
|
||||
com_endpoint_openai_resend_files: 'Reenviar todos os arquivos anexados anteriormente. Nota: isso aumentará o custo de tokens e você pode experimentar erros com muitos anexos.',
|
||||
com_endpoint_openai_detail: 'A resolução para solicitações de Visão. "Baixa" é mais barata e rápida, "Alta" é mais detalhada e cara, e "Auto" escolherá automaticamente entre as duas com base na resolução da imagem.',
|
||||
com_endpoint_openai_max_tokens:
|
||||
'Campo opcional `max_tokens`, representando o número máximo de tokens que podem ser gerados na conclusão do chat. O comprimento total dos tokens de entrada e dos tokens gerados é limitado pelo comprimento do contexto dos modelos. Você pode experimentar erros se esse número exceder o máximo de tokens de contexto.',
|
||||
com_endpoint_openai_temp:
|
||||
'Valores mais altos = mais aleatório, enquanto valores mais baixos = mais focado e determinístico. Recomendamos alterar isso ou Top P, mas não ambos.',
|
||||
com_endpoint_openai_max:
|
||||
'O máximo de tokens para gerar. O comprimento total dos tokens de entrada e dos tokens gerados é limitado pelo comprimento do contexto do modelo.',
|
||||
com_endpoint_openai_topp:
|
||||
'Uma alternativa à amostragem com temperatura, chamada amostragem de núcleo, onde o modelo considera os resultados dos tokens com massa de probabilidade top_p. Então, 0.1 significa que apenas os tokens que compreendem os 10% principais da massa de probabilidade são considerados. Recomendamos alterar isso ou a temperatura, mas não ambos.',
|
||||
com_endpoint_openai_freq:
|
||||
'Número entre -2.0 e 2.0. Valores positivos penalizam novos tokens com base em sua frequência existente no texto até agora, diminuindo a probabilidade do modelo de repetir a mesma linha literalmente.',
|
||||
com_endpoint_openai_pres:
|
||||
'Número entre -2.0 e 2.0. Valores positivos penalizam novos tokens com base em sua presença no texto até agora, aumentando a probabilidade do modelo de falar sobre novos tópicos.',
|
||||
com_endpoint_openai_resend:
|
||||
'Reenviar todas as imagens anexadas anteriormente. Nota: isso pode aumentar significativamente o custo de tokens e você pode experimentar erros com muitos anexos de imagem.',
|
||||
com_endpoint_openai_resend_files:
|
||||
'Reenviar todos os arquivos anexados anteriormente. Nota: isso aumentará o custo de tokens e você pode experimentar erros com muitos anexos.',
|
||||
com_endpoint_openai_detail:
|
||||
'A resolução para solicitações de Visão. "Baixa" é mais barata e rápida, "Alta" é mais detalhada e cara, e "Auto" escolherá automaticamente entre as duas com base na resolução da imagem.',
|
||||
com_endpoint_openai_stop: 'Até 4 sequências onde a API parará de gerar mais tokens.',
|
||||
com_endpoint_openai_custom_name_placeholder: 'Defina um nome personalizado para a IA',
|
||||
com_endpoint_openai_prompt_prefix_placeholder: 'Defina instruções personalizadas para incluir na Mensagem do Sistema. Padrão: nenhuma',
|
||||
com_endpoint_anthropic_temp: 'Varia de 0 a 1. Use temperatura mais próxima de 0 para tarefas analíticas / de múltipla escolha, e mais próxima de 1 para tarefas criativas e generativas. Recomendamos alterar isso ou Top P, mas não ambos.',
|
||||
com_endpoint_anthropic_topp: 'Top-p altera como o modelo seleciona tokens para saída. Os tokens são selecionados dos mais prováveis (veja o parâmetro topK) até os menos prováveis até que a soma de suas probabilidades atinja o valor top-p.',
|
||||
com_endpoint_anthropic_topk: 'Top-k altera como o modelo seleciona tokens para saída. Um top-k de 1 significa que o token selecionado é o mais provável entre todos os tokens no vocabulário do modelo (também chamado de decodificação gananciosa), enquanto um top-k de 3 significa que o próximo token é selecionado entre os 3 tokens mais prováveis (usando temperatura).',
|
||||
com_endpoint_anthropic_maxoutputtokens: 'Número máximo de tokens que podem ser gerados na resposta. Especifique um valor mais baixo para respostas mais curtas e um valor mais alto para respostas mais longas. Nota: os modelos podem parar antes de atingir esse máximo.',
|
||||
com_endpoint_anthropic_prompt_cache: 'O cache de prompt permite reutilizar um grande contexto ou instruções em chamadas de API, reduzindo custos e latência',
|
||||
com_endpoint_openai_prompt_prefix_placeholder:
|
||||
'Defina instruções personalizadas para incluir na Mensagem do Sistema. Padrão: nenhuma',
|
||||
com_endpoint_anthropic_temp:
|
||||
'Varia de 0 a 1. Use temperatura mais próxima de 0 para tarefas analíticas / de múltipla escolha, e mais próxima de 1 para tarefas criativas e generativas. Recomendamos alterar isso ou Top P, mas não ambos.',
|
||||
com_endpoint_anthropic_topp:
|
||||
'Top-p altera como o modelo seleciona tokens para saída. Os tokens são selecionados dos mais prováveis (veja o parâmetro topK) até os menos prováveis até que a soma de suas probabilidades atinja o valor top-p.',
|
||||
com_endpoint_anthropic_topk:
|
||||
'Top-k altera como o modelo seleciona tokens para saída. Um top-k de 1 significa que o token selecionado é o mais provável entre todos os tokens no vocabulário do modelo (também chamado de decodificação gananciosa), enquanto um top-k de 3 significa que o próximo token é selecionado entre os 3 tokens mais prováveis (usando temperatura).',
|
||||
com_endpoint_anthropic_maxoutputtokens:
|
||||
'Número máximo de tokens que podem ser gerados na resposta. Especifique um valor mais baixo para respostas mais curtas e um valor mais alto para respostas mais longas. Nota: os modelos podem parar antes de atingir esse máximo.',
|
||||
com_endpoint_anthropic_prompt_cache:
|
||||
'O cache de prompt permite reutilizar um grande contexto ou instruções em chamadas de API, reduzindo custos e latência',
|
||||
com_endpoint_prompt_cache: 'Usar Cache de Prompt',
|
||||
com_endpoint_anthropic_custom_name_placeholder: 'Defina um nome personalizado para Anthropic',
|
||||
com_endpoint_frequency_penalty: 'Penalidade de Frequência',
|
||||
|
|
@ -459,15 +531,19 @@ export default {
|
|||
com_endpoint_plug_skip_completion: 'Pular Conclusão',
|
||||
com_endpoint_disabled_with_tools: 'desativado com ferramentas',
|
||||
com_endpoint_disabled_with_tools_placeholder: 'Desativado com Ferramentas Selecionadas',
|
||||
com_endpoint_plug_set_custom_instructions_for_gpt_placeholder: 'Defina instruções personalizadas para incluir na Mensagem do Sistema. Padrão: nenhuma',
|
||||
com_endpoint_plug_set_custom_instructions_for_gpt_placeholder:
|
||||
'Defina instruções personalizadas para incluir na Mensagem do Sistema. Padrão: nenhuma',
|
||||
com_endpoint_import: 'Importar',
|
||||
com_endpoint_set_custom_name: 'Defina um nome personalizado, caso você possa encontrar este preset',
|
||||
com_endpoint_set_custom_name:
|
||||
'Defina um nome personalizado, caso você possa encontrar este preset',
|
||||
com_endpoint_preset_delete_confirm: 'Tem certeza de que deseja excluir este preset?',
|
||||
com_endpoint_preset_clear_all_confirm: 'Tem certeza de que deseja excluir todos os seus presets?',
|
||||
com_endpoint_preset_import: 'Preset Importado!',
|
||||
com_endpoint_preset_import_error: 'Houve um erro ao importar seu preset. Por favor, tente novamente.',
|
||||
com_endpoint_preset_import_error:
|
||||
'Houve um erro ao importar seu preset. Por favor, tente novamente.',
|
||||
com_endpoint_preset_save_error: 'Houve um erro ao salvar seu preset. Por favor, tente novamente.',
|
||||
com_endpoint_preset_delete_error: 'Houve um erro ao excluir seu preset. Por favor, tente novamente.',
|
||||
com_endpoint_preset_delete_error:
|
||||
'Houve um erro ao excluir seu preset. Por favor, tente novamente.',
|
||||
com_endpoint_preset_default_removed: 'não é mais o preset padrão.',
|
||||
com_endpoint_preset_default_item: 'Padrão:',
|
||||
com_endpoint_preset_default_none: 'Nenhum preset padrão ativo.',
|
||||
|
|
@ -493,7 +569,8 @@ export default {
|
|||
com_endpoint_use_active_assistant: 'Usar Assistente Ativo',
|
||||
com_endpoint_assistant_model: 'Modelo de Assistente',
|
||||
com_endpoint_save_as_preset: 'Salvar Como Preset',
|
||||
com_endpoint_presets_clear_warning: 'Tem certeza de que deseja limpar todos os presets? Isso é irreversível.',
|
||||
com_endpoint_presets_clear_warning:
|
||||
'Tem certeza de que deseja limpar todos os presets? Isso é irreversível.',
|
||||
com_endpoint_not_implemented: 'Não implementado',
|
||||
com_endpoint_no_presets: 'Ainda não há presets, use o botão de configurações para criar um',
|
||||
com_endpoint_not_available: 'Nenhum endpoint disponível',
|
||||
|
|
@ -503,9 +580,11 @@ export default {
|
|||
com_endpoint_agent_model: 'Modelo de Agente (Recomendado: GPT-3.5)',
|
||||
com_endpoint_completion_model: 'Modelo de Conclusão (Recomendado: GPT-4)',
|
||||
com_endpoint_func_hover: 'Habilitar uso de Plugins como Funções OpenAI',
|
||||
com_endpoint_skip_hover: 'Habilitar pular a etapa de conclusão, que revisa a resposta final e os passos gerados',
|
||||
com_endpoint_skip_hover:
|
||||
'Habilitar pular a etapa de conclusão, que revisa a resposta final e os passos gerados',
|
||||
com_endpoint_config_key: 'Definir Chave API',
|
||||
com_endpoint_assistant_placeholder: 'Por favor, selecione um Assistente no Painel Lateral Direito',
|
||||
com_endpoint_assistant_placeholder:
|
||||
'Por favor, selecione um Assistente no Painel Lateral Direito',
|
||||
com_endpoint_config_placeholder: 'Defina sua Chave no menu do Cabeçalho para conversar.',
|
||||
com_endpoint_config_key_for: 'Definir Chave API para',
|
||||
com_endpoint_config_key_name: 'Chave',
|
||||
|
|
@ -519,22 +598,28 @@ export default {
|
|||
com_endpoint_config_google_cloud_platform: '(do Google Cloud Platform)',
|
||||
com_endpoint_config_google_api_key: 'Chave API do Google',
|
||||
com_endpoint_config_google_gemini_api: '(API Gemini)',
|
||||
com_endpoint_config_google_api_info: 'Para obter sua chave API de Linguagem Generativa (para Gemini),',
|
||||
com_endpoint_config_google_api_info:
|
||||
'Para obter sua chave API de Linguagem Generativa (para Gemini),',
|
||||
com_endpoint_config_key_import_json_key: 'Importar Chave JSON da Conta de Serviço.',
|
||||
com_endpoint_config_key_import_json_key_success: 'Chave JSON da Conta de Serviço Importada com Sucesso',
|
||||
com_endpoint_config_key_import_json_key_invalid: 'Chave JSON da Conta de Serviço Inválida, Você importou o arquivo correto?',
|
||||
com_endpoint_config_key_import_json_key_success:
|
||||
'Chave JSON da Conta de Serviço Importada com Sucesso',
|
||||
com_endpoint_config_key_import_json_key_invalid:
|
||||
'Chave JSON da Conta de Serviço Inválida, Você importou o arquivo correto?',
|
||||
com_endpoint_config_key_get_edge_key: 'Para obter seu token de acesso para o Bing, faça login em',
|
||||
com_endpoint_config_key_get_edge_key_dev_tool: 'Use ferramentas de desenvolvedor ou uma extensão enquanto estiver logado no site para copiar o conteúdo do cookie _U. Se isso falhar, siga estas',
|
||||
com_endpoint_config_key_get_edge_key_dev_tool:
|
||||
'Use ferramentas de desenvolvedor ou uma extensão enquanto estiver logado no site para copiar o conteúdo do cookie _U. Se isso falhar, siga estas',
|
||||
com_endpoint_config_key_edge_instructions: 'instruções',
|
||||
com_endpoint_config_key_edge_full_key_string: 'para fornecer as strings completas do cookie.',
|
||||
com_endpoint_config_key_chatgpt: 'Para obter seu token de acesso para o ChatGPT "Versão Gratuita", faça login em',
|
||||
com_endpoint_config_key_chatgpt:
|
||||
'Para obter seu token de acesso para o ChatGPT "Versão Gratuita", faça login em',
|
||||
com_endpoint_config_key_chatgpt_then_visit: 'depois visite',
|
||||
com_endpoint_config_key_chatgpt_copy_token: 'Copiar token de acesso.',
|
||||
com_endpoint_config_key_google_need_to: 'Você precisa',
|
||||
com_endpoint_config_key_google_vertex_ai: 'Habilitar Vertex AI',
|
||||
com_endpoint_config_key_google_vertex_api: 'API no Google Cloud, então',
|
||||
com_endpoint_config_key_google_service_account: 'Criar uma Conta de Serviço',
|
||||
com_endpoint_config_key_google_vertex_api_role: 'Certifique-se de clicar em "Criar e Continuar" para dar pelo menos o papel de "Usuário do Vertex AI". Por fim, crie uma chave JSON para importar aqui.',
|
||||
com_endpoint_config_key_google_vertex_api_role:
|
||||
'Certifique-se de clicar em "Criar e Continuar" para dar pelo menos o papel de "Usuário do Vertex AI". Por fim, crie uma chave JSON para importar aqui.',
|
||||
com_nav_account_settings: 'Configurações da Conta',
|
||||
com_nav_font_size: 'Tamanho da Fonte da Mensagem',
|
||||
com_nav_font_size_xs: 'Extra Pequeno',
|
||||
|
|
@ -573,14 +658,16 @@ export default {
|
|||
com_ui_upload_image: 'Carregar uma imagem',
|
||||
com_ui_select_a_category: 'Nenhuma categoria selecionada',
|
||||
com_ui_clear_all: 'Limpar tudo',
|
||||
com_nav_tool_dialog_description: 'O assistente deve ser salvo para persistir as seleções de ferramentas.',
|
||||
com_nav_tool_dialog_description:
|
||||
'O assistente deve ser salvo para persistir as seleções de ferramentas.',
|
||||
com_show_agent_settings: 'Mostrar Configurações do Agente',
|
||||
com_show_completion_settings: 'Mostrar Configurações de Conclusão',
|
||||
com_hide_examples: 'Ocultar Exemplos',
|
||||
com_show_examples: 'Mostrar Exemplos',
|
||||
com_nav_plugin_search: 'Buscar plugins',
|
||||
com_nav_tool_search: 'Buscar ferramentas',
|
||||
com_nav_plugin_auth_error: 'Houve um erro ao tentar autenticar este plugin. Por favor, tente novamente.',
|
||||
com_nav_plugin_auth_error:
|
||||
'Houve um erro ao tentar autenticar este plugin. Por favor, tente novamente.',
|
||||
com_nav_export_filename: 'Nome do arquivo',
|
||||
com_nav_export_filename_placeholder: 'Definir o nome do arquivo',
|
||||
com_nav_export_type: 'Tipo',
|
||||
|
|
@ -625,7 +712,8 @@ export default {
|
|||
com_nav_archive_name: 'Nome',
|
||||
com_nav_archive_created_at: 'Data de Arquivamento',
|
||||
com_nav_clear_conversation: 'Limpar conversas',
|
||||
com_nav_clear_conversation_confirm_message: 'Tem certeza de que deseja limpar todas as conversas? Isso é irreversível.',
|
||||
com_nav_clear_conversation_confirm_message:
|
||||
'Tem certeza de que deseja limpar todas as conversas? Isso é irreversível.',
|
||||
com_nav_help_faq: 'Ajuda & FAQ',
|
||||
com_nav_settings: 'Configurações',
|
||||
com_nav_search_placeholder: 'Buscar mensagens',
|
||||
|
|
@ -646,12 +734,18 @@ export default {
|
|||
com_nav_audio_process_error: 'Erro ao processar áudio: {0}',
|
||||
com_nav_long_audio_warning: 'Textos mais longos levarão mais tempo para processar.',
|
||||
com_nav_tts_init_error: 'Falha ao inicializar texto-para-fala: {0}',
|
||||
com_nav_tts_unsupported_error: 'Texto-para-fala para o mecanismo selecionado não é suportado neste navegador.',
|
||||
com_nav_source_buffer_error: 'Erro ao configurar a reprodução de áudio. Por favor, atualize a página.',
|
||||
com_nav_media_source_init_error: 'Não foi possível preparar o reprodutor de áudio. Por favor, verifique as configurações do seu navegador.',
|
||||
com_nav_buffer_append_error: 'Problema com o streaming de áudio. A reprodução pode ser interrompida.',
|
||||
com_nav_speech_cancel_error: 'Não foi possível parar a reprodução de áudio. Você pode precisar atualizar a página.',
|
||||
com_nav_voices_fetch_error: 'Não foi possível recuperar as opções de voz. Por favor, verifique sua conexão com a internet.',
|
||||
com_nav_tts_unsupported_error:
|
||||
'Texto-para-fala para o mecanismo selecionado não é suportado neste navegador.',
|
||||
com_nav_source_buffer_error:
|
||||
'Erro ao configurar a reprodução de áudio. Por favor, atualize a página.',
|
||||
com_nav_media_source_init_error:
|
||||
'Não foi possível preparar o reprodutor de áudio. Por favor, verifique as configurações do seu navegador.',
|
||||
com_nav_buffer_append_error:
|
||||
'Problema com o streaming de áudio. A reprodução pode ser interrompida.',
|
||||
com_nav_speech_cancel_error:
|
||||
'Não foi possível parar a reprodução de áudio. Você pode precisar atualizar a página.',
|
||||
com_nav_voices_fetch_error:
|
||||
'Não foi possível recuperar as opções de voz. Por favor, verifique sua conexão com a internet.',
|
||||
com_nav_engine: 'Motor',
|
||||
com_nav_browser: 'Navegador',
|
||||
com_nav_edge: 'Edge',
|
||||
|
|
@ -660,20 +754,30 @@ export default {
|
|||
com_nav_enable_cache_tts: 'Habilitar cache TTS',
|
||||
com_nav_voice_select: 'Voz',
|
||||
com_nav_enable_cloud_browser_voice: 'Usar vozes baseadas na nuvem',
|
||||
com_nav_info_enter_to_send: 'Quando habilitado, pressionar `ENTER` enviará sua mensagem. Quando desabilitado, pressionar Enter adicionará uma nova linha, e você precisará pressionar `CTRL + ENTER` para enviar sua mensagem.',
|
||||
com_nav_info_save_draft: 'Quando habilitado, o texto e os anexos que você inserir no formulário de chat serão salvos automaticamente localmente como rascunhos. Esses rascunhos estarão disponíveis mesmo se você recarregar a página ou mudar para uma conversa diferente. Os rascunhos são armazenados localmente no seu dispositivo e são excluídos uma vez que a mensagem é enviada.',
|
||||
com_nav_info_fork_change_default: '`Apenas mensagens visíveis` inclui apenas o caminho direto para a mensagem selecionada. `Incluir ramos relacionados` adiciona ramos ao longo do caminho. `Incluir tudo de/para aqui` inclui todas as mensagens e ramos conectados.',
|
||||
com_nav_info_fork_split_target_setting: 'Quando habilitado, a bifurcação começará da mensagem alvo até a última mensagem na conversa, de acordo com o comportamento selecionado.',
|
||||
com_nav_info_user_name_display: 'Quando habilitado, o nome de usuário do remetente será mostrado acima de cada mensagem que você enviar. Quando desabilitado, você verá apenas "Você" acima de suas mensagens.',
|
||||
com_nav_info_latex_parsing: 'Quando habilitado, o código LaTeX nas mensagens será renderizado como equações matemáticas. Desabilitar isso pode melhorar o desempenho se você não precisar de renderização LaTeX.',
|
||||
com_nav_info_revoke: 'Esta ação revogará e removerá todas as chaves de API que você forneceu. Você precisará reentrar essas credenciais para continuar usando esses endpoints.',
|
||||
com_nav_info_delete_cache_storage: 'Esta ação excluirá todos os arquivos de áudio TTS (Texto-para-Fala) armazenados em cache no seu dispositivo. Arquivos de áudio em cache são usados para acelerar a reprodução de TTS gerado anteriormente, mas podem consumir espaço de armazenamento no seu dispositivo.',
|
||||
com_nav_info_enter_to_send:
|
||||
'Quando habilitado, pressionar `ENTER` enviará sua mensagem. Quando desabilitado, pressionar Enter adicionará uma nova linha, e você precisará pressionar `CTRL + ENTER` para enviar sua mensagem.',
|
||||
com_nav_info_save_draft:
|
||||
'Quando habilitado, o texto e os anexos que você inserir no formulário de chat serão salvos automaticamente localmente como rascunhos. Esses rascunhos estarão disponíveis mesmo se você recarregar a página ou mudar para uma conversa diferente. Os rascunhos são armazenados localmente no seu dispositivo e são excluídos uma vez que a mensagem é enviada.',
|
||||
com_nav_info_fork_change_default:
|
||||
'`Apenas mensagens visíveis` inclui apenas o caminho direto para a mensagem selecionada. `Incluir ramos relacionados` adiciona ramos ao longo do caminho. `Incluir tudo de/para aqui` inclui todas as mensagens e ramos conectados.',
|
||||
com_nav_info_fork_split_target_setting:
|
||||
'Quando habilitado, a bifurcação começará da mensagem alvo até a última mensagem na conversa, de acordo com o comportamento selecionado.',
|
||||
com_nav_info_user_name_display:
|
||||
'Quando habilitado, o nome de usuário do remetente será mostrado acima de cada mensagem que você enviar. Quando desabilitado, você verá apenas "Você" acima de suas mensagens.',
|
||||
com_nav_info_latex_parsing:
|
||||
'Quando habilitado, o código LaTeX nas mensagens será renderizado como equações matemáticas. Desabilitar isso pode melhorar o desempenho se você não precisar de renderização LaTeX.',
|
||||
com_nav_info_revoke:
|
||||
'Esta ação revogará e removerá todas as chaves de API que você forneceu. Você precisará reentrar essas credenciais para continuar usando esses endpoints.',
|
||||
com_nav_info_delete_cache_storage:
|
||||
'Esta ação excluirá todos os arquivos de áudio TTS (Texto-para-Fala) armazenados em cache no seu dispositivo. Arquivos de áudio em cache são usados para acelerar a reprodução de TTS gerado anteriormente, mas podem consumir espaço de armazenamento no seu dispositivo.',
|
||||
com_nav_commands: 'Comandos',
|
||||
com_nav_commands_tab: 'Configurações de Comando',
|
||||
com_nav_at_command: 'Comando @',
|
||||
com_nav_at_command_description: 'Alternar comando "@" para alternar endpoints, modelos, predefinições, etc.',
|
||||
com_nav_at_command_description:
|
||||
'Alternar comando "@" para alternar endpoints, modelos, predefinições, etc.',
|
||||
com_nav_plus_command: 'Comando +',
|
||||
com_nav_plus_command_description: 'Alternar comando "+" para adicionar uma configuração de resposta múltipla',
|
||||
com_nav_plus_command_description:
|
||||
'Alternar comando "+" para adicionar uma configuração de resposta múltipla',
|
||||
com_nav_slash_command: 'Comando /',
|
||||
com_nav_slash_command_description: 'Alternar comando "/" para selecionar um prompt via teclado',
|
||||
com_nav_command_settings: 'Configurações de Comando',
|
||||
|
|
@ -770,7 +874,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Pesquisar assistentes por nome',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Ferramentas',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: 'Die folgenden Dateien sind nur für den Code-Interpreter:',
|
||||
com_assistants_retrieval: 'Abruf',
|
||||
com_assistants_search_name: 'Assistenten nach Namen suchen',
|
||||
com_assistants_tools: 'Werkzeuge',
|
||||
com_ui_tools: 'Werkzeuge',
|
||||
com_assistants_actions: 'Aktionen',
|
||||
com_assistants_add_tools: 'Werkzeuge hinzufügen',
|
||||
com_assistants_add_actions: 'Aktionen hinzufügen',
|
||||
|
|
@ -840,7 +840,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Assistenten nach Namen suchen',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Werkzeuge',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ export default {
|
|||
'The latest message token count is too long, exceeding the token limit ({0} respectively). Please shorten your message, adjust the max context size from the conversation parameters, or fork the conversation to continue.',
|
||||
com_files_no_results: 'No results.',
|
||||
com_files_filter: 'Filter files...',
|
||||
com_generated_files: 'Generated files:',
|
||||
com_download_expired: '(download expired)',
|
||||
com_download_expires: '(click here to download - expires {0})',
|
||||
com_click_to_download: '(click here to download)',
|
||||
com_files_number_selected: '{0} of {1} file(s) selected',
|
||||
com_sidepanel_select_assistant: 'Select an Assistant',
|
||||
com_sidepanel_parameters: 'Parameters',
|
||||
|
|
@ -59,7 +63,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: 'Files below are for Code Interpreter only:',
|
||||
com_assistants_retrieval: 'Retrieval',
|
||||
com_assistants_search_name: 'Search assistants by name',
|
||||
com_assistants_tools: 'Tools',
|
||||
com_ui_tools: 'Tools',
|
||||
com_assistants_actions: 'Actions',
|
||||
com_assistants_add_tools: 'Add Tools',
|
||||
com_assistants_add_actions: 'Add Actions',
|
||||
|
|
@ -93,6 +97,18 @@ export default {
|
|||
com_agents_search_name: 'Search agents by name',
|
||||
com_agents_update_error: 'There was an error updating your agent.',
|
||||
com_agents_create_error: 'There was an error creating your agent.',
|
||||
com_agents_missing_provider_model: 'Please select a provider and model before creating an agent.',
|
||||
com_agents_allow_editing: 'Allow other users to edit your agent',
|
||||
com_agents_not_available: 'Agent Not Available',
|
||||
com_agents_no_access: 'You don\'t have access to edit this agent.',
|
||||
com_agents_enable_file_search: 'Enable File Search',
|
||||
com_agents_file_search_info:
|
||||
'When enabled, the agent will be informed of the exact filenames listed below, allowing it to retrieve relevant context from these files.',
|
||||
com_agents_file_search_disabled: 'Agent must be created before uploading files for File Search.',
|
||||
com_agents_execute_code: 'Run Code',
|
||||
com_ui_agent_already_shared_to_all: 'This agent is already shared to all users',
|
||||
com_ui_agent_editing_allowed: 'Other users can already edit this agent',
|
||||
com_ui_no_changes: 'No changes to update',
|
||||
com_ui_date_today: 'Today',
|
||||
com_ui_date_yesterday: 'Yesterday',
|
||||
com_ui_date_previous_7_days: 'Previous 7 days',
|
||||
|
|
@ -639,6 +655,7 @@ export default {
|
|||
com_nav_plugin_uninstall: 'Uninstall',
|
||||
com_ui_add: 'Add',
|
||||
com_nav_tool_remove: 'Remove',
|
||||
com_nav_tool_dialog_agents: 'Agent Tools',
|
||||
com_nav_tool_dialog: 'Assistant Tools',
|
||||
com_ui_misc: 'Misc.',
|
||||
com_ui_roleplay: 'Roleplay',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export default {
|
|||
'Los siguientes archivos solo están disponibles para el Intérprete de Código:',
|
||||
com_assistants_retrieval: 'Recuperación',
|
||||
com_assistants_search_name: 'Buscar asistentes por nombre',
|
||||
com_assistants_tools: 'Herramientas',
|
||||
com_ui_tools: 'Herramientas',
|
||||
com_assistants_actions: 'Acciones',
|
||||
com_assistants_add_tools: 'Añadir Herramientas',
|
||||
com_assistants_add_actions: 'Añadir Acciones',
|
||||
|
|
@ -697,7 +697,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Buscar asistentes por nombre',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Herramientas',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: 'Seuraavat tiedostot ovat vain Kooditulkin käytettävissä:',
|
||||
com_assistants_retrieval: 'Tiedonhaku',
|
||||
com_assistants_search_name: 'Hae Avustajia nimen perusteella',
|
||||
com_assistants_tools: 'Työkalut',
|
||||
com_ui_tools: 'Työkalut',
|
||||
com_assistants_actions: 'Toiminnot',
|
||||
com_assistants_add_tools: 'Lisää Työkaluja',
|
||||
com_assistants_add_actions: 'Lisää Toimintoja',
|
||||
|
|
|
|||
|
|
@ -483,7 +483,7 @@ export default {
|
|||
'Les fichiers suivants sont disponibles uniquement pour l\'interpréteur de code :',
|
||||
com_assistants_retrieval: 'Récupération',
|
||||
com_assistants_search_name: 'Rechercher des assistants par nom',
|
||||
com_assistants_tools: 'Outils',
|
||||
com_ui_tools: 'Outils',
|
||||
com_assistants_actions: 'Actions',
|
||||
com_assistants_add_tools: 'Ajouter des outils',
|
||||
com_assistants_add_actions: 'Ajouter des actions',
|
||||
|
|
@ -2434,7 +2434,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Rechercher des assistants par nom',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Outils',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default {
|
|||
'I seguenti file sono disponibili solo per Code Interpreter:',
|
||||
com_assistants_retrieval: 'Retrival',
|
||||
com_assistants_search_name: 'Cerca assistenti per nome',
|
||||
com_assistants_tools: 'Strumenti',
|
||||
com_ui_tools: 'Strumenti',
|
||||
com_assistants_actions: 'Azioni',
|
||||
com_assistants_add_tools: 'Aggiungi Strumenti',
|
||||
com_assistants_add_actions: 'Aggiungi Azioni',
|
||||
|
|
@ -727,7 +727,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Cerca assistenti per nome',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Strumenti',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ export default {
|
|||
com_nav_convo_menu_options: '会話メニューオプション',
|
||||
com_ui_artifacts: 'アーティファクト',
|
||||
com_ui_artifacts_toggle: 'アーティファクト UI の切替',
|
||||
com_nav_info_code_artifacts:
|
||||
'チャットの横に実験的なコード アーティファクトの表示を有効にします',
|
||||
com_nav_info_code_artifacts: 'チャットの横に実験的なコード アーティファクトの表示を有効にします',
|
||||
com_ui_include_shadcnui: 'shadcn/uiコンポーネントの指示を含める',
|
||||
com_nav_info_include_shadcnui:
|
||||
'有効にすると、shadcn/uiコンポーネントを使用するための指示が含まれます。shadcn/uiはRadix UIとTailwind CSSを使用して構築された再利用可能なコンポーネントのコレクションです。注:これらの指示は長文ですので、LLM に正しいインポートとコンポーネントを知らせることが重要でない限り、有効にしないでください。これらのコンポーネントの詳細については、https://ui.shadcn.com/をご覧ください。',
|
||||
|
|
@ -53,7 +52,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: '次のファイルはコードインタプリタでのみ使用できます。',
|
||||
com_assistants_retrieval: '検索',
|
||||
com_assistants_search_name: 'アシスタントの名前で検索',
|
||||
com_assistants_tools: 'ツール',
|
||||
com_ui_tools: 'ツール',
|
||||
com_assistants_actions: 'アクション',
|
||||
com_assistants_add_tools: 'ツールを追加',
|
||||
com_assistants_add_actions: 'アクションを追加',
|
||||
|
|
@ -69,7 +68,8 @@ export default {
|
|||
com_assistants_update_actions_success: 'アクションが作成または更新されました',
|
||||
com_assistants_update_actions_error: 'アクションの作成または更新中にエラーが発生しました。',
|
||||
com_assistants_delete_actions_error: 'アクションの削除中にエラーが発生しました。',
|
||||
com_assistants_actions_info: 'アシスタントが API を介して情報を取得したり、アクションを実行したりできるようにします\'s',
|
||||
com_assistants_actions_info:
|
||||
'アシスタントが API を介して情報を取得したり、アクションを実行したりできるようにします\'s',
|
||||
com_assistants_name_placeholder: 'オプション: アシスタントの名前',
|
||||
com_assistants_instructions_placeholder: 'アシスタントが使用するシステム指示',
|
||||
com_assistants_description_placeholder: 'オプション: ここにアシスタントについて説明します',
|
||||
|
|
@ -104,10 +104,12 @@ export default {
|
|||
com_ui_date_november: '11月',
|
||||
com_ui_date_december: '12月',
|
||||
com_ui_field_required: '必須入力項目です',
|
||||
com_ui_download_error: 'ファイルのダウンロード中にエラーが発生しました。ファイルが削除された可能性があります。',
|
||||
com_ui_download_error:
|
||||
'ファイルのダウンロード中にエラーが発生しました。ファイルが削除された可能性があります。',
|
||||
com_ui_attach_error_type: 'エンドポイントでサポートされていないファイルタイプ:',
|
||||
com_ui_attach_error_openai: '他のエンドポイントにアシスタントファイルを添付することはできません',
|
||||
com_ui_attach_warn_endpoint: '互換性のあるツールがない場合、非アシスタントのファイルは無視される可能性があります',
|
||||
com_ui_attach_warn_endpoint:
|
||||
'互換性のあるツールがない場合、非アシスタントのファイルは無視される可能性があります',
|
||||
com_ui_attach_error_size: 'エンドポイントのファイルサイズ制限を超えました:',
|
||||
com_ui_attach_error:
|
||||
'ファイルを添付できません。会話を作成または選択するか、ページを更新してみてください。',
|
||||
|
|
@ -193,8 +195,10 @@ export default {
|
|||
com_ui_none_selected: '選択されていません',
|
||||
com_ui_upload_success: 'アップロード成功',
|
||||
com_ui_upload_error: 'ファイルのアップロード中にエラーが発生しました。',
|
||||
com_ui_upload_invalid: 'アップロードに無効なファイルです。制限を超えない画像である必要があります。',
|
||||
com_ui_upload_invalid_var: 'アップロードに無効なファイルです。 {0} MBまでの画像である必要があります。',
|
||||
com_ui_upload_invalid:
|
||||
'アップロードに無効なファイルです。制限を超えない画像である必要があります。',
|
||||
com_ui_upload_invalid_var:
|
||||
'アップロードに無効なファイルです。 {0} MBまでの画像である必要があります。',
|
||||
com_ui_cancel: 'キャンセル',
|
||||
com_ui_save: '保存',
|
||||
com_ui_renaming_var: '改名 "{0}"',
|
||||
|
|
@ -235,7 +239,8 @@ export default {
|
|||
com_ui_fork_branches: '関連ブランチを含める',
|
||||
com_ui_fork_visible: '表示メッセージのみ',
|
||||
com_ui_fork_from_message: '分岐オプションを選択する',
|
||||
com_ui_mention: 'エンドポイント、アシスタント、またはプリセットを素早く切り替えるには、それらを言及してください。',
|
||||
com_ui_mention:
|
||||
'エンドポイント、アシスタント、またはプリセットを素早く切り替えるには、それらを言及してください。',
|
||||
com_ui_add_model_preset: '追加の応答のためのモデルまたはプリセットを追加する',
|
||||
com_assistants_max_starters_reached: '会話の開始が最大数に達しました',
|
||||
com_ui_regenerate: '再度 生成する',
|
||||
|
|
@ -303,7 +308,8 @@ export default {
|
|||
com_ui_share_error: 'チャットの共有リンクの共有中にエラーが発生しました',
|
||||
com_ui_share_retrieve_error: '共有リンクの削除中にエラーが発生しました。',
|
||||
com_ui_share_delete_error: '共有リンクの削除中にエラーが発生しました。',
|
||||
com_ui_share_create_message: 'あなたの名前と共有リンクを作成した後のメッセージは、共有されません。',
|
||||
com_ui_share_create_message:
|
||||
'あなたの名前と共有リンクを作成した後のメッセージは、共有されません。',
|
||||
com_ui_share_created_message:
|
||||
'チャットの共有リンクが作成されました。設定から以前共有したチャットを管理できます。',
|
||||
com_ui_share_update_message:
|
||||
|
|
@ -319,8 +325,7 @@ export default {
|
|||
com_ui_delete_action_confirm: 'このアクションを削除してもよろしいですか?',
|
||||
com_ui_delete_confirm_prompt_version_var:
|
||||
'これは、選択されたバージョンを "{0}." から削除します。他のバージョンが存在しない場合、プロンプトが削除されます。',
|
||||
com_ui_delete_assistant_confirm:
|
||||
'このアシスタントを削除しますか? この操作は元に戻せません。',
|
||||
com_ui_delete_assistant_confirm: 'このアシスタントを削除しますか? この操作は元に戻せません。',
|
||||
com_ui_rename: 'タイトル変更',
|
||||
com_ui_archive: 'アーカイブ',
|
||||
com_ui_archive_error: 'アーカイブに失敗しました。',
|
||||
|
|
@ -353,14 +358,14 @@ export default {
|
|||
com_ui_bookmarks_delete_error: 'ブックマークの削除中にエラーが発生しました',
|
||||
com_ui_bookmarks_add_to_conversation: '現在の会話に追加',
|
||||
com_ui_bookmarks_filter: 'ブックマークをフィルタリング...',
|
||||
com_ui_no_bookmarks: 'ブックマークがまだないようです。チャットをクリックして新しいブックマークを追加してください',
|
||||
com_ui_no_bookmarks:
|
||||
'ブックマークがまだないようです。チャットをクリックして新しいブックマークを追加してください',
|
||||
com_ui_no_conversation_id: '会話 ID が見つかりません',
|
||||
com_auth_error_login:
|
||||
'入力された情報ではログインできませんでした。認証情報を確認した上で再度お試しください。',
|
||||
com_auth_error_login_rl:
|
||||
'お使いのIPアドレスから短時間に多数のログイン試行がありました。しばらくしてから再度お試しください。',
|
||||
com_auth_error_login_ban:
|
||||
'本サービスの利用規約違反のため、一時的にアカウントを停止しました。',
|
||||
com_auth_error_login_ban: '本サービスの利用規約違反のため、一時的にアカウントを停止しました。',
|
||||
com_auth_error_login_server:
|
||||
'サーバーエラーが発生しています。。しばらくしてから再度お試しください。',
|
||||
com_auth_error_login_unverified:
|
||||
|
|
@ -387,8 +392,7 @@ export default {
|
|||
com_auth_password_not_match: 'パスワードが一致しません',
|
||||
com_auth_continue: '続ける',
|
||||
com_auth_create_account: 'アカウント登録',
|
||||
com_auth_error_create:
|
||||
'アカウント登録に失敗しました。もう一度お試しください。',
|
||||
com_auth_error_create: 'アカウント登録に失敗しました。もう一度お試しください。',
|
||||
com_auth_full_name: 'フルネーム',
|
||||
com_auth_name_required: 'フルネームは必須です',
|
||||
com_auth_name_min_length: 'フルネームは最低3文字で入力してください',
|
||||
|
|
@ -408,8 +412,7 @@ export default {
|
|||
com_auth_reset_password_link_sent: 'メールを送信',
|
||||
com_auth_reset_password_if_email_exists:
|
||||
'そのメールアドレスのアカウントが存在する場合は、パスワードリセット手順が記載されたメールが送信されています。スパムフォルダを必ず確認してください。',
|
||||
com_auth_reset_password_email_sent:
|
||||
'パスワードのリセット方法を記載したメールを送信しました。',
|
||||
com_auth_reset_password_email_sent: 'パスワードのリセット方法を記載したメールを送信しました。',
|
||||
com_auth_reset_password_success: 'パスワードのリセットに成功しました',
|
||||
com_auth_login_with_new_password: '新しいパスワードでログインをお試しください。',
|
||||
com_auth_error_invalid_reset_token: '無効なパスワードリセットトークンです。',
|
||||
|
|
@ -419,7 +422,8 @@ export default {
|
|||
com_auth_welcome_back: 'おかえりなさい',
|
||||
com_auth_back_to_login: 'ログイン画面に戻る',
|
||||
com_auth_email_verification_failed: 'メール検証に失敗しました',
|
||||
com_auth_email_verification_rate_limited: 'リクエストが多すぎます。しばらくしてからもう一度お試しください',
|
||||
com_auth_email_verification_rate_limited:
|
||||
'リクエストが多すぎます。しばらくしてからもう一度お試しください',
|
||||
com_auth_email_verification_success: 'メールが正常に検証されました',
|
||||
com_auth_email_resent_success: '検証メールが正常に再送信されました',
|
||||
com_auth_email_resent_failed: '検証メールの再送信に失敗しました',
|
||||
|
|
@ -461,7 +465,8 @@ export default {
|
|||
com_endpoint_google_maxoutputtokens:
|
||||
' 生成されるレスポンスの最大トークン数。短いレスポンスには低い値を、長いレスポンスには高い値を指定します。',
|
||||
com_endpoint_google_custom_name_placeholder: 'Googleのカスタム名を設定する',
|
||||
com_endpoint_prompt_prefix_placeholder: 'custom instructions か context を設定する。空の場合は無視されます。',
|
||||
com_endpoint_prompt_prefix_placeholder:
|
||||
'custom instructions か context を設定する。空の場合は無視されます。',
|
||||
com_endpoint_instructions_assistants_placeholder:
|
||||
'アシスタントの指示を上書きします。これは、実行ごとに動作を変更する場合に便利です。',
|
||||
com_endpoint_prompt_prefix_assistants_placeholder:
|
||||
|
|
@ -496,7 +501,8 @@ export default {
|
|||
'以前に添付されたすべてのファイルを再送信します。注意:これにより、トークンのコストが増加し、多くの添付ファイルでエラーが発生する可能性があります。',
|
||||
com_endpoint_openai_detail:
|
||||
'Visionリクエストの解像度を選択します。"Low"はコストが安くて低解像度、"Highは"コストが高くて高解像度"、"Auto"は画像の解像度に基づいて自動的に選択します。',
|
||||
com_endpoint_openai_stop: 'APIがさらにトークンを生成するのを止めるため、最大で4つのシーケンスを設定可能',
|
||||
com_endpoint_openai_stop:
|
||||
'APIがさらにトークンを生成するのを止めるため、最大で4つのシーケンスを設定可能',
|
||||
com_endpoint_openai_custom_name_placeholder: 'ChatGPTのカスタム名を設定する',
|
||||
com_endpoint_openai_prompt_prefix_placeholder:
|
||||
'システムメッセージに含める Custom Instructions。デフォルト: none',
|
||||
|
|
@ -586,15 +592,18 @@ export default {
|
|||
com_endpoint_config_google_gemini_api: '(Gemini API)',
|
||||
com_endpoint_config_google_api_info: 'Gemeni用のGenerative Language API keyを取得するには',
|
||||
com_endpoint_config_key_import_json_key: 'Service Account JSON Key をインポートする。',
|
||||
com_endpoint_config_key_import_json_key_success: 'Service Account JSON Keyのインポートに成功しました。',
|
||||
com_endpoint_config_key_import_json_key_success:
|
||||
'Service Account JSON Keyのインポートに成功しました。',
|
||||
com_endpoint_config_key_import_json_key_invalid:
|
||||
'無効なService Account JSON Keyです。正しいファイルかどうか確認してください。',
|
||||
com_endpoint_config_key_get_edge_key: 'Bing用のアクセストークンを取得するためにログインをしてください: ',
|
||||
com_endpoint_config_key_get_edge_key:
|
||||
'Bing用のアクセストークンを取得するためにログインをしてください: ',
|
||||
com_endpoint_config_key_get_edge_key_dev_tool:
|
||||
'サイトにログインした状態で、開発ツールまたは拡張機能を使用して、_U クッキーの内容をコピーします。もし失敗する場合は次の手順に従ってください。',
|
||||
com_endpoint_config_key_edge_instructions: '手順',
|
||||
com_endpoint_config_key_edge_full_key_string: 'to provide the full cookie strings.',
|
||||
com_endpoint_config_key_chatgpt: 'ChatGPTの「無料版」のアクセストークンを入手するためにへログインをしてください:',
|
||||
com_endpoint_config_key_chatgpt:
|
||||
'ChatGPTの「無料版」のアクセストークンを入手するためにへログインをしてください:',
|
||||
com_endpoint_config_key_chatgpt_then_visit: 'つぎに、ここへアクセスしてください:',
|
||||
com_endpoint_config_key_chatgpt_copy_token: 'トークンをコピーしてください。',
|
||||
com_endpoint_config_key_google_need_to: 'こちらを有効化する必要があります:',
|
||||
|
|
@ -615,7 +624,8 @@ export default {
|
|||
com_nav_auto_scroll: 'チャットを開いたときに最新まで自動でスクロール',
|
||||
com_nav_hide_panel: '右側のパネルを非表示',
|
||||
com_nav_modular_chat: '会話の途中でのエンドポイント切替を有効化',
|
||||
com_nav_latex_parsing: 'メッセージ内の LaTeX の構文解析 (パフォーマンスに影響する可能性があります)',
|
||||
com_nav_latex_parsing:
|
||||
'メッセージ内の LaTeX の構文解析 (パフォーマンスに影響する可能性があります)',
|
||||
com_nav_text_to_speech: 'テキスト読み上げ',
|
||||
com_nav_automatic_playback: '最新メッセージを自動再生',
|
||||
com_nav_speech_to_text: '音声テキスト変換',
|
||||
|
|
@ -641,7 +651,8 @@ export default {
|
|||
com_ui_upload_image: '画像をアップロード',
|
||||
com_ui_select_a_category: 'カテゴリ未選択',
|
||||
com_ui_clear_all: 'すべてクリア',
|
||||
com_nav_tool_dialog_description: 'ツールの選択を維持するには、アシスタントを保存する必要があります。',
|
||||
com_nav_tool_dialog_description:
|
||||
'ツールの選択を維持するには、アシスタントを保存する必要があります。',
|
||||
com_show_agent_settings: 'エージェント設定を表示',
|
||||
com_show_completion_settings: 'コンプリーション設定を表示',
|
||||
com_hide_examples: '例を非表示',
|
||||
|
|
@ -703,7 +714,8 @@ export default {
|
|||
com_nav_delete_account_confirm: 'アカウントを削除しますか?',
|
||||
com_nav_delete_account_button: 'アカウントを完全に削除する',
|
||||
com_nav_delete_account_email_placeholder: 'アカウントのメールアドレスを入力してください',
|
||||
com_nav_delete_account_confirm_placeholder: '続行するには、以下の入力フィールドに「DELETE」と入力してください',
|
||||
com_nav_delete_account_confirm_placeholder:
|
||||
'続行するには、以下の入力フィールドに「DELETE」と入力してください',
|
||||
com_nav_delete_warning: '警告: この操作により、アカウントが完全に削除されます。',
|
||||
com_nav_delete_data_info: 'すべてのデータが削除されます。',
|
||||
com_nav_conversation_mode: '会話モード',
|
||||
|
|
@ -718,11 +730,14 @@ export default {
|
|||
com_nav_tts_init_error: 'テキスト読み上げの初期化に失敗しました: {0}',
|
||||
com_nav_tts_unsupported_error:
|
||||
'選択したエンジンでのテキスト読み上げはこのブラウザではサポートされていません。',
|
||||
com_nav_source_buffer_error: 'オーディオ再生の設定エラーが発生しました。ページを更新してください。',
|
||||
com_nav_source_buffer_error:
|
||||
'オーディオ再生の設定エラーが発生しました。ページを更新してください。',
|
||||
com_nav_media_source_init_error:
|
||||
'オーディオプレーヤーを準備できませんでした。ブラウザの設定を確認してください。',
|
||||
com_nav_buffer_append_error: 'オーディオストリーミングに問題が発生しました。再生が中断される可能性があります。',
|
||||
com_nav_speech_cancel_error: 'オーディオ再生を停止できません。ページを更新する必要があるかもしれません。',
|
||||
com_nav_buffer_append_error:
|
||||
'オーディオストリーミングに問題が発生しました。再生が中断される可能性があります。',
|
||||
com_nav_speech_cancel_error:
|
||||
'オーディオ再生を停止できません。ページを更新する必要があるかもしれません。',
|
||||
com_nav_voices_fetch_error:
|
||||
'音声オプションを取得できませんでした。インターネット接続を確認してください。',
|
||||
com_nav_engine: 'エンジン',
|
||||
|
|
@ -753,8 +768,7 @@ export default {
|
|||
com_nav_commands: 'Commands',
|
||||
com_nav_commands_tab: 'コマンド設定',
|
||||
com_nav_at_command: '@-Command',
|
||||
com_nav_at_command_description:
|
||||
'コマンド"@"でエンドポイント、モデル、プリセットを切り替える',
|
||||
com_nav_at_command_description: 'コマンド"@"でエンドポイント、モデル、プリセットを切り替える',
|
||||
com_nav_plus_command: '+-Command',
|
||||
com_nav_plus_command_description: 'コマンド"+"で複数応答設定を追加する',
|
||||
com_nav_slash_command: '/-Command',
|
||||
|
|
@ -894,7 +908,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Assistantの名前で検索',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Tools',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -361,7 +361,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: '코드 인터프리터에서만 다음 파일을 사용할 수 있습니다:',
|
||||
com_assistants_retrieval: '검색',
|
||||
com_assistants_search_name: '이름으로 도우미 검색',
|
||||
com_assistants_tools: '도구',
|
||||
com_ui_tools: '도구',
|
||||
com_assistants_actions: '작업',
|
||||
com_assistants_add_tools: '도구 추가',
|
||||
com_assistants_add_actions: '작업 추가',
|
||||
|
|
@ -1910,7 +1910,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: '이름으로 도우미 검색',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: '도구',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ export default {
|
|||
com_assistants_capabilities: 'Возможности',
|
||||
com_assistants_image_vision: 'Анализ изображений',
|
||||
com_assistants_search_name: 'Поиск ассистентов по имени',
|
||||
com_assistants_tools: 'Инструменты',
|
||||
com_ui_tools: 'Инструменты',
|
||||
com_assistants_actions: 'Действия',
|
||||
com_assistants_add_tools: 'Добавить инструменты',
|
||||
com_assistants_add_actions: 'Добавить действия',
|
||||
|
|
@ -2243,7 +2243,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: 'Поиск ассистентов по имени',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: 'Инструменты',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export default {
|
|||
'Aşağıdaki dosyalar yalnızca Kod Yorumlayıcı için kullanılabilir:',
|
||||
com_assistants_retrieval: 'Geri Getirme',
|
||||
com_assistants_search_name: 'Asistan adında ara',
|
||||
com_assistants_tools: 'Araçlar',
|
||||
com_ui_tools: 'Araçlar',
|
||||
com_assistants_actions: 'Eylemler',
|
||||
com_assistants_add_tools: 'Araçları Ekle',
|
||||
com_assistants_add_actions: 'Eylem Ekle',
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ export default {
|
|||
com_nav_convo_menu_options: '对话菜单选项',
|
||||
com_ui_artifacts: 'Artifacts',
|
||||
com_ui_artifacts_toggle: '切换至 Artifacts UI',
|
||||
com_nav_info_code_artifacts:
|
||||
'启用在对话旁显示的实验性代码工件',
|
||||
com_nav_info_code_artifacts: '启用在对话旁显示的实验性代码工件',
|
||||
com_ui_include_shadcnui: '包含 shadcn/ui 组件指令',
|
||||
com_nav_info_include_shadcnui:
|
||||
'启用后,将包含使用 shadcn/ui 组件的指令。shadcn/ui 是一组使用 Radix UI 和 Tailwind CSS 构建的可重复使用的组件。注意:这些指令较长,仅在您需要向 LLM 提供正确的导入和组件信息时才应启用。有关这些组件的更多信息,请访问:https://ui.shadcn.com/',
|
||||
|
|
@ -23,8 +22,7 @@ export default {
|
|||
com_error_no_user_key: '没有找到密钥。请提供密钥后重试。',
|
||||
com_error_no_base_url: '未找到基础 URL,请提供一个后重试。',
|
||||
com_error_invalid_user_key: '提供的密钥无效。请提供有效的密钥后重试。',
|
||||
com_error_expired_user_key:
|
||||
'您提供的 {0} 密钥已于 {1} 过期。请提供新的密钥并重试。',
|
||||
com_error_expired_user_key: '您提供的 {0} 密钥已于 {1} 过期。请提供新的密钥并重试。',
|
||||
com_error_input_length:
|
||||
'最新消息的令牌数过长,超出了令牌限制(分别为 {0})。请缩短您的消息、调整对话参数中的最大上下文大小,或分叉对话以继续。',
|
||||
com_files_no_results: '无结果。',
|
||||
|
|
@ -44,8 +42,7 @@ export default {
|
|||
com_assistants_code_interpreter_info:
|
||||
'代码解释器使助手能够编写和运行代码。该工具可以处理具有多种数据和格式的文件,并生成图表等文件。',
|
||||
com_assistants_knowledge: '知识',
|
||||
com_assistants_knowledge_info:
|
||||
'如果您在 “知识” 中上传文件,与助手的对话可能包括文件内容。',
|
||||
com_assistants_knowledge_info: '如果您在 “知识” 中上传文件,与助手的对话可能包括文件内容。',
|
||||
com_assistants_knowledge_disabled:
|
||||
'必须创建助手,且启用并保存代码解释器或检索,才能将文件作为知识上传。',
|
||||
com_assistants_image_vision: '识图',
|
||||
|
|
@ -53,12 +50,11 @@ export default {
|
|||
com_assistants_code_interpreter_files: '以下文件仅适用于代码解释器:',
|
||||
com_assistants_retrieval: '检索',
|
||||
com_assistants_search_name: '根据名称搜索助手',
|
||||
com_assistants_tools: '工具',
|
||||
com_ui_tools: '工具',
|
||||
com_assistants_actions: '操作',
|
||||
com_assistants_add_tools: '添加工具',
|
||||
com_assistants_add_actions: '添加操作',
|
||||
com_assistants_non_retrieval_model:
|
||||
'此模型未启用文件搜索功能。请选择其他模型。',
|
||||
com_assistants_non_retrieval_model: '此模型未启用文件搜索功能。请选择其他模型。',
|
||||
com_assistants_available_actions: '可用操作',
|
||||
com_assistants_running_action: '正在运行操作',
|
||||
com_assistants_completed_action: '与 {0} 聊天',
|
||||
|
|
@ -109,8 +105,7 @@ export default {
|
|||
com_ui_attach_error_openai: '无法将助手文件附加到其他渠道',
|
||||
com_ui_attach_warn_endpoint: '不兼容的工具可能会忽略非助手文件',
|
||||
com_ui_attach_error_size: '超出渠道规定的文件大小:',
|
||||
com_ui_attach_error:
|
||||
'无法附加文件,请创建或选择一个对话,或尝试刷新页面。',
|
||||
com_ui_attach_error: '无法附加文件,请创建或选择一个对话,或尝试刷新页面。',
|
||||
com_ui_examples: '示例',
|
||||
com_ui_new_chat: '创建新对话',
|
||||
com_ui_happy_birthday: '这是我的第一个生日!',
|
||||
|
|
@ -219,8 +214,7 @@ export default {
|
|||
'此选项会分叉所有导向目标消息的消息分支,包括其相邻消息;换句话说,无论是否可见或在同一路径上,所有消息分支都会被包含在内。',
|
||||
com_ui_fork_info_start:
|
||||
'如果勾选,则根据上述选择的行为,从此消息开始到对话中最新的消息将被分叉。',
|
||||
com_ui_fork_info_remember:
|
||||
'选中此项可记住您的选择,以便下次分叉对话时更快捷地使用您偏好的选项。',
|
||||
com_ui_fork_info_remember: '选中此项可记住您的选择,以便下次分叉对话时更快捷地使用您偏好的选项。',
|
||||
com_ui_fork_success: '对话分叉成功',
|
||||
com_ui_fork_processing: '正在分叉对话...',
|
||||
com_ui_fork_error: '分叉对话时出现错误',
|
||||
|
|
@ -229,8 +223,7 @@ export default {
|
|||
com_ui_fork_remember: '记住',
|
||||
com_ui_fork_split_target_setting: '默认以目标消息开始分叉',
|
||||
com_ui_fork_split_target: '在此分叉',
|
||||
com_ui_fork_remember_checked:
|
||||
'您的选择将在使用后被记住。您可以随时在设置中更改。',
|
||||
com_ui_fork_remember_checked: '您的选择将在使用后被记住。您可以随时在设置中更改。',
|
||||
com_ui_fork_all_target: '包含所有目标',
|
||||
com_ui_fork_branches: '包含相关分支',
|
||||
com_ui_fork_visible: '仅可见消息',
|
||||
|
|
@ -304,12 +297,9 @@ export default {
|
|||
com_ui_share_retrieve_error: '检索共享链接时出现错误',
|
||||
com_ui_share_delete_error: '删除共享链接时出现错误',
|
||||
com_ui_share_create_message: '您的姓名以及您在共享后添加的任何消息将保持私密。',
|
||||
com_ui_share_created_message:
|
||||
'已创建到您聊天的共享链接。可以通过设置随时管理以前共享的聊天。',
|
||||
com_ui_share_update_message:
|
||||
'您的姓名、自定义指令以及您在共享后添加的任何消息将保持私密。',
|
||||
com_ui_share_updated_message:
|
||||
'已更新到您聊天的共享链接。可以通过设置随时管理以前共享的聊天。',
|
||||
com_ui_share_created_message: '已创建到您聊天的共享链接。可以通过设置随时管理以前共享的聊天。',
|
||||
com_ui_share_update_message: '您的姓名、自定义指令以及您在共享后添加的任何消息将保持私密。',
|
||||
com_ui_share_updated_message: '已更新到您聊天的共享链接。可以通过设置随时管理以前共享的聊天。',
|
||||
com_ui_shared_link_not_found: '未找到共享链接',
|
||||
com_ui_delete_conversation: '删除对话?',
|
||||
com_ui_delete_confirm: '这将删除',
|
||||
|
|
@ -319,8 +309,7 @@ export default {
|
|||
com_ui_delete_action_confirm: '您确定要删除此操作吗?',
|
||||
com_ui_delete_confirm_prompt_version_var:
|
||||
'这将删除选中版本的 “{0}”。如果没有其他版本,该提示词将被删除。',
|
||||
com_ui_delete_assistant_confirm:
|
||||
'您确定要删除此助手吗?该操作无法撤销。',
|
||||
com_ui_delete_assistant_confirm: '您确定要删除此助手吗?该操作无法撤销。',
|
||||
com_ui_rename: '重命名',
|
||||
com_ui_archive: '归档',
|
||||
com_ui_archive_error: '归档对话失败',
|
||||
|
|
@ -331,8 +320,7 @@ export default {
|
|||
com_ui_upload: '上传',
|
||||
com_ui_connect: '连接',
|
||||
com_ui_locked: '已锁定',
|
||||
com_ui_upload_delay:
|
||||
'上传 “{0}” 时比预期花了更长时间。文件正在进行检索索引,请稍候。',
|
||||
com_ui_upload_delay: '上传 “{0}” 时比预期花了更长时间。文件正在进行检索索引,请稍候。',
|
||||
com_ui_privacy_policy: '隐私政策',
|
||||
com_ui_terms_of_service: '服务政策',
|
||||
com_ui_use_micrphone: '使用麦克风',
|
||||
|
|
@ -355,16 +343,11 @@ export default {
|
|||
com_ui_bookmarks_filter: '筛选书签...',
|
||||
com_ui_no_bookmarks: '似乎您还没有书签。点击一个对话并添加一个新的书签',
|
||||
com_ui_no_conversation_id: '未找到对话 ID',
|
||||
com_auth_error_login:
|
||||
'无法登录,请确认提供的账户密码正确,并重新尝试。',
|
||||
com_auth_error_login_rl:
|
||||
'尝试登录次数过多,请稍后再试。',
|
||||
com_auth_error_login_ban:
|
||||
'根据我们的服务规则,您的帐号被暂时禁用。',
|
||||
com_auth_error_login_server:
|
||||
'内部服务器错误,请稍后再试。',
|
||||
com_auth_error_login_unverified:
|
||||
'您的账户尚未验证。请检查您的邮件以获取验证链接。',
|
||||
com_auth_error_login: '无法登录,请确认提供的账户密码正确,并重新尝试。',
|
||||
com_auth_error_login_rl: '尝试登录次数过多,请稍后再试。',
|
||||
com_auth_error_login_ban: '根据我们的服务规则,您的帐号被暂时禁用。',
|
||||
com_auth_error_login_server: '内部服务器错误,请稍后再试。',
|
||||
com_auth_error_login_unverified: '您的账户尚未验证。请检查您的邮件以获取验证链接。',
|
||||
com_auth_no_account: '还没有账户?',
|
||||
com_auth_sign_up: '注册',
|
||||
com_auth_sign_in: '登录',
|
||||
|
|
@ -387,8 +370,7 @@ export default {
|
|||
com_auth_password_not_match: '密码不一致',
|
||||
com_auth_continue: '继续',
|
||||
com_auth_create_account: '创建账户',
|
||||
com_auth_error_create:
|
||||
'注册账户过程中出现错误,请重试。',
|
||||
com_auth_error_create: '注册账户过程中出现错误,请重试。',
|
||||
com_auth_full_name: '姓名',
|
||||
com_auth_name_required: '姓名为必填项',
|
||||
com_auth_name_min_length: '姓名至少 3 个字符',
|
||||
|
|
@ -408,8 +390,7 @@ export default {
|
|||
com_auth_reset_password_link_sent: '密码重置链接已发送',
|
||||
com_auth_reset_password_if_email_exists:
|
||||
'如果存在使用该邮件地址的帐户,则会发送一封包含密码重置说明的邮件。请务必检查您的垃圾邮件文件夹。',
|
||||
com_auth_reset_password_email_sent:
|
||||
'如果用户已注册,将会发送一封邮件到收件箱。',
|
||||
com_auth_reset_password_email_sent: '如果用户已注册,将会发送一封邮件到收件箱。',
|
||||
com_auth_reset_password_success: '密码重置成功',
|
||||
com_auth_login_with_new_password: '现在你可以使用你的新密码登录。',
|
||||
com_auth_error_invalid_reset_token: '重置密码的密钥已失效。',
|
||||
|
|
@ -482,8 +463,7 @@ export default {
|
|||
输入词元和生成词元的总长度受模型上下文长度的限制。如果该数值超过最大上下文词元数,您可能会遇到错误。`,
|
||||
com_endpoint_openai_temp:
|
||||
'值越高表示输出越随机,值越低表示输出越确定。建议不要同时改变此值和 Top P。',
|
||||
com_endpoint_openai_max:
|
||||
'最大生成词元数。输入词元长度由模型的上下文长度决定。',
|
||||
com_endpoint_openai_max: '最大生成词元数。输入词元长度由模型的上下文长度决定。',
|
||||
com_endpoint_openai_topp:
|
||||
'相较于随机性的另一个取样方法,称为核采样,模型选取输出词元中大于 top_p(概率密度在整个概率分布中的比例)的结果。比如 top_p=0.1 表示只有概率占比为前 10% 的词元才会被考虑作为输出。建议不要同时改变此值和随机性。',
|
||||
com_endpoint_openai_freq:
|
||||
|
|
@ -498,8 +478,7 @@ export default {
|
|||
'发送给Vision的图像分辨率。 “Low” 更便宜且更快,“High” 更详细但更昂贵,“Auto” 将基于图像分辨率自动在两者之间进行选择。',
|
||||
com_endpoint_openai_stop: '最多 4 个序列,API 将停止生成更多词元。',
|
||||
com_endpoint_openai_custom_name_placeholder: '为 AI 设置一个名称',
|
||||
com_endpoint_openai_prompt_prefix_placeholder:
|
||||
'在系统消息中添加自定义指令,默认为空',
|
||||
com_endpoint_openai_prompt_prefix_placeholder: '在系统消息中添加自定义指令,默认为空',
|
||||
com_endpoint_anthropic_temp:
|
||||
'值介于 0 到 1 之间。对于分析性/选择性任务,值应更接近 0;对于创造性和生成性任务,值应更接近 1。我们建议更改该参数或 Top P,但不要同时更改这两个参数。',
|
||||
com_endpoint_anthropic_topp:
|
||||
|
|
@ -556,8 +535,7 @@ export default {
|
|||
com_endpoint_use_active_assistant: '使用激活的助手',
|
||||
com_endpoint_assistant_model: '助手模型',
|
||||
com_endpoint_save_as_preset: '保存为预设',
|
||||
com_endpoint_presets_clear_warning:
|
||||
'确定要清除所有预设吗?此操作不可逆。',
|
||||
com_endpoint_presets_clear_warning: '确定要清除所有预设吗?此操作不可逆。',
|
||||
com_endpoint_not_implemented: '未实现功能',
|
||||
com_endpoint_no_presets: '暂无预设,使用设置按钮创建一个',
|
||||
com_endpoint_not_available: '无可用渠道',
|
||||
|
|
@ -567,8 +545,7 @@ export default {
|
|||
com_endpoint_agent_model: '代理模型(推荐: GPT-3.5)',
|
||||
com_endpoint_completion_model: '补全模型(推荐: GPT-4)',
|
||||
com_endpoint_func_hover: '将插件作为 OpenAI 函数使用',
|
||||
com_endpoint_skip_hover:
|
||||
'跳过补全步骤,检查最终答案和生成步骤',
|
||||
com_endpoint_skip_hover: '跳过补全步骤,检查最终答案和生成步骤',
|
||||
com_endpoint_config_key: '设置 API 密钥',
|
||||
com_endpoint_assistant_placeholder: '请从右侧面板中选择助手',
|
||||
com_endpoint_config_placeholder: '在顶部菜单设置 API 密钥。',
|
||||
|
|
@ -648,8 +625,7 @@ export default {
|
|||
com_show_examples: '显示示例',
|
||||
com_nav_plugin_search: '搜索插件',
|
||||
com_nav_tool_search: '搜索工具',
|
||||
com_nav_plugin_auth_error:
|
||||
'尝试验证此插件时出现错误。请重试。',
|
||||
com_nav_plugin_auth_error: '尝试验证此插件时出现错误。请重试。',
|
||||
com_nav_export_filename: '文件名',
|
||||
com_nav_export_filename_placeholder: '设置文件名',
|
||||
com_nav_export_type: '类型',
|
||||
|
|
@ -694,8 +670,7 @@ export default {
|
|||
com_nav_archive_name: '名称',
|
||||
com_nav_archive_created_at: '归档时间',
|
||||
com_nav_clear_conversation: '清除对话',
|
||||
com_nav_clear_conversation_confirm_message:
|
||||
'您确定要删除所有对话吗?该操作无法撤销。',
|
||||
com_nav_clear_conversation_confirm_message: '您确定要删除所有对话吗?该操作无法撤销。',
|
||||
com_nav_help_faq: '帮助',
|
||||
com_nav_settings: '设置',
|
||||
com_nav_search_placeholder: '搜索消息',
|
||||
|
|
@ -716,15 +691,12 @@ export default {
|
|||
com_nav_audio_process_error: '处理音频时发生错误:{0}',
|
||||
com_nav_long_audio_warning: '较长的文本将需要更长时间来处理。',
|
||||
com_nav_tts_init_error: '初始化文本转语音失败:{0}',
|
||||
com_nav_tts_unsupported_error:
|
||||
'所选引擎的文本转语音在此浏览器中不受支持。',
|
||||
com_nav_tts_unsupported_error: '所选引擎的文本转语音在此浏览器中不受支持。',
|
||||
com_nav_source_buffer_error: '设置音频播放时发生错误。请刷新页面。',
|
||||
com_nav_media_source_init_error:
|
||||
'无法准备音频播放器。请检查您的浏览器设置。',
|
||||
com_nav_media_source_init_error: '无法准备音频播放器。请检查您的浏览器设置。',
|
||||
com_nav_buffer_append_error: '音频流处理出现问题。播放可能会被中断。',
|
||||
com_nav_speech_cancel_error: '无法停止音频播放。您可能需要刷新页面。',
|
||||
com_nav_voices_fetch_error:
|
||||
'无法获取语音选项。请检查您的网络连接。',
|
||||
com_nav_voices_fetch_error: '无法获取语音选项。请检查您的网络连接。',
|
||||
com_nav_engine: '引擎',
|
||||
com_nav_browser: '浏览器',
|
||||
com_nav_edge: '边缘端',
|
||||
|
|
@ -753,8 +725,7 @@ export default {
|
|||
com_nav_commands: '命令',
|
||||
com_nav_commands_tab: '命令设置',
|
||||
com_nav_at_command: '@-命令',
|
||||
com_nav_at_command_description:
|
||||
'切换至命令 “@” 以更改端点、模型、预设等',
|
||||
com_nav_at_command_description: '切换至命令 “@” 以更改端点、模型、预设等',
|
||||
com_nav_plus_command: '+-命令',
|
||||
com_nav_plus_command_description: '切换至命令 “+” 以添加多响应设置',
|
||||
com_nav_slash_command: '/-命令',
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ export default {
|
|||
com_assistants_code_interpreter_files: '以下檔案僅適用於程式碼解譯器:',
|
||||
com_assistants_retrieval: '檢索',
|
||||
com_assistants_search_name: '搜尋助理名稱',
|
||||
com_assistants_tools: '工具',
|
||||
com_ui_tools: '工具',
|
||||
com_assistants_actions: '操作',
|
||||
com_assistants_add_tools: '新增工具',
|
||||
com_assistants_add_actions: '新增操作',
|
||||
|
|
@ -1933,7 +1933,7 @@ export const comparisons = {
|
|||
english: 'Search assistants by name',
|
||||
translated: '搜尋助理名稱',
|
||||
},
|
||||
com_assistants_tools: {
|
||||
com_ui_tools: {
|
||||
english: 'Tools',
|
||||
translated: '工具',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
|||
- **english**: Search assistants by name
|
||||
- **translated**: Pesquisar assistentes por nome
|
||||
|
||||
- **com_assistants_tools**:
|
||||
- **com_ui_tools**:
|
||||
- **english**: Tools
|
||||
- **translated**: Ferramentas
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
|||
- **english**: Search assistants by name
|
||||
- **translated**: Assistenten nach Namen suchen
|
||||
|
||||
- **com_assistants_tools**:
|
||||
- **com_ui_tools**:
|
||||
- **english**: Tools
|
||||
- **translated**: Werkzeuge
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
|||
- **english**: Search assistants by name
|
||||
- **translated**: Buscar asistentes por nombre
|
||||
|
||||
- **com_assistants_tools**:
|
||||
- **com_ui_tools**:
|
||||
- **english**: Tools
|
||||
- **translated**: Herramientas
|
||||
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
|||
- **english**: Search assistants by name
|
||||
- **translated**: Cerca assistenti per nome
|
||||
|
||||
- **com_assistants_tools**:
|
||||
- **com_ui_tools**:
|
||||
- **english**: Tools
|
||||
- **translated**: Strumenti
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
|||
- **english**: Search assistants by name
|
||||
- **translated**: Assistantの名前で検索
|
||||
|
||||
- **com_assistants_tools**:
|
||||
- **com_ui_tools**:
|
||||
- **english**: Tools
|
||||
- **translated**: Tools
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ Write a prompt that is mindful of the nuances in the language with respect to it
|
|||
- **english**: Search assistants by name
|
||||
- **translated**: 按名称搜索助手
|
||||
|
||||
- **com_assistants_tools**:
|
||||
- **com_ui_tools**:
|
||||
- **english**: Tools
|
||||
- **translated**: 工具
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { atom, selector, atomFamily } from 'recoil';
|
||||
import { TConversation, TMessagesAtom, TMessage } from 'librechat-data-provider';
|
||||
import { TConversation, TMessagesAtom, TMessage, TAttachment } from 'librechat-data-provider';
|
||||
import { buildTree } from '~/utils';
|
||||
|
||||
const conversation = atom<TConversation | null>({
|
||||
|
|
@ -22,6 +22,11 @@ const messagesTree = selector({
|
|||
},
|
||||
});
|
||||
|
||||
const messageAttachmentsMap = atom<Record<string, TAttachment[] | undefined>>({
|
||||
key: 'messageAttachmentsMap',
|
||||
default: {},
|
||||
});
|
||||
|
||||
const latestMessage = atom<TMessage | null>({
|
||||
key: 'latestMessage',
|
||||
default: null,
|
||||
|
|
@ -37,5 +42,6 @@ export default {
|
|||
conversation,
|
||||
messagesTree,
|
||||
latestMessage,
|
||||
messageAttachmentsMap,
|
||||
messagesSiblingIdxFamily,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const localStorageAtoms = {
|
|||
// Messages settings
|
||||
enterToSend: atomWithLocalStorage('enterToSend', true),
|
||||
chatDirection: atomWithLocalStorage('chatDirection', 'LTR'),
|
||||
showCode: atomWithLocalStorage('showCode', false),
|
||||
showCode: atomWithLocalStorage(LocalStorageKeys.SHOW_ANALYSIS_CODE, true),
|
||||
saveDrafts: atomWithLocalStorage('saveDrafts', true),
|
||||
forkSetting: atomWithLocalStorage('forkSetting', ''),
|
||||
splitAtTarget: atomWithLocalStorage('splitAtTarget', false),
|
||||
|
|
|
|||
|
|
@ -3,19 +3,14 @@ import {
|
|||
defaultEndpoints,
|
||||
modularEndpoints,
|
||||
LocalStorageKeys,
|
||||
isAgentsEndpoint,
|
||||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import type {
|
||||
TConfig,
|
||||
TPreset,
|
||||
TModelSpec,
|
||||
TConversation,
|
||||
TEndpointsConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import type * as t from 'librechat-data-provider';
|
||||
import type { LocalizeFunction } from '~/common';
|
||||
|
||||
export const getAssistantName = ({
|
||||
name,
|
||||
name = '',
|
||||
localize,
|
||||
}: {
|
||||
name?: string;
|
||||
|
|
@ -28,7 +23,7 @@ export const getAssistantName = ({
|
|||
}
|
||||
};
|
||||
|
||||
export const getEndpointsFilter = (endpointsConfig: TEndpointsConfig) => {
|
||||
export const getEndpointsFilter = (endpointsConfig: t.TEndpointsConfig) => {
|
||||
const filter: Record<string, boolean> = {};
|
||||
if (!endpointsConfig) {
|
||||
return filter;
|
||||
|
|
@ -41,7 +36,7 @@ export const getEndpointsFilter = (endpointsConfig: TEndpointsConfig) => {
|
|||
|
||||
export const getAvailableEndpoints = (
|
||||
filter: Record<string, boolean>,
|
||||
endpointsConfig: TEndpointsConfig,
|
||||
endpointsConfig: t.TEndpointsConfig,
|
||||
) => {
|
||||
const defaultSet = new Set(defaultEndpoints);
|
||||
const availableEndpoints: EModelEndpoint[] = [];
|
||||
|
|
@ -61,11 +56,11 @@ export const getAvailableEndpoints = (
|
|||
};
|
||||
|
||||
/** Get the specified field from the endpoint config */
|
||||
export function getEndpointField<K extends keyof TConfig>(
|
||||
endpointsConfig: TEndpointsConfig | undefined,
|
||||
export function getEndpointField<K extends keyof t.TConfig>(
|
||||
endpointsConfig: t.TEndpointsConfig | undefined,
|
||||
endpoint: EModelEndpoint | string | null | undefined,
|
||||
property: K,
|
||||
): TConfig[K] | undefined {
|
||||
): t.TConfig[K] | undefined {
|
||||
if (!endpointsConfig || endpoint === null || endpoint === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
@ -78,7 +73,7 @@ export function getEndpointField<K extends keyof TConfig>(
|
|||
return config[property];
|
||||
}
|
||||
|
||||
export function mapEndpoints(endpointsConfig: TEndpointsConfig) {
|
||||
export function mapEndpoints(endpointsConfig: t.TEndpointsConfig) {
|
||||
const filter = getEndpointsFilter(endpointsConfig);
|
||||
return getAvailableEndpoints(filter, endpointsConfig).sort(
|
||||
(a, b) => (endpointsConfig?.[a]?.order ?? 0) - (endpointsConfig?.[b]?.order ?? 0),
|
||||
|
|
@ -113,14 +108,14 @@ export function updateLastSelectedModel({
|
|||
}
|
||||
|
||||
interface ConversationInitParams {
|
||||
conversation: TConversation | null;
|
||||
newEndpoint: EModelEndpoint | string;
|
||||
endpointsConfig: TEndpointsConfig;
|
||||
conversation: t.TConversation | null;
|
||||
newEndpoint: EModelEndpoint | string | null;
|
||||
endpointsConfig: t.TEndpointsConfig;
|
||||
modularChat?: boolean;
|
||||
}
|
||||
|
||||
interface InitiatedTemplateResult {
|
||||
template: Partial<TPreset>;
|
||||
template: Partial<t.TPreset>;
|
||||
shouldSwitch: boolean;
|
||||
isExistingConversation: boolean;
|
||||
isCurrentModular: boolean;
|
||||
|
|
@ -130,10 +125,10 @@ interface InitiatedTemplateResult {
|
|||
|
||||
/** Get the conditional logic for switching conversations */
|
||||
export function getConvoSwitchLogic(params: ConversationInitParams): InitiatedTemplateResult {
|
||||
const { conversation, newEndpoint, endpointsConfig, modularChat } = params;
|
||||
const { conversation, newEndpoint, endpointsConfig, modularChat = false } = params;
|
||||
|
||||
const currentEndpoint = conversation?.endpoint;
|
||||
const template: Partial<TPreset> = {
|
||||
const template: Partial<t.TPreset> = {
|
||||
...conversation,
|
||||
endpoint: newEndpoint,
|
||||
conversationId: 'new',
|
||||
|
|
@ -144,7 +139,7 @@ export function getConvoSwitchLogic(params: ConversationInitParams): InitiatedTe
|
|||
isAssistantsEndpoint(currentEndpoint) &&
|
||||
currentEndpoint === newEndpoint;
|
||||
|
||||
const conversationId = conversation?.conversationId;
|
||||
const conversationId = conversation?.conversationId ?? '';
|
||||
const isExistingConversation = !!(conversationId && conversationId !== 'new');
|
||||
|
||||
const currentEndpointType =
|
||||
|
|
@ -178,7 +173,7 @@ export function getConvoSwitchLogic(params: ConversationInitParams): InitiatedTe
|
|||
*
|
||||
* First, the admin defined default, then last selected spec, followed by first spec
|
||||
*/
|
||||
export function getDefaultModelSpec(modelSpecs?: TModelSpec[]) {
|
||||
export function getDefaultModelSpec(modelSpecs?: t.TModelSpec[]) {
|
||||
const defaultSpec = modelSpecs?.find((spec) => spec.default);
|
||||
const lastSelectedSpecName = localStorage.getItem(LocalStorageKeys.LAST_SPEC);
|
||||
const lastSelectedSpec = modelSpecs?.find((spec) => spec.name === lastSelectedSpecName);
|
||||
|
|
@ -189,7 +184,7 @@ export function getDefaultModelSpec(modelSpecs?: TModelSpec[]) {
|
|||
*
|
||||
* First, the admin defined default, then last selected spec, followed by first spec
|
||||
*/
|
||||
export function getModelSpecIconURL(modelSpec: TModelSpec) {
|
||||
export function getModelSpecIconURL(modelSpec: t.TModelSpec) {
|
||||
return modelSpec.iconURL ?? modelSpec.preset.iconURL ?? modelSpec.preset.endpoint ?? '';
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +197,7 @@ export function getIconEndpoint({
|
|||
iconURL,
|
||||
endpoint,
|
||||
}: {
|
||||
endpointsConfig: TEndpointsConfig | undefined;
|
||||
endpointsConfig: t.TEndpointsConfig | undefined;
|
||||
iconURL: string | undefined;
|
||||
endpoint: string | null | undefined;
|
||||
}) {
|
||||
|
|
@ -217,14 +212,44 @@ export function getIconKey({
|
|||
endpointIconURL: iconURL,
|
||||
}: {
|
||||
endpoint?: string | null;
|
||||
endpointsConfig?: TEndpointsConfig | undefined;
|
||||
endpointsConfig?: t.TEndpointsConfig | undefined;
|
||||
endpointType?: string | null;
|
||||
endpointIconURL?: string;
|
||||
}) {
|
||||
const endpointType = _eType ?? getEndpointField(endpointsConfig, endpoint, 'type');
|
||||
const endpointIconURL = iconURL ?? getEndpointField(endpointsConfig, endpoint, 'iconURL');
|
||||
if (endpointIconURL && EModelEndpoint[endpointIconURL]) {
|
||||
const endpointType = _eType ?? getEndpointField(endpointsConfig, endpoint, 'type') ?? '';
|
||||
const endpointIconURL = iconURL ?? getEndpointField(endpointsConfig, endpoint, 'iconURL') ?? '';
|
||||
if (endpointIconURL && EModelEndpoint[endpointIconURL] != null) {
|
||||
return endpointIconURL;
|
||||
}
|
||||
return endpointType ? 'unknown' : endpoint ?? 'unknown';
|
||||
}
|
||||
|
||||
export const getEntity = ({
|
||||
endpoint,
|
||||
assistant_id,
|
||||
agent_id,
|
||||
agentsMap,
|
||||
assistantMap,
|
||||
}: {
|
||||
endpoint: EModelEndpoint | string | null | undefined;
|
||||
assistant_id: string | undefined;
|
||||
agent_id: string | undefined;
|
||||
agentsMap: t.TAgentsMap | undefined;
|
||||
assistantMap: t.TAssistantsMap | undefined;
|
||||
}): {
|
||||
entity: t.Agent | t.Assistant | undefined | null;
|
||||
isAgent: boolean;
|
||||
isAssistant: boolean;
|
||||
} => {
|
||||
const isAgent = isAgentsEndpoint(endpoint);
|
||||
const isAssistant = isAssistantsEndpoint(endpoint);
|
||||
|
||||
if (isAgent) {
|
||||
const agent = agentsMap?.[agent_id ?? ''];
|
||||
return { entity: agent, isAgent, isAssistant };
|
||||
} else if (isAssistant) {
|
||||
const assistant = assistantMap?.[endpoint ?? '']?.[assistant_id ?? ''];
|
||||
return { entity: assistant, isAgent, isAssistant };
|
||||
}
|
||||
return { entity: null, isAgent, isAssistant };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
import { EarthIcon } from 'lucide-react';
|
||||
import { alternateName } from 'librechat-data-provider';
|
||||
import {
|
||||
alternateName,
|
||||
EModelEndpoint,
|
||||
FileSources,
|
||||
EToolResources,
|
||||
} from 'librechat-data-provider';
|
||||
import type { Agent, TFile } from 'librechat-data-provider';
|
||||
import type { DropdownValueSetter, TAgentOption } from '~/common';
|
||||
import type { DropdownValueSetter, TAgentOption, ExtendedFile } from '~/common';
|
||||
|
||||
/**
|
||||
* Creates a Dropdown value setter that always passes a string value,
|
||||
|
|
@ -37,9 +42,6 @@ export const createProviderOption = (provider: string) => ({
|
|||
value: provider,
|
||||
});
|
||||
|
||||
type FileTuple = [string, Partial<TFile>];
|
||||
type FileList = Array<FileTuple>;
|
||||
|
||||
export const processAgentOption = ({
|
||||
agent: _agent,
|
||||
fileMap,
|
||||
|
|
@ -56,20 +58,49 @@ export const processAgentOption = ({
|
|||
label: _agent?.name ?? '',
|
||||
value: _agent?.id ?? '',
|
||||
icon: isGlobal ? <EarthIcon className="icon-md text-green-400" /> : null,
|
||||
// files: _agent?.file_ids ? ([] as FileList) : undefined,
|
||||
// code_files: _agent?.tool_resources?.code_interpreter?.file_ids
|
||||
// ? ([] as FileList)
|
||||
// : undefined,
|
||||
knowledge_files: _agent?.tool_resources?.file_search?.file_ids
|
||||
? ([] as Array<[string, ExtendedFile]>)
|
||||
: undefined,
|
||||
code_files: _agent?.tool_resources?.execute_code?.file_ids
|
||||
? ([] as Array<[string, ExtendedFile]>)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
if (!fileMap) {
|
||||
return agent;
|
||||
}
|
||||
|
||||
const handleFile = (file_id: string, list?: FileList) => {
|
||||
const handleFile = ({
|
||||
file_id,
|
||||
tool_resource,
|
||||
list,
|
||||
}: {
|
||||
file_id: string;
|
||||
tool_resource: EToolResources;
|
||||
list?: Array<[string, ExtendedFile]>;
|
||||
}) => {
|
||||
const file = fileMap[file_id];
|
||||
const source =
|
||||
tool_resource === EToolResources.file_search
|
||||
? FileSources.vectordb
|
||||
: file?.source ?? FileSources.local;
|
||||
|
||||
if (file) {
|
||||
list?.push([file_id, file]);
|
||||
list?.push([
|
||||
file_id,
|
||||
{
|
||||
file_id: file.file_id,
|
||||
type: file.type,
|
||||
filepath: file.filepath,
|
||||
filename: file.filename,
|
||||
width: file.width,
|
||||
height: file.height,
|
||||
size: file.bytes,
|
||||
preview: file.filepath,
|
||||
progress: 1,
|
||||
source,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
list?.push([
|
||||
file_id,
|
||||
|
|
@ -77,22 +108,28 @@ export const processAgentOption = ({
|
|||
file_id,
|
||||
type: '',
|
||||
filename: '',
|
||||
bytes: 1,
|
||||
// progress: 1,
|
||||
// TODO: file handling
|
||||
// filepath: endpoint,
|
||||
size: 1,
|
||||
progress: 1,
|
||||
filepath: EModelEndpoint.agents,
|
||||
source,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
if (agent.files && _agent?.file_ids) {
|
||||
_agent.file_ids.forEach((file_id) => handleFile(file_id, agent.files));
|
||||
if (agent.knowledge_files && _agent?.tool_resources?.file_search?.file_ids) {
|
||||
_agent.tool_resources.file_search.file_ids.forEach((file_id) =>
|
||||
handleFile({
|
||||
file_id,
|
||||
list: agent.knowledge_files,
|
||||
tool_resource: EToolResources.file_search,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (agent.code_files && _agent?.tool_resources?.code_interpreter?.file_ids) {
|
||||
_agent.tool_resources.code_interpreter.file_ids.forEach((file_id) =>
|
||||
handleFile(file_id, agent.code_files),
|
||||
if (agent.code_files && _agent?.tool_resources?.execute_code?.file_ids) {
|
||||
_agent.tool_resources.execute_code.file_ids.forEach((file_id) =>
|
||||
handleFile({ file_id, list: agent.code_files, tool_resource: EToolResources.execute_code }),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,29 @@
|
|||
import type { TFile, Assistant, Agent, TPlugin } from 'librechat-data-provider';
|
||||
import type { TFile, TAttachment, Assistant, Agent, TPlugin } from 'librechat-data-provider';
|
||||
import type { TPluginMap } from '~/common';
|
||||
|
||||
/** Maps Attachments by `toolCallId` for quick lookup */
|
||||
export function mapAttachments(attachments: Array<TAttachment | null | undefined>) {
|
||||
const attachmentMap: Record<string, TAttachment[] | undefined> = {};
|
||||
|
||||
for (const attachment of attachments) {
|
||||
if (attachment === null || attachment === undefined) {
|
||||
continue;
|
||||
}
|
||||
const key = attachment.toolCallId ?? '';
|
||||
if (key.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!attachmentMap[key]) {
|
||||
attachmentMap[key] = [];
|
||||
}
|
||||
|
||||
attachmentMap[key].push(attachment);
|
||||
}
|
||||
|
||||
return attachmentMap;
|
||||
}
|
||||
|
||||
/** Maps Files by `file_id` for quick lookup */
|
||||
export function mapFiles(files: TFile[]) {
|
||||
const fileMap = {} as Record<string, TFile>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue