2025-05-16 17:50:18 +02:00
|
|
|
import React, { useMemo, useState, useRef, useEffect } from 'react';
|
2024-09-30 17:17:57 -04:00
|
|
|
import { useRecoilValue } from 'recoil';
|
🎉 feat: Code Interpreter API and Agents Release (#4860)
* feat: Code Interpreter API & File Search Agent Uploads
chore: add back code files
wip: first pass, abstract key dialog
refactor: influence checkbox on key changes
refactor: update localization keys for 'execute code' to 'run code'
wip: run code button
refactor: add throwError parameter to loadAuthValues and getUserPluginAuthValue functions
feat: first pass, API tool calling
fix: handle missing toolId in callTool function and return 404 for non-existent tools
feat: show code outputs
fix: improve error handling in callTool function and log errors
fix: handle potential null value for filepath in attachment destructuring
fix: normalize language before rendering and prevent null return
fix: add loading indicator in RunCode component while executing code
feat: add support for conditional code execution in Markdown components
feat: attachments
refactor: remove bash
fix: pass abort signal to graph/run
refactor: debounce and rate limit tool call
refactor: increase debounce delay for execute function
feat: set code output attachments
feat: image attachments
refactor: apply message context
refactor: pass `partIndex`
feat: toolCall schema/model/methods
feat: block indexing
feat: get tool calls
chore: imports
chore: typing
chore: condense type imports
feat: get tool calls
fix: block indexing
chore: typing
refactor: update tool calls mapping to support multiple results
fix: add unique key to nav link for rendering
wip: first pass, tool call results
refactor: update query cache from successful tool call mutation
style: improve result switcher styling
chore: note on using \`.toObject()\`
feat: add agent_id field to conversation schema
chore: typing
refactor: rename agentMap to agentsMap for consistency
feat: Agent Name as chat input placeholder
chore: bump agents
📦 chore: update @langchain dependencies to latest versions to match agents package
📦 chore: update @librechat/agents dependency to version 1.8.0
fix: Aborting agent stream removes sender; fix(bedrock): completion removes preset name label
refactor: remove direct file parameter to use req.file, add `processAgentFileUpload` for image uploads
feat: upload menu
feat: prime message_file resources
feat: implement conversation access validation in chat route
refactor: remove file parameter from processFileUpload and use req.file instead
feat: add savedMessageIds set to track saved message IDs in BaseClient, to prevent unnecessary double-write to db
feat: prevent duplicate message saves by checking savedMessageIds in AgentController
refactor: skip legacy RAG API handling for agents
feat: add files field to convoSchema
refactor: update request type annotations from Express.Request to ServerRequest in file processing functions
feat: track conversation files
fix: resendFiles, addPreviousAttachments handling
feat: add ID validation for session_id and file_id in download route
feat: entity_id for code file uploads/downloads
fix: code file edge cases
feat: delete related tool calls
feat: add stream rate handling for LLM configuration
feat: enhance system content with attached file information
fix: improve error logging in resource priming function
* WIP: PoC, sequential agents
WIP: PoC Sequential Agents, first pass content data + bump agents package
fix: package-lock
WIP: PoC, o1 support, refactor bufferString
feat: convertJsonSchemaToZod
fix: form issues and schema defining erroneous model
fix: max length issue on agent form instructions, limit conversation messages to sequential agents
feat: add abort signal support to createRun function and AgentClient
feat: PoC, hide prior sequential agent steps
fix: update parameter naming from config to metadata in event handlers for clarity, add model to usage data
refactor: use only last contentData, track model for usage data
chore: bump agents package
fix: content parts issue
refactor: filter contentParts to include tool calls and relevant indices
feat: show function calls
refactor: filter context messages to exclude tool calls when no tools are available to the agent
fix: ensure tool call content is not undefined in formatMessages
feat: add agent_id field to conversationPreset schema
feat: hide sequential agents
feat: increase upload toast duration to 10 seconds
* refactor: tool context handling & update Code API Key Dialog
feat: toolContextMap
chore: skipSpecs -> useSpecs
ci: fix handleTools tests
feat: API Key Dialog
* feat: Agent Permissions Admin Controls
feat: replace label with button for prompt permission toggle
feat: update agent permissions
feat: enable experimental agents and streamline capability configuration
feat: implement access control for agents and enhance endpoint menu items
feat: add welcome message for agent selection in localization
feat: add agents permission to access control and update version to 0.7.57
* fix: update types in useAssistantListMap and useMentions hooks for better null handling
* feat: mention agents
* fix: agent tool resource race conditions when deleting agent tool resource files
* feat: add error handling for code execution with user feedback
* refactor: rename AdminControls to AdminSettings for clarity
* style: add gap to button in AdminSettings for improved layout
* refactor: separate agent query hooks and check access to enable fetching
* fix: remove unused provider from agent initialization options, creates issue with custom endpoints
* refactor: remove redundant/deprecated modelOptions from AgentClient processes
* chore: update @librechat/agents to version 1.8.5 in package.json and package-lock.json
* fix: minor styling issues + agent panel uniformity
* fix: agent edge cases when set endpoint is no longer defined
* refactor: remove unused cleanup function call from AppService
* fix: update link in ApiKeyDialog to point to pricing page
* fix: improve type handling and layout calculations in SidePanel component
* fix: add missing localization string for agent selection in SidePanel
* chore: form styling and localizations for upload filesearch/code interpreter
* fix: model selection placeholder logic in AgentConfig component
* style: agent capabilities
* fix: add localization for provider selection and improve dropdown styling in ModelPanel
* refactor: use gpt-4o-mini > gpt-3.5-turbo
* fix: agents configuration for loadDefaultInterface and update related tests
* feat: DALLE Agents support
2024-12-04 15:48:13 -05:00
|
|
|
import type { TAttachment } from 'librechat-data-provider';
|
2024-09-30 17:17:57 -04:00
|
|
|
import ProgressText from '~/components/Chat/Messages/Content/ProgressText';
|
|
|
|
|
import MarkdownLite from '~/components/Chat/Messages/Content/MarkdownLite';
|
2025-02-11 18:00:38 -05:00
|
|
|
import { useProgress, useLocalize } from '~/hooks';
|
2025-05-16 17:50:18 +02:00
|
|
|
import { AttachmentGroup } from './Attachment';
|
2025-04-08 23:18:50 -04:00
|
|
|
import Stdout from './Stdout';
|
2025-05-16 17:50:18 +02:00
|
|
|
import { cn } from '~/utils';
|
2024-09-30 17:17:57 -04:00
|
|
|
import store from '~/store';
|
|
|
|
|
|
|
|
|
|
interface ParsedArgs {
|
|
|
|
|
lang: string;
|
|
|
|
|
code: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useParseArgs(args: string): ParsedArgs {
|
|
|
|
|
return useMemo(() => {
|
2025-04-08 23:18:50 -04:00
|
|
|
let parsedArgs: ParsedArgs | string = args;
|
|
|
|
|
try {
|
|
|
|
|
parsedArgs = JSON.parse(args);
|
|
|
|
|
} catch {
|
|
|
|
|
// console.error('Failed to parse args:', e);
|
|
|
|
|
}
|
|
|
|
|
if (typeof parsedArgs === 'object') {
|
|
|
|
|
return parsedArgs;
|
|
|
|
|
}
|
2024-09-30 17:17:57 -04:00
|
|
|
const langMatch = args.match(/"lang"\s*:\s*"(\w+)"/);
|
2025-04-08 23:18:50 -04:00
|
|
|
const codeMatch = args.match(/"code"\s*:\s*"(.+?)(?="\s*,\s*"(session_id|args)"|"\s*})/s);
|
2024-09-30 17:17:57 -04:00
|
|
|
|
|
|
|
|
let code = '';
|
|
|
|
|
if (codeMatch) {
|
|
|
|
|
code = codeMatch[1];
|
|
|
|
|
if (code.endsWith('"}')) {
|
|
|
|
|
code = code.slice(0, -2);
|
|
|
|
|
}
|
2025-04-08 23:18:50 -04:00
|
|
|
code = code.replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
2024-09-30 17:17:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
lang: langMatch ? langMatch[1] : '',
|
|
|
|
|
code,
|
|
|
|
|
};
|
|
|
|
|
}, [args]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function ExecuteCode({
|
|
|
|
|
initialProgress = 0.1,
|
|
|
|
|
args,
|
|
|
|
|
output = '',
|
|
|
|
|
attachments,
|
|
|
|
|
}: {
|
|
|
|
|
initialProgress: number;
|
|
|
|
|
args: string;
|
|
|
|
|
output?: string;
|
|
|
|
|
attachments?: TAttachment[];
|
|
|
|
|
}) {
|
2025-02-11 18:00:38 -05:00
|
|
|
const localize = useLocalize();
|
2024-09-30 17:17:57 -04:00
|
|
|
const showAnalysisCode = useRecoilValue(store.showCode);
|
|
|
|
|
const [showCode, setShowCode] = useState(showAnalysisCode);
|
2025-05-16 17:50:18 +02:00
|
|
|
const codeContentRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
const [contentHeight, setContentHeight] = useState<number | undefined>(0);
|
|
|
|
|
const [isAnimating, setIsAnimating] = useState(false);
|
|
|
|
|
const hasOutput = output.length > 0;
|
|
|
|
|
const outputRef = useRef<string>(output);
|
|
|
|
|
const prevShowCodeRef = useRef<boolean>(showCode);
|
2024-09-30 17:17:57 -04:00
|
|
|
|
|
|
|
|
const { lang, code } = useParseArgs(args);
|
|
|
|
|
const progress = useProgress(initialProgress);
|
2025-05-16 17:50:18 +02:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (output !== outputRef.current) {
|
|
|
|
|
outputRef.current = output;
|
|
|
|
|
|
|
|
|
|
if (showCode && codeContentRef.current) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (codeContentRef.current) {
|
|
|
|
|
const newHeight = codeContentRef.current.scrollHeight;
|
|
|
|
|
setContentHeight(newHeight);
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [output, showCode]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (showCode !== prevShowCodeRef.current) {
|
|
|
|
|
prevShowCodeRef.current = showCode;
|
|
|
|
|
|
|
|
|
|
if (showCode && codeContentRef.current) {
|
|
|
|
|
setIsAnimating(true);
|
|
|
|
|
requestAnimationFrame(() => {
|
|
|
|
|
if (codeContentRef.current) {
|
|
|
|
|
const height = codeContentRef.current.scrollHeight;
|
|
|
|
|
setContentHeight(height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
setIsAnimating(false);
|
|
|
|
|
}, 500);
|
|
|
|
|
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
});
|
|
|
|
|
} else if (!showCode) {
|
|
|
|
|
setIsAnimating(true);
|
|
|
|
|
setContentHeight(0);
|
|
|
|
|
|
|
|
|
|
const timer = setTimeout(() => {
|
|
|
|
|
setIsAnimating(false);
|
|
|
|
|
}, 500);
|
|
|
|
|
|
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [showCode]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!codeContentRef.current) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
|
|
|
if (showCode && !isAnimating) {
|
|
|
|
|
for (const entry of entries) {
|
|
|
|
|
if (entry.target === codeContentRef.current) {
|
|
|
|
|
setContentHeight(entry.contentRect.height);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
resizeObserver.observe(codeContentRef.current);
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
resizeObserver.disconnect();
|
|
|
|
|
};
|
|
|
|
|
}, [showCode, isAnimating]);
|
2024-09-30 17:17:57 -04:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
2025-05-16 17:50:18 +02:00
|
|
|
<div className="relative my-2.5 flex size-5 shrink-0 items-center gap-2.5">
|
2024-09-30 17:17:57 -04:00
|
|
|
<ProgressText
|
|
|
|
|
progress={progress}
|
|
|
|
|
onClick={() => setShowCode((prev) => !prev)}
|
2025-02-11 18:00:38 -05:00
|
|
|
inProgressText={localize('com_ui_analyzing')}
|
|
|
|
|
finishedText={localize('com_ui_analyzing_finished')}
|
2024-09-30 17:17:57 -04:00
|
|
|
hasInput={!!code.length}
|
2025-02-11 18:00:38 -05:00
|
|
|
isExpanded={showCode}
|
2024-09-30 17:17:57 -04:00
|
|
|
/>
|
|
|
|
|
</div>
|
2025-05-16 17:50:18 +02:00
|
|
|
<div
|
|
|
|
|
className="relative mb-2"
|
|
|
|
|
style={{
|
|
|
|
|
height: showCode ? contentHeight : 0,
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
transition:
|
|
|
|
|
'height 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
|
|
|
|
|
opacity: showCode ? 1 : 0,
|
|
|
|
|
transformOrigin: 'top',
|
|
|
|
|
willChange: 'height, opacity',
|
|
|
|
|
perspective: '1000px',
|
|
|
|
|
backfaceVisibility: 'hidden',
|
|
|
|
|
WebkitFontSmoothing: 'subpixel-antialiased',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
'code-analyze-block mt-0.5 overflow-hidden rounded-xl bg-surface-primary',
|
|
|
|
|
showCode && 'shadow-lg',
|
|
|
|
|
)}
|
|
|
|
|
ref={codeContentRef}
|
|
|
|
|
style={{
|
|
|
|
|
transform: showCode ? 'translateY(0) scale(1)' : 'translateY(-8px) scale(0.98)',
|
|
|
|
|
opacity: showCode ? 1 : 0,
|
|
|
|
|
transition:
|
|
|
|
|
'transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{showCode && (
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
transform: showCode ? 'translateY(0)' : 'translateY(-4px)',
|
|
|
|
|
opacity: showCode ? 1 : 0,
|
|
|
|
|
transition:
|
|
|
|
|
'transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.35s cubic-bezier(0.16, 1, 0.3, 1)',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<MarkdownLite
|
|
|
|
|
content={code ? `\`\`\`${lang}\n${code}\n\`\`\`` : ''}
|
|
|
|
|
codeExecution={false}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
{hasOutput && (
|
|
|
|
|
<div
|
|
|
|
|
className={cn(
|
|
|
|
|
'bg-surface-tertiary p-4 text-xs',
|
|
|
|
|
showCode ? 'border-t border-surface-primary-contrast' : '',
|
|
|
|
|
)}
|
|
|
|
|
style={{
|
|
|
|
|
transform: showCode ? 'translateY(0)' : 'translateY(-6px)',
|
|
|
|
|
opacity: showCode ? 1 : 0,
|
|
|
|
|
transition:
|
|
|
|
|
'transform 0.45s cubic-bezier(0.16, 1, 0.3, 1) 0.05s, opacity 0.45s cubic-bezier(0.19, 1, 0.22, 1) 0.05s',
|
|
|
|
|
boxShadow: showCode ? '0 -1px 0 rgba(0,0,0,0.05)' : 'none',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<div className="prose flex flex-col-reverse">
|
2025-04-08 23:18:50 -04:00
|
|
|
<Stdout output={output} />
|
2024-09-30 17:17:57 -04:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-04-08 23:18:50 -04:00
|
|
|
</div>
|
2025-05-16 17:50:18 +02:00
|
|
|
{attachments && attachments.length > 0 && <AttachmentGroup attachments={attachments} />}
|
2024-09-30 17:17:57 -04:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|