autobyteus 1.1.8__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +6 -2
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +17 -19
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +6 -3
- autobyteus/agent/handlers/tool_result_event_handler.py +61 -18
- autobyteus/agent/handlers/user_input_message_event_handler.py +19 -10
- autobyteus/agent/hooks/base_phase_hook.py +17 -0
- autobyteus/agent/hooks/hook_registry.py +15 -27
- autobyteus/agent/input_processor/base_user_input_processor.py +17 -1
- autobyteus/agent/input_processor/processor_registry.py +15 -27
- autobyteus/agent/llm_response_processor/base_processor.py +17 -1
- autobyteus/agent/llm_response_processor/processor_registry.py +15 -24
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +14 -0
- autobyteus/agent/message/agent_input_user_message.py +15 -2
- autobyteus/agent/message/send_message_to.py +1 -1
- autobyteus/agent/processor_option.py +17 -0
- autobyteus/agent/sender_type.py +1 -0
- autobyteus/agent/system_prompt_processor/base_processor.py +17 -1
- autobyteus/agent/system_prompt_processor/processor_registry.py +15 -27
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +10 -0
- autobyteus/agent/tool_execution_result_processor/base_processor.py +17 -1
- autobyteus/agent/tool_execution_result_processor/processor_registry.py +15 -1
- autobyteus/agent/workspace/base_workspace.py +1 -1
- autobyteus/agent/workspace/workspace_definition.py +1 -1
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +1 -1
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +2 -2
- autobyteus/agent_team/task_notification/__init__.py +4 -0
- autobyteus/agent_team/task_notification/activation_policy.py +70 -0
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +56 -122
- autobyteus/agent_team/task_notification/task_activator.py +66 -0
- autobyteus/cli/agent_team_tui/state.py +17 -20
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +1 -1
- autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +1 -1
- autobyteus/clients/__init__.py +10 -0
- autobyteus/clients/autobyteus_client.py +318 -0
- autobyteus/clients/cert_utils.py +105 -0
- autobyteus/clients/certificates/cert.pem +34 -0
- autobyteus/events/event_types.py +2 -2
- autobyteus/llm/api/autobyteus_llm.py +1 -1
- autobyteus/llm/api/gemini_llm.py +45 -54
- autobyteus/llm/api/qwen_llm.py +25 -0
- autobyteus/llm/api/zhipu_llm.py +26 -0
- autobyteus/llm/autobyteus_provider.py +9 -3
- autobyteus/llm/llm_factory.py +39 -0
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +1 -0
- autobyteus/llm/token_counter/token_counter_factory.py +3 -0
- autobyteus/llm/token_counter/zhipu_token_counter.py +24 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +5 -2
- autobyteus/multimedia/audio/api/gemini_audio_client.py +84 -153
- autobyteus/multimedia/audio/audio_client_factory.py +47 -22
- autobyteus/multimedia/audio/audio_model.py +13 -6
- autobyteus/multimedia/audio/autobyteus_audio_provider.py +9 -3
- autobyteus/multimedia/audio/base_audio_client.py +3 -1
- autobyteus/multimedia/image/api/autobyteus_image_client.py +13 -6
- autobyteus/multimedia/image/api/gemini_image_client.py +72 -130
- autobyteus/multimedia/image/api/openai_image_client.py +4 -2
- autobyteus/multimedia/image/autobyteus_image_provider.py +9 -3
- autobyteus/multimedia/image/base_image_client.py +6 -2
- autobyteus/multimedia/image/image_client_factory.py +20 -19
- autobyteus/multimedia/image/image_model.py +13 -6
- autobyteus/multimedia/providers.py +1 -0
- autobyteus/task_management/__init__.py +10 -10
- autobyteus/task_management/base_task_board.py +14 -6
- autobyteus/task_management/converters/__init__.py +0 -2
- autobyteus/task_management/converters/task_board_converter.py +7 -16
- autobyteus/task_management/events.py +6 -6
- autobyteus/task_management/in_memory_task_board.py +48 -38
- autobyteus/task_management/schemas/__init__.py +2 -2
- autobyteus/task_management/schemas/{plan_definition.py → task_definition.py} +6 -7
- autobyteus/task_management/schemas/task_status_report.py +1 -2
- autobyteus/task_management/task.py +60 -0
- autobyteus/task_management/tools/__init__.py +6 -2
- autobyteus/task_management/tools/assign_task_to.py +125 -0
- autobyteus/task_management/tools/get_my_tasks.py +80 -0
- autobyteus/task_management/tools/get_task_board_status.py +3 -3
- autobyteus/task_management/tools/publish_task.py +77 -0
- autobyteus/task_management/tools/publish_tasks.py +74 -0
- autobyteus/task_management/tools/update_task_status.py +5 -5
- autobyteus/tools/__init__.py +54 -16
- autobyteus/tools/base_tool.py +4 -4
- autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +1 -1
- autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +1 -1
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +1 -1
- autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +1 -1
- autobyteus/tools/browser/standalone/navigate_to.py +1 -1
- autobyteus/tools/browser/standalone/web_page_pdf_generator.py +1 -1
- autobyteus/tools/browser/standalone/webpage_image_downloader.py +1 -1
- autobyteus/tools/browser/standalone/webpage_reader.py +1 -1
- autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +1 -1
- autobyteus/tools/download_media_tool.py +136 -0
- autobyteus/tools/file/file_editor.py +200 -0
- autobyteus/tools/functional_tool.py +1 -1
- autobyteus/tools/google_search.py +1 -1
- autobyteus/tools/mcp/factory.py +1 -1
- autobyteus/tools/mcp/schema_mapper.py +1 -1
- autobyteus/tools/mcp/tool.py +1 -1
- autobyteus/tools/multimedia/__init__.py +2 -0
- autobyteus/tools/multimedia/audio_tools.py +10 -20
- autobyteus/tools/multimedia/image_tools.py +21 -22
- autobyteus/tools/multimedia/media_reader_tool.py +117 -0
- autobyteus/tools/pydantic_schema_converter.py +1 -1
- autobyteus/tools/registry/tool_definition.py +1 -1
- autobyteus/tools/timer.py +1 -1
- autobyteus/tools/tool_meta.py +1 -1
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +1 -1
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +1 -1
- autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +59 -3
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +1 -1
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +1 -1
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +1 -1
- autobyteus/tools/usage/parsers/_string_decoders.py +18 -0
- autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +9 -1
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +15 -1
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +4 -1
- autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +4 -1
- autobyteus/{tools → utils}/parameter_schema.py +1 -1
- {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/METADATA +4 -3
- {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/RECORD +122 -108
- examples/run_poem_writer.py +1 -1
- autobyteus/task_management/converters/task_plan_converter.py +0 -48
- autobyteus/task_management/task_plan.py +0 -110
- autobyteus/task_management/tools/publish_task_plan.py +0 -101
- autobyteus/tools/image_downloader.py +0 -99
- autobyteus/tools/pdf_downloader.py +0 -89
- {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.1.8.dist-info → autobyteus-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/tools/publish_tasks.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, Dict, Any
|
|
4
|
+
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
8
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
9
|
+
from autobyteus.utils.parameter_schema import ParameterSchema
|
|
10
|
+
from autobyteus.tools.pydantic_schema_converter import pydantic_to_parameter_schema
|
|
11
|
+
from autobyteus.task_management.schemas import TasksDefinitionSchema
|
|
12
|
+
from autobyteus.task_management.task import Task
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from autobyteus.agent.context import AgentContext
|
|
16
|
+
from autobyteus.agent_team.context import AgentTeamContext
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
class PublishTasks(BaseTool):
|
|
21
|
+
"""
|
|
22
|
+
A tool to publish multiple tasks to the task board. This is an additive-only operation.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
CATEGORY = ToolCategory.TASK_MANAGEMENT
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def get_name(cls) -> str:
|
|
29
|
+
return "PublishTasks"
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def get_description(cls) -> str:
|
|
33
|
+
return (
|
|
34
|
+
"Adds a list of new tasks to the team's shared task board. This action is additive and "
|
|
35
|
+
"does not affect existing tasks or the team's overall goal."
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def get_argument_schema(cls) -> Optional[ParameterSchema]:
|
|
40
|
+
return pydantic_to_parameter_schema(TasksDefinitionSchema)
|
|
41
|
+
|
|
42
|
+
async def _execute(self, context: 'AgentContext', **kwargs: Any) -> str:
|
|
43
|
+
agent_name = context.config.name
|
|
44
|
+
logger.info(f"Agent '{agent_name}' is executing PublishTasks.")
|
|
45
|
+
|
|
46
|
+
team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
|
|
47
|
+
if not team_context:
|
|
48
|
+
error_msg = "Error: Team context is not available. Cannot access the task board."
|
|
49
|
+
logger.error(f"Agent '{agent_name}': {error_msg}")
|
|
50
|
+
return error_msg
|
|
51
|
+
|
|
52
|
+
task_board = getattr(team_context.state, 'task_board', None)
|
|
53
|
+
if not task_board:
|
|
54
|
+
error_msg = "Error: Task board has not been initialized for this team."
|
|
55
|
+
logger.error(f"Agent '{agent_name}': {error_msg}")
|
|
56
|
+
return error_msg
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
tasks_def_schema = TasksDefinitionSchema(**kwargs)
|
|
60
|
+
final_tasks = [Task(**task_def.model_dump()) for task_def in tasks_def_schema.tasks]
|
|
61
|
+
except (ValidationError, ValueError) as e:
|
|
62
|
+
error_msg = f"Invalid task definitions provided: {e}"
|
|
63
|
+
logger.warning(f"Agent '{agent_name}' provided an invalid definition for PublishTasks: {error_msg}")
|
|
64
|
+
return f"Error: {error_msg}"
|
|
65
|
+
|
|
66
|
+
if task_board.add_tasks(tasks=final_tasks):
|
|
67
|
+
success_msg = f"Successfully published {len(final_tasks)} new task(s) to the task board."
|
|
68
|
+
logger.info(f"Agent '{agent_name}': {success_msg}")
|
|
69
|
+
return success_msg
|
|
70
|
+
else:
|
|
71
|
+
# This path is less likely now but kept for robustness.
|
|
72
|
+
error_msg = "Failed to publish tasks to the board for an unknown reason."
|
|
73
|
+
logger.error(f"Agent '{agent_name}': {error_msg}")
|
|
74
|
+
return f"Error: {error_msg}"
|
|
@@ -5,7 +5,7 @@ from pydantic import ValidationError
|
|
|
5
5
|
|
|
6
6
|
from autobyteus.tools.base_tool import BaseTool
|
|
7
7
|
from autobyteus.tools.tool_category import ToolCategory
|
|
8
|
-
from autobyteus.
|
|
8
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
9
9
|
from autobyteus.tools.pydantic_schema_converter import pydantic_to_parameter_schema
|
|
10
10
|
from autobyteus.task_management.base_task_board import TaskStatus
|
|
11
11
|
from autobyteus.task_management.deliverable import FileDeliverable
|
|
@@ -77,12 +77,12 @@ class UpdateTaskStatus(BaseTool):
|
|
|
77
77
|
logger.error(f"Agent '{agent_name}': {error_msg}")
|
|
78
78
|
return error_msg
|
|
79
79
|
|
|
80
|
-
if not task_board.
|
|
81
|
-
error_msg = "Error: No
|
|
82
|
-
logger.warning(f"Agent '{agent_name}' tried to update task status, but
|
|
80
|
+
if not task_board.tasks:
|
|
81
|
+
error_msg = "Error: No tasks are currently loaded on the task board."
|
|
82
|
+
logger.warning(f"Agent '{agent_name}' tried to update task status, but the board is empty.")
|
|
83
83
|
return error_msg
|
|
84
84
|
|
|
85
|
-
target_task = next((t for t in task_board.
|
|
85
|
+
target_task = next((t for t in task_board.tasks if t.task_name == task_name), None)
|
|
86
86
|
|
|
87
87
|
if not target_task:
|
|
88
88
|
error_msg = f"Failed to update status for task '{task_name}'. The task name does not exist on the current plan."
|
autobyteus/tools/__init__.py
CHANGED
|
@@ -5,40 +5,77 @@ for creating tools within the AutoByteUs framework.
|
|
|
5
5
|
It also contains implementations of various standard tools.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import logging
|
|
9
|
+
|
|
8
10
|
# Core components for defining tools
|
|
9
11
|
from .base_tool import BaseTool
|
|
10
12
|
from .functional_tool import tool # The @tool decorator
|
|
11
|
-
from .parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
13
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
12
14
|
from .tool_config import ToolConfig # Configuration data object, primarily for class-based tools
|
|
13
15
|
from .tool_origin import ToolOrigin
|
|
14
16
|
from .tool_category import ToolCategory
|
|
15
17
|
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
16
20
|
# --- Re-export specific tools for easier access ---
|
|
17
21
|
|
|
18
22
|
# Functional tools (decorated functions are now instances)
|
|
19
|
-
from .pdf_downloader import pdf_downloader
|
|
20
23
|
from .bash.bash_executor import bash_executor
|
|
21
24
|
from .file.file_reader import file_reader
|
|
22
25
|
from .file.file_writer import file_writer
|
|
26
|
+
from .file.file_editor import file_edit
|
|
23
27
|
|
|
24
28
|
# General Class-based tools
|
|
25
|
-
|
|
26
|
-
from .
|
|
29
|
+
try:
|
|
30
|
+
from .google_search import GoogleSearch
|
|
31
|
+
except ModuleNotFoundError as import_err:
|
|
32
|
+
logger.warning("GoogleSearch tool not available: %s", import_err)
|
|
33
|
+
GoogleSearch = None
|
|
27
34
|
from .timer import Timer
|
|
28
|
-
|
|
35
|
+
try:
|
|
36
|
+
from .multimedia.image_tools import GenerateImageTool, EditImageTool
|
|
37
|
+
except ModuleNotFoundError as import_err:
|
|
38
|
+
logger.warning("Image tools not available: %s", import_err)
|
|
39
|
+
GenerateImageTool = None
|
|
40
|
+
EditImageTool = None
|
|
41
|
+
try:
|
|
42
|
+
from .multimedia.media_reader_tool import ReadMediaFile
|
|
43
|
+
except ModuleNotFoundError as import_err:
|
|
44
|
+
logger.warning("Media reader tool not available: %s", import_err)
|
|
45
|
+
ReadMediaFile = None
|
|
46
|
+
try:
|
|
47
|
+
from .download_media_tool import DownloadMediaTool
|
|
48
|
+
except ModuleNotFoundError as import_err:
|
|
49
|
+
logger.warning("Download media tool not available: %s", import_err)
|
|
50
|
+
DownloadMediaTool = None
|
|
29
51
|
|
|
30
52
|
# Standalone Browser tools
|
|
31
|
-
|
|
32
|
-
from .browser.standalone.
|
|
33
|
-
from .browser.standalone.
|
|
34
|
-
from .browser.standalone.
|
|
35
|
-
from .browser.standalone.
|
|
53
|
+
try:
|
|
54
|
+
from .browser.standalone.navigate_to import NavigateTo as StandaloneNavigateTo # Alias to avoid name clash
|
|
55
|
+
from .browser.standalone.webpage_reader import WebPageReader as StandaloneWebPageReader # Alias
|
|
56
|
+
from .browser.standalone.webpage_screenshot_taker import WebPageScreenshotTaker as StandaloneWebPageScreenshotTaker # Alias
|
|
57
|
+
from .browser.standalone.webpage_image_downloader import WebPageImageDownloader
|
|
58
|
+
from .browser.standalone.web_page_pdf_generator import WebPagePDFGenerator
|
|
59
|
+
except ModuleNotFoundError as import_err:
|
|
60
|
+
logger.warning('Standalone browser tools not available: %s', import_err)
|
|
61
|
+
StandaloneNavigateTo = None
|
|
62
|
+
StandaloneWebPageReader = None
|
|
63
|
+
StandaloneWebPageScreenshotTaker = None
|
|
64
|
+
WebPageImageDownloader = None
|
|
65
|
+
WebPagePDFGenerator = None
|
|
36
66
|
|
|
37
67
|
# Session-Aware Browser tools
|
|
38
|
-
|
|
39
|
-
from .browser.session_aware.
|
|
40
|
-
from .browser.session_aware.
|
|
41
|
-
from .browser.session_aware.
|
|
68
|
+
try:
|
|
69
|
+
from .browser.session_aware.browser_session_aware_navigate_to import BrowserSessionAwareNavigateTo
|
|
70
|
+
from .browser.session_aware.browser_session_aware_web_element_trigger import BrowserSessionAwareWebElementTrigger
|
|
71
|
+
from .browser.session_aware.browser_session_aware_webpage_reader import BrowserSessionAwareWebPageReader
|
|
72
|
+
from .browser.session_aware.browser_session_aware_webpage_screenshot_taker import BrowserSessionAwareWebPageScreenshotTaker
|
|
73
|
+
except ModuleNotFoundError as import_err:
|
|
74
|
+
logger.warning('Session-aware browser tools not available: %s', import_err)
|
|
75
|
+
BrowserSessionAwareNavigateTo = None
|
|
76
|
+
BrowserSessionAwareWebElementTrigger = None
|
|
77
|
+
BrowserSessionAwareWebPageReader = None
|
|
78
|
+
BrowserSessionAwareWebPageScreenshotTaker = None
|
|
42
79
|
|
|
43
80
|
|
|
44
81
|
__all__ = [
|
|
@@ -53,17 +90,18 @@ __all__ = [
|
|
|
53
90
|
"ToolCategory",
|
|
54
91
|
|
|
55
92
|
# Re-exported functional tool instances
|
|
56
|
-
"pdf_downloader",
|
|
57
93
|
"bash_executor",
|
|
58
94
|
"file_reader",
|
|
59
95
|
"file_writer",
|
|
96
|
+
"file_edit",
|
|
60
97
|
|
|
61
98
|
# Re-exported general class-based tools
|
|
62
99
|
"GoogleSearch",
|
|
63
|
-
"ImageDownloader",
|
|
64
100
|
"Timer",
|
|
65
101
|
"GenerateImageTool",
|
|
66
102
|
"EditImageTool",
|
|
103
|
+
"ReadMediaFile",
|
|
104
|
+
"DownloadMediaTool",
|
|
67
105
|
|
|
68
106
|
# Re-exported Standalone Browser tools
|
|
69
107
|
"StandaloneNavigateTo",
|
autobyteus/tools/base_tool.py
CHANGED
|
@@ -5,14 +5,14 @@ from abc import ABC, abstractmethod
|
|
|
5
5
|
from typing import Optional, Any, TYPE_CHECKING, List as TypingList, Dict, Union
|
|
6
6
|
|
|
7
7
|
from autobyteus.events.event_emitter import EventEmitter
|
|
8
|
-
from autobyteus.
|
|
8
|
+
from autobyteus.utils.parameter_schema import ParameterType
|
|
9
9
|
|
|
10
10
|
from .tool_meta import ToolMeta
|
|
11
11
|
from .tool_state import ToolState
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from autobyteus.agent.context import AgentContext
|
|
15
|
-
from autobyteus.
|
|
15
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition
|
|
16
16
|
from autobyteus.tools.tool_config import ToolConfig
|
|
17
17
|
from .tool_state import ToolState
|
|
18
18
|
from autobyteus.tools.registry import ToolDefinition
|
|
@@ -90,8 +90,8 @@ class BaseTool(ABC, EventEmitter, metaclass=ToolMeta):
|
|
|
90
90
|
if item_schema_dict and isinstance(item_schema_dict, dict) and item_schema_dict.get("type") == "object":
|
|
91
91
|
# Create a temporary ParameterSchema for the item type to enable recursion.
|
|
92
92
|
# This is a simplified conversion for coercion purposes only.
|
|
93
|
-
from .parameter_schema import ParameterSchema as TempSchema
|
|
94
|
-
from .parameter_schema import ParameterDefinition as TempDef
|
|
93
|
+
from autobyteus.utils.parameter_schema import ParameterSchema as TempSchema
|
|
94
|
+
from autobyteus.utils.parameter_schema import ParameterDefinition as TempDef
|
|
95
95
|
|
|
96
96
|
item_param_schema = TempSchema()
|
|
97
97
|
props = item_schema_dict.get("properties", {})
|
|
@@ -6,7 +6,7 @@ from urllib.parse import urlparse
|
|
|
6
6
|
from typing import Optional, TYPE_CHECKING, Any
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
-
from autobyteus.
|
|
9
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from autobyteus.agent.context import AgentContext
|
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
from autobyteus.tools.browser.session_aware.browser_session_aware_tool import BrowserSessionAwareTool
|
|
8
8
|
from autobyteus.tools.browser.session_aware.shared_browser_session import SharedBrowserSession
|
|
9
9
|
from autobyteus.tools.browser.session_aware.web_element_action import WebElementAction
|
|
10
|
-
from autobyteus.
|
|
10
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
11
11
|
from autobyteus.tools.tool_config import ToolConfig
|
|
12
12
|
from autobyteus.tools.tool_category import ToolCategory
|
|
13
13
|
|
|
@@ -5,7 +5,7 @@ from typing import Optional, TYPE_CHECKING, Any
|
|
|
5
5
|
from autobyteus.tools.browser.session_aware.browser_session_aware_tool import BrowserSessionAwareTool
|
|
6
6
|
from autobyteus.tools.browser.session_aware.shared_browser_session import SharedBrowserSession
|
|
7
7
|
from autobyteus.tools.tool_config import ToolConfig
|
|
8
|
-
from autobyteus.
|
|
8
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
9
9
|
from autobyteus.tools.tool_category import ToolCategory
|
|
10
10
|
from autobyteus.utils.html_cleaner import clean, CleaningMode
|
|
11
11
|
|
|
@@ -6,7 +6,7 @@ from typing import Optional, TYPE_CHECKING, Any
|
|
|
6
6
|
from autobyteus.tools.browser.session_aware.browser_session_aware_tool import BrowserSessionAwareTool
|
|
7
7
|
from autobyteus.tools.browser.session_aware.shared_browser_session import SharedBrowserSession
|
|
8
8
|
from autobyteus.tools.tool_config import ToolConfig
|
|
9
|
-
from autobyteus.
|
|
9
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
10
10
|
from autobyteus.tools.tool_category import ToolCategory
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
@@ -6,7 +6,7 @@ from urllib.parse import urlparse
|
|
|
6
6
|
from typing import Optional, TYPE_CHECKING, Any
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
-
from autobyteus.
|
|
9
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from autobyteus.agent.context import AgentContext
|
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
from typing import Optional, TYPE_CHECKING, Any
|
|
8
8
|
from urllib.parse import urlparse
|
|
9
9
|
|
|
10
|
-
from autobyteus.
|
|
10
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from autobyteus.agent.context import AgentContext
|
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
from urllib.parse import urljoin, urlparse
|
|
8
8
|
from typing import Optional, TYPE_CHECKING, Any, List
|
|
9
9
|
|
|
10
|
-
from autobyteus.
|
|
10
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from autobyteus.agent.context import AgentContext
|
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
from typing import Optional, TYPE_CHECKING, Any
|
|
8
8
|
from autobyteus.tools.base_tool import BaseTool
|
|
9
9
|
from autobyteus.tools.tool_config import ToolConfig
|
|
10
|
-
from autobyteus.
|
|
10
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
11
11
|
from autobyteus.tools.tool_category import ToolCategory
|
|
12
12
|
from brui_core.ui_integrator import UIIntegrator
|
|
13
13
|
from autobyteus.utils.html_cleaner import clean, CleaningMode
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Optional, TYPE_CHECKING, Any
|
|
2
2
|
from autobyteus.tools.base_tool import BaseTool
|
|
3
3
|
from autobyteus.tools.tool_config import ToolConfig
|
|
4
|
-
from autobyteus.
|
|
4
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
5
5
|
from autobyteus.tools.tool_category import ToolCategory
|
|
6
6
|
from brui_core.ui_integrator import UIIntegrator
|
|
7
7
|
import logging
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
import mimetypes
|
|
4
|
+
import aiohttp
|
|
5
|
+
from typing import Optional, TYPE_CHECKING
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
|
|
8
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
9
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
10
|
+
from autobyteus.utils.file_utils import get_default_download_folder
|
|
11
|
+
from autobyteus.utils.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from autobyteus.agent.context import AgentContext
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
class DownloadMediaTool(BaseTool):
|
|
19
|
+
"""
|
|
20
|
+
A unified tool to download any media file (e.g., image, PDF, audio) from a URL.
|
|
21
|
+
"""
|
|
22
|
+
CATEGORY = ToolCategory.WEB
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def get_name(cls) -> str:
|
|
26
|
+
return "DownloadMedia"
|
|
27
|
+
|
|
28
|
+
@classmethod
|
|
29
|
+
def get_description(cls) -> str:
|
|
30
|
+
return (
|
|
31
|
+
"Downloads various media files (e.g., images like PNG/JPG, documents like PDF, audio like MP3/WAV) "
|
|
32
|
+
"from a direct URL and saves them locally. It intelligently determines the correct file extension "
|
|
33
|
+
"based on the content type. Returns the absolute path to the downloaded file."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def get_argument_schema(cls) -> ParameterSchema:
|
|
38
|
+
schema = ParameterSchema()
|
|
39
|
+
schema.add_parameter(ParameterDefinition(
|
|
40
|
+
name="url",
|
|
41
|
+
param_type=ParameterType.STRING,
|
|
42
|
+
description="The direct URL of the media file to download.",
|
|
43
|
+
required=True
|
|
44
|
+
))
|
|
45
|
+
schema.add_parameter(ParameterDefinition(
|
|
46
|
+
name="filename",
|
|
47
|
+
param_type=ParameterType.STRING,
|
|
48
|
+
description="The desired base name for the downloaded file (e.g., 'vacation_photo', 'annual_report'). The tool will automatically add the correct file extension.",
|
|
49
|
+
required=True
|
|
50
|
+
))
|
|
51
|
+
schema.add_parameter(ParameterDefinition(
|
|
52
|
+
name="folder",
|
|
53
|
+
param_type=ParameterType.STRING,
|
|
54
|
+
description="Optional. A custom directory path to save the file. If not provided, the system's default download folder will be used.",
|
|
55
|
+
required=False
|
|
56
|
+
))
|
|
57
|
+
return schema
|
|
58
|
+
|
|
59
|
+
async def _execute(self, context: 'AgentContext', url: str, filename: str, folder: Optional[str] = None) -> str:
|
|
60
|
+
# 1. Determine download directory
|
|
61
|
+
try:
|
|
62
|
+
if folder:
|
|
63
|
+
# Security: prevent path traversal attacks.
|
|
64
|
+
if ".." in folder:
|
|
65
|
+
raise ValueError("Security error: 'folder' path cannot contain '..'.")
|
|
66
|
+
destination_dir = os.path.abspath(folder)
|
|
67
|
+
else:
|
|
68
|
+
destination_dir = get_default_download_folder()
|
|
69
|
+
|
|
70
|
+
os.makedirs(destination_dir, exist_ok=True)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.error(f"Error preparing download directory '{folder or 'default'}': {e}", exc_info=True)
|
|
73
|
+
raise IOError(f"Failed to create or access download directory: {e}")
|
|
74
|
+
|
|
75
|
+
# 2. Sanitize filename provided by the LLM
|
|
76
|
+
if not filename or ".." in filename or os.path.isabs(filename) or "/" in filename or "\\" in filename:
|
|
77
|
+
raise ValueError("Invalid filename. It must be a simple name without any path characters ('..', '/', '\\').")
|
|
78
|
+
|
|
79
|
+
logger.info(f"Attempting to download from {url} to save as '{filename}' in '{destination_dir}'.")
|
|
80
|
+
|
|
81
|
+
# 3. Download and process file asynchronously
|
|
82
|
+
try:
|
|
83
|
+
async with aiohttp.ClientSession() as session:
|
|
84
|
+
async with session.get(url, timeout=60) as response:
|
|
85
|
+
response.raise_for_status()
|
|
86
|
+
|
|
87
|
+
# 4. Intelligently determine file extension from Content-Type header
|
|
88
|
+
content_type = response.headers.get('Content-Type')
|
|
89
|
+
correct_ext = ''
|
|
90
|
+
if content_type:
|
|
91
|
+
mime_type = content_type.split(';')[0].strip()
|
|
92
|
+
guess = mimetypes.guess_extension(mime_type)
|
|
93
|
+
if guess:
|
|
94
|
+
correct_ext = guess
|
|
95
|
+
logger.debug(f"Determined extension '{correct_ext}' from Content-Type: '{mime_type}'")
|
|
96
|
+
|
|
97
|
+
# Fallback to URL extension if Content-Type is generic or missing
|
|
98
|
+
if not correct_ext or correct_ext == '.bin':
|
|
99
|
+
url_path = urlparse(url).path
|
|
100
|
+
_, ext_from_url = os.path.splitext(os.path.basename(url_path))
|
|
101
|
+
if ext_from_url and len(ext_from_url) > 1: # Ensure it's not just a dot
|
|
102
|
+
logger.debug(f"Using fallback extension '{ext_from_url}' from URL.")
|
|
103
|
+
correct_ext = ext_from_url
|
|
104
|
+
|
|
105
|
+
if not correct_ext:
|
|
106
|
+
logger.warning("Could not determine a file extension. The file will be saved without one.")
|
|
107
|
+
|
|
108
|
+
# 5. Construct final filename and path
|
|
109
|
+
base_filename, _ = os.path.splitext(filename)
|
|
110
|
+
final_filename = f"{base_filename}{correct_ext}"
|
|
111
|
+
save_path = os.path.join(destination_dir, final_filename)
|
|
112
|
+
|
|
113
|
+
# Ensure filename is unique to avoid overwriting
|
|
114
|
+
counter = 1
|
|
115
|
+
while os.path.exists(save_path):
|
|
116
|
+
final_filename = f"{base_filename}_{counter}{correct_ext}"
|
|
117
|
+
save_path = os.path.join(destination_dir, final_filename)
|
|
118
|
+
counter += 1
|
|
119
|
+
|
|
120
|
+
# 6. Stream file content to disk
|
|
121
|
+
with open(save_path, 'wb') as f:
|
|
122
|
+
async for chunk in response.content.iter_chunked(8192):
|
|
123
|
+
f.write(chunk)
|
|
124
|
+
|
|
125
|
+
logger.info(f"Successfully downloaded and saved file to: {save_path}")
|
|
126
|
+
return f"Successfully downloaded file to: {save_path}"
|
|
127
|
+
|
|
128
|
+
except aiohttp.ClientError as e:
|
|
129
|
+
logger.error(f"Network error while downloading from {url}: {e}", exc_info=True)
|
|
130
|
+
raise ConnectionError(f"Failed to download from {url}: {e}")
|
|
131
|
+
except IOError as e:
|
|
132
|
+
logger.error(f"Failed to write downloaded file to {destination_dir}: {e}", exc_info=True)
|
|
133
|
+
raise
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.error(f"An unexpected error occurred during download from {url}: {e}", exc_info=True)
|
|
136
|
+
raise RuntimeError(f"An unexpected error occurred: {e}")
|