Skip to content

Commit 672592b

Browse files
committed
allow user to remove file if it's not processed yet
1 parent 0eba048 commit 672592b

File tree

8 files changed

+265
-35
lines changed

8 files changed

+265
-35
lines changed

AgentCrew/modules/chat/message/command_processor.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ async def process_command(self, user_input: str) -> CommandResult:
105105
elif user_input.startswith("/file "):
106106
return self._handle_file_command(user_input)
107107

108+
# Drop command
109+
elif user_input.startswith("/drop "):
110+
return self._handle_drop_command(user_input)
111+
108112
# Not a command
109113
return CommandResult(handled=False)
110114

@@ -507,3 +511,50 @@ def _handle_file_command(self, user_input: str) -> CommandResult:
507511
)
508512

509513
return CommandResult(handled=True, clear_flag=True)
514+
515+
def _handle_drop_command(self, user_input: str) -> CommandResult:
516+
"""Handle drop command to remove queued files."""
517+
# Extract file identifier from user input
518+
file_path = user_input[6:].strip() # Remove "/drop " prefix
519+
520+
if not file_path:
521+
# Show available files if no argument provided
522+
if not self.message_handler._queued_attached_files:
523+
self.message_handler._notify(
524+
"error", "No files are currently queued for processing"
525+
)
526+
return CommandResult(handled=True, clear_flag=True)
527+
528+
self.message_handler._notify(
529+
"system_message",
530+
"📋 Queued files:\n"
531+
+ "\n".join(self.message_handler._queued_attached_files)
532+
+ "\nUsage: /drop <file_id>",
533+
)
534+
return CommandResult(handled=True, clear_flag=True)
535+
536+
try:
537+
# Get the file command and parse its file paths
538+
try:
539+
self.message_handler._queued_attached_files.remove(file_path)
540+
except Exception:
541+
self.message_handler._notify(
542+
"error", f"Cannot unqueue file: {file_path}"
543+
)
544+
545+
# Update or remove the command
546+
self.message_handler._notify(
547+
"system_message", f"🗑️ Removed file from queue: {file_path}"
548+
)
549+
550+
# Notify about the file being removed for UI updates
551+
self.message_handler._notify("file_dropped", {"file_path": file_path})
552+
553+
return CommandResult(handled=True, clear_flag=True)
554+
555+
except ValueError as e:
556+
self.message_handler._notify("error", f"Invalid file ID format: {str(e)}")
557+
return CommandResult(handled=True, clear_flag=True)
558+
except Exception as e:
559+
self.message_handler._notify("error", f"Error removing file: {str(e)}")
560+
return CommandResult(handled=True, clear_flag=True)

AgentCrew/modules/chat/message/handler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ def _messages_append(self, message):
7979
self.streamline_messages.extend(std_msg)
8080

8181
def _prepare_files_processing(self, file_command):
82-
self._queued_attached_files.append(file_command)
8382
file_paths_str: str = file_command[6:].strip()
8483
file_paths: List[str] = [
8584
os.path.expanduser(path.strip())
@@ -88,6 +87,7 @@ def _prepare_files_processing(self, file_command):
8887
]
8988

9089
for file_path in file_paths:
90+
self._queued_attached_files.append(file_path)
9191
self._notify("file_processing", {"file_path": file_path})
9292

9393
async def process_user_input(
@@ -146,7 +146,7 @@ async def process_user_input(
146146

147147
while len(self._queued_attached_files) > 0:
148148
file_command = self._queued_attached_files.pop(0)
149-
await self.command_processor.process_command(file_command)
149+
await self.command_processor.process_command(f"/file {file_command}")
150150

151151
# Add regular text message
152152
self._messages_append(

AgentCrew/modules/console/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@
1414
"UIEffects",
1515
"ConfirmationHandler",
1616
"ConversationHandler",
17-
]
17+
]

AgentCrew/modules/console/completers.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import os
66
import re
77

8-
COMPLETER_PATTERN = re.compile(r"[A-z0-9-_.]+")
8+
COMPLETER_PATTERN = re.compile(r"[a-zA-Z0-9-_.]+")
99

1010

1111
class JumpCompleter(Completer):
@@ -120,6 +120,7 @@ def __init__(self, message_handler=None):
120120
self.agent_completer = AgentCompleter()
121121
self.jump_completer = JumpCompleter(message_handler)
122122
self.mcp_completer = MCPCompleter(message_handler)
123+
self.drop_completer = DropCompleter(message_handler)
123124

124125
def get_completions(self, document, complete_event):
125126
text = document.text
@@ -137,6 +138,8 @@ def get_completions(self, document, complete_event):
137138
yield from self.mcp_completer.get_completions(document, complete_event)
138139
elif text.startswith("/file "):
139140
yield from self.file_completer.get_completions(document, complete_event)
141+
elif text.startswith("/drop "):
142+
yield from self.drop_completer.get_completions(document, complete_event)
140143
elif text.startswith("/"):
141144
yield from self.get_command_completions(document)
142145

@@ -169,6 +172,7 @@ def get_command_completions(self, document):
169172
"List MCP prompts or fetch specific prompt (usage: /mcp [server_id/prompt_name])",
170173
),
171174
("/file", "Process a file (usage: /file <path>)"),
175+
("/drop", "Remove a queued file from processing (usage: /drop <file_id>)"),
172176
("/list", "List available conversations"),
173177
("/load", "Load a conversation (usage: /load <conversation_id>)"),
174178
("/help", "Show help message"),
@@ -219,6 +223,43 @@ def get_completions(self, document, complete_event):
219223
)
220224

