mirror of https://github.com/kortix-ai/suna.git
AI: can we streamline the edit-file just like the create_file tool, in the front end ?
like stream the code-edit output of the agent; Then we should be able to show the diff as well, when the tool complete. We get the original file content, and the full updated code (output of morph) and some how send it to the front end. the front end should show this properly , concisely so user can see the changes in green / red. This shouldn't change the content feed to the model btw. Like it will pollute the context. make a plan what to do first, not make changes yet
This commit is contained in:
parent
2608ad2ef1
commit
9a0dc4e200
|
@ -525,10 +525,13 @@ def authenticate_user(username, password):
|
|||
# AI editing successful
|
||||
await self.sandbox.fs.upload_file(new_content.encode(), full_path)
|
||||
|
||||
|
||||
message = f"File '{target_file}' edited successfully."
|
||||
|
||||
return self.success_response(message)
|
||||
# Return rich data for frontend diff view
|
||||
return self.success_response({
|
||||
"message": f"File '{target_file}' edited successfully.",
|
||||
"file_path": target_file,
|
||||
"original_content": original_content,
|
||||
"updated_content": new_content
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Unhandled error in edit_file: {str(e)}", exc_info=True)
|
||||
|
|
|
@ -1637,23 +1637,35 @@ class ResponseProcessor:
|
|||
# Determine message role based on strategy
|
||||
result_role = "user" if strategy == "user_message" else "assistant"
|
||||
|
||||
# Create the new structured tool result format
|
||||
structured_result = self._create_structured_tool_result(tool_call, result, parsing_details)
|
||||
|
||||
# Create two versions of the structured result
|
||||
# 1. Rich version for the frontend
|
||||
structured_result_for_frontend = self._create_structured_tool_result(tool_call, result, parsing_details, for_llm=False)
|
||||
# 2. Concise version for the LLM
|
||||
structured_result_for_llm = self._create_structured_tool_result(tool_call, result, parsing_details, for_llm=True)
|
||||
|
||||
# Add the message with the appropriate role to the conversation history
|
||||
# This allows the LLM to see the tool result in subsequent interactions
|
||||
result_message = {
|
||||
result_message_for_llm = {
|
||||
"role": result_role,
|
||||
"content": json.dumps(structured_result)
|
||||
"content": json.dumps(structured_result_for_llm)
|
||||
}
|
||||
message_obj = await self.add_message(
|
||||
thread_id=thread_id,
|
||||
type="tool",
|
||||
content=result_message,
|
||||
content=result_message_for_llm, # Save the LLM-friendly version
|
||||
is_llm_message=True,
|
||||
metadata=metadata
|
||||
)
|
||||
return message_obj # Return the full message object
|
||||
|
||||
# If the message was saved, modify it in-memory for the frontend before returning
|
||||
if message_obj:
|
||||
result_message_for_frontend = {
|
||||
"role": result_role,
|
||||
"content": json.dumps(structured_result_for_frontend)
|
||||
}
|
||||
message_obj['content'] = result_message_for_frontend
|
||||
|
||||
return message_obj # Return the modified message object
|
||||
except Exception as e:
|
||||
logger.error(f"Error adding tool result: {str(e)}", exc_info=True)
|
||||
self.trace.event(name="error_adding_tool_result", level="ERROR", status_message=(f"Error adding tool result: {str(e)}"), metadata={"tool_call": tool_call, "result": result, "strategy": strategy, "assistant_message_id": assistant_message_id, "parsing_details": parsing_details})
|
||||
|
@ -1676,13 +1688,14 @@ class ResponseProcessor:
|
|||
self.trace.event(name="failed_even_with_fallback_message", level="ERROR", status_message=(f"Failed even with fallback message: {str(e2)}"), metadata={"tool_call": tool_call, "result": result, "strategy": strategy, "assistant_message_id": assistant_message_id, "parsing_details": parsing_details})
|
||||
return None # Return None on error
|
||||
|
||||
def _create_structured_tool_result(self, tool_call: Dict[str, Any], result: ToolResult, parsing_details: Optional[Dict[str, Any]] = None):
|
||||
def _create_structured_tool_result(self, tool_call: Dict[str, Any], result: ToolResult, parsing_details: Optional[Dict[str, Any]] = None, for_llm: bool = False):
|
||||
"""Create a structured tool result format that's tool-agnostic and provides rich information.
|
||||
|
||||
Args:
|
||||
tool_call: The original tool call that was executed
|
||||
result: The result from the tool execution
|
||||
parsing_details: Optional parsing details for XML calls
|
||||
for_llm: If True, creates a concise version for the LLM context.
|
||||
|
||||
Returns:
|
||||
Structured dictionary containing tool execution information
|
||||
|
@ -1692,7 +1705,6 @@ class ResponseProcessor:
|
|||
xml_tag_name = tool_call.get("xml_tag_name")
|
||||
arguments = tool_call.get("arguments", {})
|
||||
tool_call_id = tool_call.get("id")
|
||||
logger.info(f"Creating structured tool result for tool_call: {tool_call}")
|
||||
|
||||
# Process the output - if it's a JSON string, parse it back to an object
|
||||
output = result.output if hasattr(result, 'output') else str(result)
|
||||
|
@ -1707,7 +1719,12 @@ class ResponseProcessor:
|
|||
except Exception:
|
||||
# If parsing fails, keep the original string
|
||||
pass
|
||||
|
||||
|
||||
# If this is for the LLM and it's an edit_file tool, create a concise output
|
||||
if for_llm and function_name == 'edit_file' and isinstance(output, dict):
|
||||
output_for_llm = {"message": output.get("message", "File edited successfully.")}
|
||||
output = output_for_llm
|
||||
|
||||
# Create the structured result
|
||||
structured_result_v1 = {
|
||||
"tool_execution": {
|
||||
|
@ -1717,53 +1734,11 @@ class ResponseProcessor:
|
|||
"arguments": arguments,
|
||||
"result": {
|
||||
"success": result.success if hasattr(result, 'success') else True,
|
||||
"output": output, # Now properly structured for frontend
|
||||
"output": output, # This will be either rich or concise based on `for_llm`
|
||||
"error": getattr(result, 'error', None) if hasattr(result, 'error') else None
|
||||
},
|
||||
# "execution_details": {
|
||||
# "timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
# "parsing_details": parsing_details
|
||||
# }
|
||||
}
|
||||
}
|
||||
|
||||
# STRUCTURED_OUTPUT_TOOLS = {
|
||||
# "str_replace",
|
||||
# "get_data_provider_endpoints",
|
||||
# }
|
||||
|
||||
# summary_output = result.output if hasattr(result, 'output') else str(result)
|
||||
|
||||
# if xml_tag_name:
|
||||
# status = "completed successfully" if structured_result_v1["tool_execution"]["result"]["success"] else "failed"
|
||||
# summary = f"Tool '{xml_tag_name}' {status}. Output: {summary_output}"
|
||||
# else:
|
||||
# status = "completed successfully" if structured_result_v1["tool_execution"]["result"]["success"] else "failed"
|
||||
# summary = f"Function '{function_name}' {status}. Output: {summary_output}"
|
||||
|
||||
# if self.is_agent_builder:
|
||||
# return summary
|
||||
# if function_name in STRUCTURED_OUTPUT_TOOLS:
|
||||
# return structured_result_v1
|
||||
# else:
|
||||
# return summary
|
||||
|
||||
summary_output = result.output if hasattr(result, 'output') else str(result)
|
||||
success_status = structured_result_v1["tool_execution"]["result"]["success"]
|
||||
|
||||
# # Create a more comprehensive summary for the LLM
|
||||
# if xml_tag_name:
|
||||
# status = "completed successfully" if structured_result_v1["tool_execution"]["result"]["success"] else "failed"
|
||||
# summary = f"Tool '{xml_tag_name}' {status}. Output: {summary_output}"
|
||||
# else:
|
||||
# status = "completed successfully" if structured_result_v1["tool_execution"]["result"]["success"] else "failed"
|
||||
# summary = f"Function '{function_name}' {status}. Output: {summary_output}"
|
||||
|
||||
# if self.is_agent_builder:
|
||||
# return summary
|
||||
# elif function_name == "get_data_provider_endpoints":
|
||||
# logger.info(f"Returning sumnary for data provider call: {summary}")
|
||||
# return summary
|
||||
|
||||
return structured_result_v1
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ const FILE_OPERATION_TOOLS = new Set([
|
|||
'Delete File',
|
||||
'Full File Rewrite',
|
||||
'Read File',
|
||||
'AI File Edit',
|
||||
]);
|
||||
|
||||
interface ShowToolStreamProps {
|
||||
|
@ -38,6 +39,18 @@ export const ShowToolStream: React.FC<ShowToolStreamProps> = ({
|
|||
}
|
||||
|
||||
const toolName = extractToolNameFromStream(content);
|
||||
const isEditFile = toolName === 'AI File Edit';
|
||||
|
||||
// Extract code_edit content for streaming
|
||||
const codeEditContent = React.useMemo(() => {
|
||||
if (!isEditFile || !content) return '';
|
||||
const match = content.match(/<code_edit>([\s\S]*)/);
|
||||
if (match) {
|
||||
// Remove closing tag if present
|
||||
return match[1].replace(/<\/code_edit>[\s\S]*$/, '');
|
||||
}
|
||||
return '';
|
||||
}, [content, isEditFile]);
|
||||
|
||||
// Time-based logic - show streaming content after 1500ms
|
||||
useEffect(() => {
|
||||
|
@ -97,7 +110,7 @@ export const ShowToolStream: React.FC<ShowToolStreamProps> = ({
|
|||
const paramDisplay = extractPrimaryParam(toolName, content);
|
||||
|
||||
// Always show tool button, conditionally show content below for file operations only
|
||||
if (showExpanded && isFileOperationTool) {
|
||||
if (showExpanded && (isFileOperationTool || isEditFile)) {
|
||||
return (
|
||||
<div className="my-1">
|
||||
{shouldShowContent ? (
|
||||
|
@ -126,7 +139,7 @@ export const ShowToolStream: React.FC<ShowToolStreamProps> = ({
|
|||
WebkitMaskImage: 'linear-gradient(to bottom, transparent 0%, black 8%, black 92%, transparent 100%)'
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
{isEditFile ? codeEditContent : content}
|
||||
</div>
|
||||
{/* Top gradient */}
|
||||
<div className={`absolute top-0 left-0 right-0 h-8 pointer-events-none transition-all duration-500 ease-in-out ${shouldShowContent
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
import React, { useState } from 'react';
|
||||
import {
|
||||
FileDiff,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
Loader2,
|
||||
File,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Minus,
|
||||
Plus,
|
||||
} from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import {
|
||||
extractFileEditData,
|
||||
generateLineDiff,
|
||||
calculateDiffStats,
|
||||
LineDiff,
|
||||
DiffStats
|
||||
} from './_utils';
|
||||
import { formatTimestamp, getToolTitle } from '../utils';
|
||||
import { ToolViewProps } from '../types';
|
||||
import { LoadingState } from '../shared/LoadingState';
|
||||
import ReactDiffViewer from 'react-diff-viewer-continued';
|
||||
|
||||
const UnifiedDiffView: React.FC<{ oldCode: string; newCode: string }> = ({ oldCode, newCode }) => (
|
||||
<ReactDiffViewer
|
||||
oldValue={oldCode}
|
||||
newValue={newCode}
|
||||
splitView={false}
|
||||
useDarkTheme={document.documentElement.classList.contains('dark')}
|
||||
styles={{
|
||||
variables: {
|
||||
dark: {
|
||||
color: '#e2e8f0',
|
||||
background: '#09090b',
|
||||
addedBackground: '#104a32',
|
||||
addedColor: '#6ee7b7',
|
||||
removedBackground: '#5c1a2e',
|
||||
removedColor: '#fca5a5',
|
||||
},
|
||||
},
|
||||
diffContainer: {
|
||||
backgroundColor: 'var(--card)',
|
||||
border: 'none',
|
||||
},
|
||||
gutter: {
|
||||
backgroundColor: 'var(--muted)',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--accent)',
|
||||
},
|
||||
},
|
||||
line: {
|
||||
fontFamily: 'monospace',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const SplitDiffView: React.FC<{ oldCode: string; newCode: string }> = ({ oldCode, newCode }) => (
|
||||
<ReactDiffViewer
|
||||
oldValue={oldCode}
|
||||
newValue={newCode}
|
||||
splitView={true}
|
||||
useDarkTheme={document.documentElement.classList.contains('dark')}
|
||||
styles={{
|
||||
variables: {
|
||||
dark: {
|
||||
color: '#e2e8f0',
|
||||
background: '#09090b',
|
||||
addedBackground: '#104a32',
|
||||
addedColor: '#6ee7b7',
|
||||
removedBackground: '#5c1a2e',
|
||||
removedColor: '#fca5a5',
|
||||
},
|
||||
},
|
||||
diffContainer: {
|
||||
backgroundColor: 'var(--card)',
|
||||
border: 'none',
|
||||
},
|
||||
gutter: {
|
||||
backgroundColor: 'var(--muted)',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--accent)',
|
||||
},
|
||||
},
|
||||
line: {
|
||||
fontFamily: 'monospace',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const ErrorState: React.FC = () => (
|
||||
<div className="flex flex-col items-center justify-center h-full py-12 px-6 bg-gradient-to-b from-white to-zinc-50 dark:from-zinc-950 dark:to-zinc-900">
|
||||
<div className="text-center w-full max-w-xs">
|
||||
<AlertTriangle className="h-16 w-16 mx-auto mb-6 text-amber-500" />
|
||||
<h3 className="text-lg font-medium text-zinc-900 dark:text-zinc-100 mb-2">
|
||||
Invalid File Edit
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-500 dark:text-zinc-400">
|
||||
Could not extract the file changes from the tool result.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export function FileEditToolView({
|
||||
name = 'edit-file',
|
||||
assistantContent,
|
||||
toolContent,
|
||||
assistantTimestamp,
|
||||
toolTimestamp,
|
||||
isSuccess = true,
|
||||
isStreaming = false,
|
||||
}: ToolViewProps): JSX.Element {
|
||||
const [viewMode, setViewMode] = useState<'unified' | 'split'>('unified');
|
||||
|
||||
const {
|
||||
filePath,
|
||||
originalContent,
|
||||
updatedContent,
|
||||
actualIsSuccess,
|
||||
actualToolTimestamp,
|
||||
} = extractFileEditData(
|
||||
assistantContent,
|
||||
toolContent,
|
||||
isSuccess,
|
||||
toolTimestamp,
|
||||
assistantTimestamp
|
||||
);
|
||||
|
||||
const toolTitle = getToolTitle(name);
|
||||
|
||||
const lineDiff = originalContent && updatedContent ? generateLineDiff(originalContent, updatedContent) : [];
|
||||
const stats: DiffStats = calculateDiffStats(lineDiff);
|
||||
|
||||
const shouldShowError = !isStreaming && (!originalContent || !updatedContent);
|
||||
|
||||
return (
|
||||
<Card className="gap-0 flex border shadow-none border-t border-b-0 border-x-0 p-0 rounded-none flex-col h-full overflow-hidden bg-card">
|
||||
<CardHeader className="h-14 bg-zinc-50/80 dark:bg-zinc-900/80 backdrop-blur-sm border-b p-2 px-4 space-y-2">
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="relative p-2 rounded-lg bg-gradient-to-br from-blue-500/20 to-blue-600/10 border border-blue-500/20">
|
||||
<FileDiff className="w-5 h-5 text-blue-500 dark:text-blue-400" />
|
||||
</div>
|
||||
<CardTitle className="text-base font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{toolTitle}
|
||||
</CardTitle>
|
||||
</div>
|
||||
|
||||
{!isStreaming && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={
|
||||
actualIsSuccess
|
||||
? "bg-gradient-to-b from-emerald-200 to-emerald-100 text-emerald-700 dark:from-emerald-800/50 dark:to-emerald-900/60 dark:text-emerald-300"
|
||||
: "bg-gradient-to-b from-rose-200 to-rose-100 text-rose-700 dark:from-rose-800/50 dark:to-rose-900/60 dark:text-rose-300"
|
||||
}
|
||||
>
|
||||
{actualIsSuccess ? (
|
||||
<CheckCircle className="h-3.5 w-3.5 mr-1" />
|
||||
) : (
|
||||
<AlertTriangle className="h-3.5 w-3.5 mr-1" />
|
||||
)}
|
||||
{actualIsSuccess ? 'Edit applied' : 'Edit failed'}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="p-0 h-full flex-1 overflow-hidden relative">
|
||||
{isStreaming ? (
|
||||
<LoadingState
|
||||
icon={FileDiff}
|
||||
iconColor="text-blue-500 dark:text-blue-400"
|
||||
bgColor="bg-gradient-to-b from-blue-100 to-blue-50 shadow-inner dark:from-blue-800/40 dark:to-blue-900/60 dark:shadow-blue-950/20"
|
||||
title="Applying File Edit"
|
||||
filePath={filePath || 'Processing file...'}
|
||||
progressText="Analyzing changes"
|
||||
subtitle="Please wait while the file is being modified"
|
||||
/>
|
||||
) : shouldShowError ? (
|
||||
<ErrorState />
|
||||
) : (
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="p-3 border-b border-zinc-200 dark:border-zinc-800 bg-accent flex items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<File className="h-4 w-4 mr-2 text-zinc-500 dark:text-zinc-400" />
|
||||
<code className="text-sm font-mono text-zinc-700 dark:text-zinc-300">
|
||||
{filePath || 'Unknown file'}
|
||||
</code>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex items-center text-xs text-zinc-500 dark:text-zinc-400 gap-3">
|
||||
<div className="flex items-center">
|
||||
<Plus className="h-3.5 w-3.5 text-emerald-500 mr-1" />
|
||||
<span>{stats.additions}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Minus className="h-3.5 w-3.5 text-red-500 mr-1" />
|
||||
<span>{stats.deletions}</span>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs value={viewMode} onValueChange={(v) => setViewMode(v as 'unified' | 'split')} className="w-auto">
|
||||
<TabsList className="h-7 p-0.5">
|
||||
<TabsTrigger value="unified" className="text-xs h-6 px-2">Unified</TabsTrigger>
|
||||
<TabsTrigger value="split" className="text-xs h-6 px-2">Split</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
<ScrollArea className="flex-1">
|
||||
{viewMode === 'unified' ? (
|
||||
<UnifiedDiffView oldCode={originalContent!} newCode={updatedContent!} />
|
||||
) : (
|
||||
<SplitDiffView oldCode={originalContent!} newCode={updatedContent!} />
|
||||
)}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { LucideIcon, FilePen, Replace, Trash2, FileCode, FileSpreadsheet, File } from 'lucide-react';
|
||||
|
||||
export type FileOperation = 'create' | 'rewrite' | 'delete' | 'edit';
|
||||
export type FileOperation = 'create' | 'rewrite' | 'delete' | 'edit' | 'str-replace';
|
||||
|
||||
export interface OperationConfig {
|
||||
icon: LucideIcon;
|
||||
|
@ -77,12 +77,99 @@ export const getLanguageFromFileName = (fileName: string): string => {
|
|||
return extensionMap[extension] || 'text';
|
||||
};
|
||||
|
||||
export interface ExtractedEditData {
|
||||
filePath: string | null;
|
||||
originalContent: string | null;
|
||||
updatedContent: string | null;
|
||||
success?: boolean;
|
||||
timestamp?: string;
|
||||
}
|
||||
|
||||
export const extractFileEditData = (
|
||||
assistantContent: any,
|
||||
toolContent: any,
|
||||
isSuccess: boolean,
|
||||
toolTimestamp?: string,
|
||||
assistantTimestamp?: string
|
||||
): {
|
||||
filePath: string | null;
|
||||
originalContent: string | null;
|
||||
updatedContent: string | null;
|
||||
actualIsSuccess: boolean;
|
||||
actualToolTimestamp?: string;
|
||||
actualAssistantTimestamp?: string;
|
||||
} => {
|
||||
let filePath: string | null = null;
|
||||
let originalContent: string | null = null;
|
||||
let updatedContent: string | null = null;
|
||||
let actualIsSuccess = isSuccess;
|
||||
let actualToolTimestamp = toolTimestamp;
|
||||
let actualAssistantTimestamp = assistantTimestamp;
|
||||
|
||||
const parseOutput = (output: any) => {
|
||||
if (typeof output === 'string') {
|
||||
try {
|
||||
return JSON.parse(output);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
};
|
||||
|
||||
const extractData = (content: any) => {
|
||||
const parsed = typeof content === 'string' ? parseContent(content) : content;
|
||||
if (parsed?.tool_execution) {
|
||||
const args = parsed.tool_execution.arguments || {};
|
||||
const output = parseOutput(parsed.tool_execution.result?.output);
|
||||
return {
|
||||
filePath: args.target_file || output?.file_path || null,
|
||||
originalContent: output?.original_content || null,
|
||||
updatedContent: output?.updated_content || null,
|
||||
success: parsed.tool_execution.result?.success,
|
||||
timestamp: parsed.tool_execution.execution_details?.timestamp,
|
||||
};
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
const toolData = extractData(toolContent);
|
||||
const assistantData = extractData(assistantContent);
|
||||
|
||||
filePath = toolData.filePath || assistantData.filePath;
|
||||
originalContent = toolData.originalContent || assistantData.originalContent;
|
||||
updatedContent = toolData.updatedContent || assistantData.updatedContent;
|
||||
|
||||
if (toolData.success !== undefined) {
|
||||
actualIsSuccess = toolData.success;
|
||||
actualToolTimestamp = toolData.timestamp || toolTimestamp;
|
||||
} else if (assistantData.success !== undefined) {
|
||||
actualIsSuccess = assistantData.success;
|
||||
actualAssistantTimestamp = assistantData.timestamp || assistantTimestamp;
|
||||
}
|
||||
|
||||
return { filePath, originalContent, updatedContent, actualIsSuccess, actualToolTimestamp, actualAssistantTimestamp };
|
||||
};
|
||||
|
||||
const parseContent = (content: any): any => {
|
||||
if (typeof content === 'string') {
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
|
||||
export const getOperationType = (name?: string, assistantContent?: any): FileOperation => {
|
||||
if (name) {
|
||||
if (name.includes('create')) return 'create';
|
||||
if (name.includes('rewrite')) return 'rewrite';
|
||||
if (name.includes('delete')) return 'delete';
|
||||
if (name.includes('edit')) return 'edit';
|
||||
if (name.includes('edit-file')) return 'edit'; // Specific for edit_file
|
||||
if (name.includes('str-replace')) return 'str-replace';
|
||||
}
|
||||
|
||||
if (!assistantContent) return 'create';
|
||||
|
|
|
@ -5,6 +5,7 @@ import { BrowserToolView } from '../BrowserToolView';
|
|||
import { CommandToolView } from '../command-tool/CommandToolView';
|
||||
import { ExposePortToolView } from '../expose-port-tool/ExposePortToolView';
|
||||
import { FileOperationToolView } from '../file-operation/FileOperationToolView';
|
||||
import { FileEditToolView } from '../file-operation/FileEditToolView';
|
||||
import { StrReplaceToolView } from '../str-replace/StrReplaceToolView';
|
||||
import { WebCrawlToolView } from '../WebCrawlToolView';
|
||||
import { WebScrapeToolView } from '../web-scrape-tool/WebScrapeToolView';
|
||||
|
@ -56,7 +57,7 @@ const defaultRegistry: ToolViewRegistryType = {
|
|||
'delete-file': FileOperationToolView,
|
||||
'full-file-rewrite': FileOperationToolView,
|
||||
'read-file': FileOperationToolView,
|
||||
'edit-file': FileOperationToolView,
|
||||
'edit-file': FileEditToolView,
|
||||
|
||||
'str-replace': StrReplaceToolView,
|
||||
|
||||
|
|
Loading…
Reference in New Issue