|
1 | 1 | import asyncio
|
| 2 | +import os |
2 | 3 | from abc import ABC, ABCMeta, abstractmethod
|
3 | 4 | from dataclasses import asdict
|
4 | 5 | from logging import Logger
|
@@ -346,7 +347,76 @@ def get_mcp_config(self) -> dict[str, Any]:
|
346 | 347 | Returns the MCP config for the current chat.
|
347 | 348 | """
|
348 | 349 | return self.parent.get_mcp_config()
|
349 |
| - |
| 350 | + |
| 351 | + def process_attachments(self, message: Message) -> Optional[str]: |
| 352 | + """ |
| 353 | + Process file attachments in the message and return their content as a string. |
| 354 | + """ |
| 355 | + |
| 356 | + if not message.attachments: |
| 357 | + return None |
| 358 | + |
| 359 | + context_parts = [] |
| 360 | + |
| 361 | + for attachment_id in message.attachments: |
| 362 | + self.log.info(f"FILE: Processing attachment with ID: {attachment_id}") |
| 363 | + try: |
| 364 | + # Try to resolve attachment using multiple strategies |
| 365 | + file_path = self.resolve_attachment_to_path(attachment_id) |
| 366 | + |
| 367 | + if not file_path: |
| 368 | + self.log.warning(f"Could not resolve attachment ID: {attachment_id}") |
| 369 | + continue |
| 370 | + |
| 371 | + # Read the file content |
| 372 | + with open(file_path, "r", encoding="utf-8") as f: |
| 373 | + file_content = f.read() |
| 374 | + |
| 375 | + # Get relative path for display |
| 376 | + rel_path = os.path.relpath(file_path, self.get_workspace_dir()) |
| 377 | + |
| 378 | + # Add file content with header |
| 379 | + context_parts.append( |
| 380 | + f"File: {rel_path}\n```\n{file_content}\n```" |
| 381 | + ) |
| 382 | + |
| 383 | + except Exception as e: |
| 384 | + self.log.warning(f"Failed to read attachment {attachment_id}: {e}") |
| 385 | + context_parts.append( |
| 386 | + f"Attachment: {attachment_id} (could not read file: {e})" |
| 387 | + ) |
| 388 | + |
| 389 | + result = "\n\n".join(context_parts) if context_parts else None |
| 390 | + return result |
| 391 | + |
| 392 | + def resolve_attachment_to_path(self, attachment_id: str) -> Optional[str]: |
| 393 | + """ |
| 394 | + Resolve an attachment ID to its file path using multiple strategies. |
| 395 | + """ |
| 396 | + |
| 397 | + try: |
| 398 | + attachment_data = self.ychat.get_attachments().get(attachment_id) |
| 399 | + |
| 400 | + if attachment_data and isinstance(attachment_data, dict): |
| 401 | + # If attachment has a 'value' field with filename |
| 402 | + if 'value' in attachment_data: |
| 403 | + filename = attachment_data['value'] |
| 404 | + |
| 405 | + # Try relative to workspace directory |
| 406 | + workspace_path = os.path.join(self.get_workspace_dir(), filename) |
| 407 | + if os.path.exists(workspace_path): |
| 408 | + return workspace_path |
| 409 | + |
| 410 | + # Try as absolute path |
| 411 | + if os.path.exists(filename): |
| 412 | + return filename |
| 413 | + |
| 414 | + return None |
| 415 | + |
| 416 | + except Exception as e: |
| 417 | + self.log.error(f"Failed to resolve attachment {attachment_id}: {e}") |
| 418 | + return None |
| 419 | + |
350 | 420 | def shutdown(self) -> None:
|
351 | 421 | """
|
352 | 422 | Shuts the persona down. This method should:
|
|
0 commit comments