221225

226+
class DropCompleter(Completer):
227+
"""Completer that shows available queued files when typing /drop command."""
228+
229+
def __init__(self, message_handler=None):
230+
self.message_handler = message_handler
231+
232+
def get_completions(self, document, complete_event):
233+
text = document.text
234+
235+
# Only provide completions for the /drop command
236+
if text.startswith("/drop "):
237+
word_before_cursor = document.get_word_before_cursor(
238+
pattern=COMPLETER_PATTERN
239+
)
240+
241+
# Get all queued attached files
242+
queued_files = (
243+
self.message_handler._queued_attached_files
244+
if self.message_handler
245+
else []
246+
)
247+
248+
# Extract file paths from queued files and create completions
249+
for file_path in queued_files:
250+
if file_path.startswith(word_before_cursor):
251+
yield Completion(
252+
file_path,
253+
start_position=-len(word_before_cursor),
254+
display=file_path,
255+
)
256+
elif word_before_cursor.startswith("drop"):
257+
yield Completion(
258+
file_path,
259+
display=file_path,
260+
)
261+
262+
222263
class DirectoryListingCompleter(Completer):
223264
def __init__(self):
224265
# Use PathCompleter for the heavy lifting
@@ -230,7 +271,7 @@ def get_completions(self, document, complete_event):
230271
return
231272
# Look for patterns that might indicate a path
232273
# This regex searches for a potential directory path
233-
path_match = re.search(r"((~|\.{1,2})?/[^\s]*|~)$", text)
274+
path_match = re.search(r"((~|\.{1,2})?/[^\s/]*|~)$", text)
234275

235276
if path_match:
236277
path = path_match.group(0)

AgentCrew/modules/console/console_ui.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ def listen(self, event: str, data: Any = None):
184184
elif event == "file_processing":
185185
self.ui_effects.stop_loading_animation() # Stop loading on first chunk
186186
self.display_handlers.add_file(data["file_path"])
187+
elif event == "file_dropped":
188+
self.display_handlers._added_files.remove(data["file_path"])
187189
elif event == "consolidation_completed":
188190
self.display_handlers.display_consolidation_result(data)
189191
elif event == "conversations_listed":

AgentCrew/modules/gui/components/chat_components.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,12 @@ def append_file(self, file_path, is_user=False, is_base64=False):
9898
else:
9999
message_bubble.display_file(file_path)
100100

101+
# Connect remove button if it exists
102+
if message_bubble.remove_button:
103+
message_bubble.remove_button.clicked.connect(
104+
lambda: self._handle_file_remove(message_bubble)
105+
)
106+
101107
if is_user:
102108
container_layout.addStretch(1) # Push to left
103109
container_layout.addWidget(message_bubble, 1)
@@ -278,3 +284,48 @@ def remove_messages_after(self, message_bubble):
278284
self.chat_window.current_response_bubble = None
279285
self.chat_window.current_response_container = None
280286
self.chat_window.expecting_response = False
287+
288+
def _handle_file_remove(self, message_bubble):
289+
"""Handle removal of a file from the processing queue."""
290+
try:
291+
if message_bubble.file_path:
292+
# Use the chat window's worker to process the drop command
293+
self.chat_window.llm_worker.process_request.emit(
294+
f"/drop {message_bubble.file_path}"
295+
)
296+
297+
# Remove the message bubble from the UI immediately
298+
self._remove_file_bubble(message_bubble)
299+
300+
except Exception as e:
301+
print(f"Error removing file: {e}")
302+
303+
def _remove_file_bubble(self, message_bubble):
304+
"""Remove a specific file bubble from the chat UI."""
305+
# Find the container that holds this message bubble
306+
for i in range(self.chat_window.chat_layout.count()):
307+
item = self.chat_window.chat_layout.itemAt(i)
308+
if item and item.widget():
309+
# Check if this widget contains our message bubble
310+
from AgentCrew.modules.gui.widgets.message_bubble import MessageBubble
311+
312+
if message_bubble in item.widget().findChildren(MessageBubble):
313+
# Remove the container
314+
container = self.chat_window.chat_layout.takeAt(i)
315+
if container.widget():
316+
container.widget().deleteLater()
317+
break
318+
319+
def mark_file_processed(self, file_path):
320+
"""Mark a file as processed in all relevant file bubbles."""
321+
# Find all file bubbles with matching file path and mark them as processed
322+
for i in range(self.chat_window.chat_layout.count()):
323+
item = self.chat_window.chat_layout.itemAt(i)
324+
if item and item.widget():
325+
# Find message bubbles in this container
326+
from AgentCrew.modules.gui.widgets.message_bubble import MessageBubble
327+
328+
message_bubbles = item.widget().findChildren(MessageBubble)
329+
for bubble in message_bubbles:
330+
if hasattr(bubble, "file_path") and bubble.file_path == file_path:
331+
bubble.mark_file_processed()

AgentCrew/modules/gui/qt_ui.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ def handle_event(self, event: str, data: Any):
510510
if not self.loading_conversation:
511511
self.ui_state_manager.set_input_controls_enabled(True)
512512
elif event == "file_processed":
513+
# Mark the file as processed in the chat components
514+
file_path = data.get("file_path")
515+
if file_path:
516+
self.chat_components.mark_file_processed(file_path)
513517
self.current_file_bubble = None
514518
elif event == "image_generated":
515519
self.chat_components.append_file(data, False, True)

0 commit comments

Comments
 (0)