autobyteus 1.1.5__py3-none-any.whl → 1.1.7__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/context/agent_config.py +6 -1
- autobyteus/agent/context/agent_runtime_state.py +7 -1
- autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +30 -7
- autobyteus/agent/handlers/tool_result_event_handler.py +100 -88
- autobyteus/agent/handlers/user_input_message_event_handler.py +22 -25
- autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +7 -1
- autobyteus/agent/message/__init__.py +7 -5
- autobyteus/agent/message/agent_input_user_message.py +6 -16
- autobyteus/agent/message/context_file.py +24 -24
- autobyteus/agent/message/context_file_type.py +29 -8
- autobyteus/agent/message/multimodal_message_builder.py +47 -0
- autobyteus/agent/streaming/stream_event_payloads.py +23 -4
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +6 -2
- autobyteus/agent/tool_invocation.py +27 -2
- autobyteus/agent_team/agent_team_builder.py +22 -1
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +9 -2
- autobyteus/agent_team/context/agent_team_config.py +1 -0
- autobyteus/agent_team/context/agent_team_runtime_state.py +0 -2
- autobyteus/llm/api/autobyteus_llm.py +33 -33
- autobyteus/llm/api/bedrock_llm.py +13 -5
- autobyteus/llm/api/claude_llm.py +13 -27
- autobyteus/llm/api/gemini_llm.py +108 -42
- autobyteus/llm/api/groq_llm.py +4 -3
- autobyteus/llm/api/mistral_llm.py +97 -51
- autobyteus/llm/api/nvidia_llm.py +6 -5
- autobyteus/llm/api/ollama_llm.py +37 -12
- autobyteus/llm/api/openai_compatible_llm.py +91 -91
- autobyteus/llm/autobyteus_provider.py +1 -1
- autobyteus/llm/base_llm.py +42 -139
- autobyteus/llm/extensions/base_extension.py +6 -6
- autobyteus/llm/extensions/token_usage_tracking_extension.py +3 -2
- autobyteus/llm/llm_factory.py +131 -61
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +1 -0
- autobyteus/llm/token_counter/token_counter_factory.py +3 -1
- autobyteus/llm/user_message.py +43 -35
- autobyteus/llm/utils/llm_config.py +34 -18
- autobyteus/llm/utils/media_payload_formatter.py +99 -0
- autobyteus/llm/utils/messages.py +32 -25
- autobyteus/llm/utils/response_types.py +9 -3
- autobyteus/llm/utils/token_usage.py +6 -5
- autobyteus/multimedia/__init__.py +31 -0
- autobyteus/multimedia/audio/__init__.py +11 -0
- autobyteus/multimedia/audio/api/__init__.py +4 -0
- autobyteus/multimedia/audio/api/autobyteus_audio_client.py +59 -0
- autobyteus/multimedia/audio/api/gemini_audio_client.py +219 -0
- autobyteus/multimedia/audio/audio_client_factory.py +120 -0
- autobyteus/multimedia/audio/audio_model.py +97 -0
- autobyteus/multimedia/audio/autobyteus_audio_provider.py +108 -0
- autobyteus/multimedia/audio/base_audio_client.py +40 -0
- autobyteus/multimedia/image/__init__.py +11 -0
- autobyteus/multimedia/image/api/__init__.py +9 -0
- autobyteus/multimedia/image/api/autobyteus_image_client.py +97 -0
- autobyteus/multimedia/image/api/gemini_image_client.py +188 -0
- autobyteus/multimedia/image/api/openai_image_client.py +142 -0
- autobyteus/multimedia/image/autobyteus_image_provider.py +109 -0
- autobyteus/multimedia/image/base_image_client.py +67 -0
- autobyteus/multimedia/image/image_client_factory.py +118 -0
- autobyteus/multimedia/image/image_model.py +97 -0
- autobyteus/multimedia/providers.py +5 -0
- autobyteus/multimedia/runtimes.py +8 -0
- autobyteus/multimedia/utils/__init__.py +10 -0
- autobyteus/multimedia/utils/api_utils.py +19 -0
- autobyteus/multimedia/utils/multimedia_config.py +29 -0
- autobyteus/multimedia/utils/response_types.py +13 -0
- autobyteus/task_management/tools/publish_task_plan.py +4 -16
- autobyteus/task_management/tools/update_task_status.py +4 -19
- autobyteus/tools/__init__.py +5 -4
- autobyteus/tools/base_tool.py +98 -29
- autobyteus/tools/browser/standalone/__init__.py +0 -1
- autobyteus/tools/google_search.py +149 -0
- autobyteus/tools/mcp/schema_mapper.py +29 -71
- autobyteus/tools/multimedia/__init__.py +8 -0
- autobyteus/tools/multimedia/audio_tools.py +116 -0
- autobyteus/tools/multimedia/image_tools.py +186 -0
- autobyteus/tools/parameter_schema.py +82 -89
- autobyteus/tools/pydantic_schema_converter.py +81 -0
- autobyteus/tools/tool_category.py +1 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +89 -20
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +115 -41
- autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +50 -20
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +55 -22
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +54 -21
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +53 -23
- autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +270 -94
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +5 -2
- autobyteus/tools/usage/providers/tool_manifest_provider.py +43 -16
- autobyteus/tools/usage/registries/tool_formatting_registry.py +9 -2
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +9 -2
- autobyteus-1.1.7.dist-info/METADATA +204 -0
- {autobyteus-1.1.5.dist-info → autobyteus-1.1.7.dist-info}/RECORD +98 -71
- examples/run_browser_agent.py +1 -1
- examples/run_google_slides_agent.py +2 -2
- examples/run_mcp_google_slides_client.py +1 -1
- examples/run_sqlite_agent.py +1 -1
- autobyteus/llm/utils/image_payload_formatter.py +0 -89
- autobyteus/tools/ask_user_input.py +0 -40
- autobyteus/tools/browser/standalone/factory/google_search_factory.py +0 -25
- autobyteus/tools/browser/standalone/google_search_ui.py +0 -126
- autobyteus-1.1.5.dist-info/METADATA +0 -161
- {autobyteus-1.1.5.dist-info → autobyteus-1.1.7.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.5.dist-info → autobyteus-1.1.7.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.1.5.dist-info → autobyteus-1.1.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Dict, Optional
|
|
3
|
+
from autobyteus.multimedia.image.autobyteus_image_provider import AutobyteusImageModelProvider
|
|
4
|
+
from autobyteus.multimedia.image.base_image_client import BaseImageClient
|
|
5
|
+
from autobyteus.multimedia.image.image_model import ImageModel
|
|
6
|
+
from autobyteus.multimedia.providers import MultimediaProvider
|
|
7
|
+
from autobyteus.multimedia.image.api.openai_image_client import OpenAIImageClient
|
|
8
|
+
from autobyteus.multimedia.image.api.gemini_image_client import GeminiImageClient
|
|
9
|
+
from autobyteus.multimedia.utils.multimedia_config import MultimediaConfig
|
|
10
|
+
from autobyteus.utils.singleton import SingletonMeta
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
class ImageClientFactory(metaclass=SingletonMeta):
|
|
15
|
+
"""
|
|
16
|
+
A factory for creating instances of image generation clients based on registered ImageModels.
|
|
17
|
+
"""
|
|
18
|
+
_models_by_identifier: Dict[str, ImageModel] = {}
|
|
19
|
+
_initialized = False
|
|
20
|
+
|
|
21
|
+
@staticmethod
|
|
22
|
+
def ensure_initialized():
|
|
23
|
+
"""Ensures the factory is initialized before use."""
|
|
24
|
+
if not ImageClientFactory._initialized:
|
|
25
|
+
ImageClientFactory._initialize_registry()
|
|
26
|
+
ImageClientFactory._initialized = True
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def reinitialize():
|
|
30
|
+
"""Reinitializes the model registry, clearing all models and re-discovering them."""
|
|
31
|
+
logger.info("Reinitializing Image model registry...")
|
|
32
|
+
ImageClientFactory._initialized = False
|
|
33
|
+
ImageClientFactory._models_by_identifier.clear()
|
|
34
|
+
ImageClientFactory.ensure_initialized()
|
|
35
|
+
logger.info("Image model registry reinitialized successfully.")
|
|
36
|
+
|
|
37
|
+
@staticmethod
|
|
38
|
+
def _initialize_registry():
|
|
39
|
+
"""Initializes the registry with built-in image models and discovers remote ones."""
|
|
40
|
+
|
|
41
|
+
# OpenAI Models
|
|
42
|
+
gpt_image_1_model = ImageModel(
|
|
43
|
+
name="gpt-image-1",
|
|
44
|
+
value="dall-e-3",
|
|
45
|
+
provider=MultimediaProvider.OPENAI,
|
|
46
|
+
client_class=OpenAIImageClient,
|
|
47
|
+
parameter_schema={
|
|
48
|
+
"n": {"type": "integer", "default": 1, "allowed_values": [1], "description": "The number of images to generate."},
|
|
49
|
+
"size": {"type": "string", "default": "1024x1024", "allowed_values": ["1024x1024", "1792x1024", "1024x1792"], "description": "The size of the generated images."},
|
|
50
|
+
"quality": {"type": "string", "default": "hd", "allowed_values": ["standard", "hd"], "description": "The quality of the image that will be generated."},
|
|
51
|
+
"style": {"type": "string", "default": "vivid", "allowed_values": ["vivid", "natural"], "description": "The style of the generated images."}
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
dall_e_2_model = ImageModel(
|
|
56
|
+
name="dall-e-2",
|
|
57
|
+
value="dall-e-2",
|
|
58
|
+
provider=MultimediaProvider.OPENAI,
|
|
59
|
+
client_class=OpenAIImageClient,
|
|
60
|
+
parameter_schema={
|
|
61
|
+
"n": {"type": "integer", "default": 1, "description": "The number of images to generate."},
|
|
62
|
+
"size": {"type": "string", "default": "1024x1024", "allowed_values": ["256x256", "512x512", "1024x1024"], "description": "The size of the generated images."}
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Google Imagen Models (via Gemini API)
|
|
67
|
+
imagen_model = ImageModel(
|
|
68
|
+
name="imagen-4",
|
|
69
|
+
value="imagen-4.0-generate-001",
|
|
70
|
+
provider=MultimediaProvider.GOOGLE,
|
|
71
|
+
client_class=GeminiImageClient,
|
|
72
|
+
parameter_schema={} # The genai library doesn't expose these as simple params
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
models_to_register = [
|
|
76
|
+
gpt_image_1_model,
|
|
77
|
+
dall_e_2_model,
|
|
78
|
+
imagen_model,
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
for model in models_to_register:
|
|
82
|
+
ImageClientFactory.register_model(model)
|
|
83
|
+
|
|
84
|
+
logger.info("Default API-based image models registered.")
|
|
85
|
+
|
|
86
|
+
# Discover models from remote Autobyteus servers
|
|
87
|
+
AutobyteusImageModelProvider.discover_and_register()
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def register_model(model: ImageModel):
|
|
91
|
+
"""Registers a new image model."""
|
|
92
|
+
identifier = model.model_identifier
|
|
93
|
+
if identifier in ImageClientFactory._models_by_identifier:
|
|
94
|
+
logger.warning(f"Image model '{identifier}' is already registered. Overwriting.")
|
|
95
|
+
|
|
96
|
+
if not isinstance(model.provider, MultimediaProvider):
|
|
97
|
+
try:
|
|
98
|
+
model.provider = MultimediaProvider(model.provider)
|
|
99
|
+
except ValueError:
|
|
100
|
+
logger.error(f"Cannot register model '{identifier}' with unknown provider '{model.provider}'.")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
ImageClientFactory._models_by_identifier[identifier] = model
|
|
104
|
+
|
|
105
|
+
@staticmethod
|
|
106
|
+
def create_image_client(model_identifier: str, config_override: Optional[MultimediaConfig] = None) -> BaseImageClient:
|
|
107
|
+
"""Creates an instance of a registered image client for a specific model."""
|
|
108
|
+
ImageClientFactory.ensure_initialized()
|
|
109
|
+
|
|
110
|
+
model = ImageClientFactory._models_by_identifier.get(model_identifier)
|
|
111
|
+
if not model:
|
|
112
|
+
raise ValueError(f"No image model registered with the name '{model_identifier}'. "
|
|
113
|
+
f"Available models: {list(ImageClientFactory._models_by_identifier.keys())}")
|
|
114
|
+
|
|
115
|
+
logger.info(f"Creating instance of image client for model '{model_identifier}'.")
|
|
116
|
+
return model.create_client(config_override)
|
|
117
|
+
|
|
118
|
+
image_client_factory = ImageClientFactory()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import logging
|
|
3
|
+
from typing import TYPE_CHECKING, Type, Optional, Iterator, Dict, Any
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
from autobyteus.multimedia.providers import MultimediaProvider
|
|
7
|
+
from autobyteus.multimedia.runtimes import MultimediaRuntime
|
|
8
|
+
from autobyteus.multimedia.utils.multimedia_config import MultimediaConfig
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.multimedia.image.base_image_client import BaseImageClient
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class ImageModelMeta(type):
|
|
16
|
+
"""
|
|
17
|
+
Metaclass for ImageModel to allow discovery and access like an Enum.
|
|
18
|
+
"""
|
|
19
|
+
def __iter__(cls) -> Iterator[ImageModel]:
|
|
20
|
+
from autobyteus.multimedia.image.image_client_factory import ImageClientFactory
|
|
21
|
+
ImageClientFactory.ensure_initialized()
|
|
22
|
+
for model in ImageClientFactory._models_by_identifier.values():
|
|
23
|
+
yield model
|
|
24
|
+
|
|
25
|
+
def __getitem__(cls, name_or_identifier: str) -> ImageModel:
|
|
26
|
+
from autobyteus.multimedia.image.image_client_factory import ImageClientFactory
|
|
27
|
+
ImageClientFactory.ensure_initialized()
|
|
28
|
+
model = ImageClientFactory._models_by_identifier.get(name_or_identifier)
|
|
29
|
+
if model:
|
|
30
|
+
return model
|
|
31
|
+
available_models = list(ImageClientFactory._models_by_identifier.keys())
|
|
32
|
+
raise KeyError(f"Image model '{name_or_identifier}' not found. Available models: {available_models}")
|
|
33
|
+
|
|
34
|
+
def __len__(cls) -> int:
|
|
35
|
+
from autobyteus.multimedia.image.image_client_factory import ImageClientFactory
|
|
36
|
+
ImageClientFactory.ensure_initialized()
|
|
37
|
+
return len(ImageClientFactory._models_by_identifier)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ImageModel(metaclass=ImageModelMeta):
|
|
41
|
+
"""
|
|
42
|
+
Represents a single image model's metadata.
|
|
43
|
+
"""
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
name: str,
|
|
47
|
+
value: str,
|
|
48
|
+
provider: MultimediaProvider,
|
|
49
|
+
client_class: Type["BaseImageClient"],
|
|
50
|
+
parameter_schema: Optional[Dict[str, Any]] = None,
|
|
51
|
+
runtime: MultimediaRuntime = MultimediaRuntime.API,
|
|
52
|
+
host_url: Optional[str] = None
|
|
53
|
+
):
|
|
54
|
+
self.name = name
|
|
55
|
+
self.value = value
|
|
56
|
+
self.provider = provider
|
|
57
|
+
self.client_class = client_class
|
|
58
|
+
self.runtime = runtime
|
|
59
|
+
self.host_url = host_url
|
|
60
|
+
self.parameter_schema = parameter_schema if parameter_schema else {}
|
|
61
|
+
|
|
62
|
+
# Automatically build default_config from the schema's default values
|
|
63
|
+
default_params = {
|
|
64
|
+
key: meta.get("default")
|
|
65
|
+
for key, meta in self.parameter_schema.items()
|
|
66
|
+
if "default" in meta
|
|
67
|
+
}
|
|
68
|
+
self.default_config = MultimediaConfig(params=default_params)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def model_identifier(self) -> str:
|
|
72
|
+
"""Returns the unique identifier for the model."""
|
|
73
|
+
if self.runtime == MultimediaRuntime.AUTOBYTEUS and self.host_url:
|
|
74
|
+
try:
|
|
75
|
+
host = urlparse(self.host_url).hostname
|
|
76
|
+
return f"{self.name}@{host}"
|
|
77
|
+
except Exception:
|
|
78
|
+
return f"{self.name}@{self.host_url}" # Fallback
|
|
79
|
+
return self.name
|
|
80
|
+
|
|
81
|
+
def create_client(self, config_override: Optional[MultimediaConfig] = None) -> "BaseImageClient":
|
|
82
|
+
"""
|
|
83
|
+
Instantiates the client class for this model.
|
|
84
|
+
"""
|
|
85
|
+
config_to_use = self.default_config
|
|
86
|
+
if config_override:
|
|
87
|
+
from copy import deepcopy
|
|
88
|
+
config_to_use = deepcopy(self.default_config)
|
|
89
|
+
config_to_use.merge_with(config_override)
|
|
90
|
+
|
|
91
|
+
return self.client_class(model=self, config=config_to_use)
|
|
92
|
+
|
|
93
|
+
def __repr__(self):
|
|
94
|
+
return (
|
|
95
|
+
f"ImageModel(identifier='{self.model_identifier}', "
|
|
96
|
+
f"provider='{self.provider.name}', runtime='{self.runtime.value}')"
|
|
97
|
+
)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .multimedia_config import MultimediaConfig
|
|
2
|
+
from .response_types import ImageGenerationResponse, SpeechGenerationResponse
|
|
3
|
+
from .api_utils import load_image_from_url
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"MultimediaConfig",
|
|
7
|
+
"ImageGenerationResponse",
|
|
8
|
+
"SpeechGenerationResponse",
|
|
9
|
+
"load_image_from_url",
|
|
10
|
+
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from PIL import Image
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
def load_image_from_url(url: str) -> Image.Image:
|
|
8
|
+
"""Loads an image from a URL (http, https, or file path)."""
|
|
9
|
+
try:
|
|
10
|
+
if url.startswith(('http://', 'https://')):
|
|
11
|
+
response = requests.get(url, stream=True)
|
|
12
|
+
response.raise_for_status()
|
|
13
|
+
return Image.open(response.raw)
|
|
14
|
+
else:
|
|
15
|
+
# Assume it's a local file path
|
|
16
|
+
return Image.open(url)
|
|
17
|
+
except Exception as e:
|
|
18
|
+
logger.error(f"Failed to load image from URL/path '{url}': {e}")
|
|
19
|
+
raise
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Optional, Dict, Any
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class MultimediaConfig:
|
|
6
|
+
"""
|
|
7
|
+
Configuration for multimedia generation, using a flexible dictionary for parameters.
|
|
8
|
+
"""
|
|
9
|
+
params: Dict[str, Any] = field(default_factory=dict)
|
|
10
|
+
|
|
11
|
+
def merge_with(self, override_config: Optional['MultimediaConfig']):
|
|
12
|
+
"""
|
|
13
|
+
Merges parameters from an override config into this one.
|
|
14
|
+
"""
|
|
15
|
+
if override_config and override_config.params:
|
|
16
|
+
self.params.update(override_config.params)
|
|
17
|
+
|
|
18
|
+
@classmethod
|
|
19
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'MultimediaConfig':
|
|
20
|
+
"""
|
|
21
|
+
Creates a MultimediaConfig instance from a dictionary of parameters.
|
|
22
|
+
"""
|
|
23
|
+
return cls(params=data if data is not None else {})
|
|
24
|
+
|
|
25
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
26
|
+
"""
|
|
27
|
+
Returns the configuration parameters as a dictionary.
|
|
28
|
+
"""
|
|
29
|
+
return self.params
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class ImageGenerationResponse:
|
|
6
|
+
"""Response for image generation or editing."""
|
|
7
|
+
image_urls: List[str]
|
|
8
|
+
revised_prompt: Optional[str] = None
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class SpeechGenerationResponse:
|
|
12
|
+
"""Response for speech generation (Text-to-Speech)."""
|
|
13
|
+
audio_urls: List[str]
|
|
@@ -4,12 +4,11 @@ import logging
|
|
|
4
4
|
from typing import TYPE_CHECKING, Optional, Dict, Any
|
|
5
5
|
|
|
6
6
|
from pydantic import ValidationError
|
|
7
|
-
# No longer need GenerateJsonSchema from pydantic.json_schema
|
|
8
|
-
# from pydantic.json_schema import GenerateJsonSchema
|
|
9
7
|
|
|
10
8
|
from autobyteus.tools.base_tool import BaseTool
|
|
11
9
|
from autobyteus.tools.tool_category import ToolCategory
|
|
12
10
|
from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
11
|
+
from autobyteus.tools.pydantic_schema_converter import pydantic_to_parameter_schema
|
|
13
12
|
from autobyteus.task_management.schemas import TaskPlanDefinitionSchema
|
|
14
13
|
from autobyteus.task_management.converters import TaskPlanConverter, TaskBoardConverter
|
|
15
14
|
|
|
@@ -24,8 +23,6 @@ class PublishTaskPlan(BaseTool):
|
|
|
24
23
|
|
|
25
24
|
CATEGORY = ToolCategory.TASK_MANAGEMENT
|
|
26
25
|
|
|
27
|
-
# The failing custom InlineSchemaGenerator has been removed.
|
|
28
|
-
|
|
29
26
|
@classmethod
|
|
30
27
|
def get_name(cls) -> str:
|
|
31
28
|
return "PublishTaskPlan"
|
|
@@ -44,11 +41,8 @@ class PublishTaskPlan(BaseTool):
|
|
|
44
41
|
def get_argument_schema(cls) -> Optional[ParameterSchema]:
|
|
45
42
|
schema = ParameterSchema()
|
|
46
43
|
|
|
47
|
-
#
|
|
48
|
-
|
|
49
|
-
# JSON schema with $refs, which the framework handles correctly.
|
|
50
|
-
# This completely avoids the TypeError caused by the unsupported 'ref_strategy' argument.
|
|
51
|
-
object_json_schema = TaskPlanDefinitionSchema.model_json_schema()
|
|
44
|
+
# Convert the Pydantic model to our native ParameterSchema for the nested object
|
|
45
|
+
plan_object_schema = pydantic_to_parameter_schema(TaskPlanDefinitionSchema)
|
|
52
46
|
|
|
53
47
|
schema.add_parameter(ParameterDefinition(
|
|
54
48
|
name="plan",
|
|
@@ -59,7 +53,7 @@ class PublishTaskPlan(BaseTool):
|
|
|
59
53
|
"Each task must have a unique name within the plan."
|
|
60
54
|
),
|
|
61
55
|
required=True,
|
|
62
|
-
object_schema=
|
|
56
|
+
object_schema=plan_object_schema
|
|
63
57
|
))
|
|
64
58
|
return schema
|
|
65
59
|
|
|
@@ -83,12 +77,8 @@ class PublishTaskPlan(BaseTool):
|
|
|
83
77
|
return error_msg
|
|
84
78
|
|
|
85
79
|
try:
|
|
86
|
-
# Step 1: The input is now a dictionary, so we can directly validate it.
|
|
87
80
|
plan_definition_schema = TaskPlanDefinitionSchema(**plan)
|
|
88
|
-
|
|
89
|
-
# Step 2: Use the dedicated converter to create the internal TaskPlan object.
|
|
90
81
|
final_plan = TaskPlanConverter.from_schema(plan_definition_schema)
|
|
91
|
-
|
|
92
82
|
except (ValidationError, ValueError) as e:
|
|
93
83
|
error_msg = f"Invalid or inconsistent task plan provided: {e}"
|
|
94
84
|
logger.warning(f"Agent '{context.agent_id}' provided an invalid plan for PublishTaskPlan: {error_msg}")
|
|
@@ -100,12 +90,10 @@ class PublishTaskPlan(BaseTool):
|
|
|
100
90
|
|
|
101
91
|
if task_board.load_task_plan(final_plan):
|
|
102
92
|
logger.info(f"Agent '{context.agent_id}': Task plan published successfully. Returning new board status.")
|
|
103
|
-
# Convert the new state of the board back to an LLM-friendly schema and return it.
|
|
104
93
|
status_report_schema = TaskBoardConverter.to_schema(task_board)
|
|
105
94
|
if status_report_schema:
|
|
106
95
|
return status_report_schema.model_dump_json(indent=2)
|
|
107
96
|
else:
|
|
108
|
-
# This is a fallback case, shouldn't happen right after a successful load.
|
|
109
97
|
return "Task plan published successfully, but could not generate status report."
|
|
110
98
|
else:
|
|
111
99
|
error_msg = "Failed to load task plan onto the board. This can happen if the board implementation rejects the plan."
|
|
@@ -6,6 +6,7 @@ from pydantic import ValidationError
|
|
|
6
6
|
from autobyteus.tools.base_tool import BaseTool
|
|
7
7
|
from autobyteus.tools.tool_category import ToolCategory
|
|
8
8
|
from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
9
|
+
from autobyteus.tools.pydantic_schema_converter import pydantic_to_parameter_schema
|
|
9
10
|
from autobyteus.task_management.base_task_board import TaskStatus
|
|
10
11
|
from autobyteus.task_management.deliverable import FileDeliverable
|
|
11
12
|
from autobyteus.task_management.schemas import FileDeliverableSchema
|
|
@@ -53,14 +54,11 @@ class UpdateTaskStatus(BaseTool):
|
|
|
53
54
|
param_type=ParameterType.ARRAY,
|
|
54
55
|
description="Optional. A list of file deliverables to submit for this task, typically when status is 'completed'. Each deliverable must include a file_path and a summary.",
|
|
55
56
|
required=False,
|
|
56
|
-
array_item_schema=FileDeliverableSchema
|
|
57
|
+
array_item_schema=pydantic_to_parameter_schema(FileDeliverableSchema)
|
|
57
58
|
))
|
|
58
59
|
return schema
|
|
59
60
|
|
|
60
61
|
async def _execute(self, context: 'AgentContext', task_name: str, status: str, deliverables: Optional[List[Dict[str, Any]]] = None) -> str:
|
|
61
|
-
"""
|
|
62
|
-
Executes the tool to update a task's status and optionally submit deliverables.
|
|
63
|
-
"""
|
|
64
62
|
agent_name = context.config.name
|
|
65
63
|
log_msg = f"Agent '{agent_name}' is executing UpdateTaskStatus for task '{task_name}' to status '{status}'"
|
|
66
64
|
if deliverables:
|
|
@@ -84,12 +82,7 @@ class UpdateTaskStatus(BaseTool):
|
|
|
84
82
|
logger.warning(f"Agent '{agent_name}' tried to update task status, but no plan is loaded.")
|
|
85
83
|
return error_msg
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
target_task = None
|
|
89
|
-
for task in task_board.current_plan.tasks:
|
|
90
|
-
if task.task_name == task_name:
|
|
91
|
-
target_task = task
|
|
92
|
-
break
|
|
85
|
+
target_task = next((t for t in task_board.current_plan.tasks if t.task_name == task_name), None)
|
|
93
86
|
|
|
94
87
|
if not target_task:
|
|
95
88
|
error_msg = f"Failed to update status for task '{task_name}'. The task name does not exist on the current plan."
|
|
@@ -103,17 +96,11 @@ class UpdateTaskStatus(BaseTool):
|
|
|
103
96
|
logger.warning(f"Agent '{agent_name}' provided invalid status for UpdateTaskStatus: {status}")
|
|
104
97
|
return f"Error: {error_msg}"
|
|
105
98
|
|
|
106
|
-
# --- Process Deliverables FIRST --- (CORRECTED ORDER)
|
|
107
99
|
if deliverables:
|
|
108
100
|
try:
|
|
109
101
|
for d_data in deliverables:
|
|
110
|
-
# Validate and create the internal deliverable object
|
|
111
102
|
deliverable_schema = FileDeliverableSchema(**d_data)
|
|
112
|
-
full_deliverable = FileDeliverable(
|
|
113
|
-
**deliverable_schema.model_dump(),
|
|
114
|
-
author_agent_name=agent_name
|
|
115
|
-
)
|
|
116
|
-
# Append to the task object
|
|
103
|
+
full_deliverable = FileDeliverable(**deliverable_schema.model_dump(), author_agent_name=agent_name)
|
|
117
104
|
target_task.file_deliverables.append(full_deliverable)
|
|
118
105
|
logger.info(f"Agent '{agent_name}' successfully processed and added {len(deliverables)} deliverables to task '{task_name}'.")
|
|
119
106
|
except (ValidationError, TypeError) as e:
|
|
@@ -121,8 +108,6 @@ class UpdateTaskStatus(BaseTool):
|
|
|
121
108
|
logger.warning(f"Agent '{agent_name}': {error_msg}")
|
|
122
109
|
return f"Error: {error_msg}"
|
|
123
110
|
|
|
124
|
-
# --- Update Status SECOND --- (CORRECTED ORDER)
|
|
125
|
-
# This will now emit an event with the deliverables already attached to the task.
|
|
126
111
|
if not task_board.update_task_status(target_task.task_id, status_enum, agent_name):
|
|
127
112
|
error_msg = f"Failed to update status for task '{task_name}'. An unexpected error occurred on the task board."
|
|
128
113
|
logger.error(f"Agent '{agent_name}': {error_msg}")
|
autobyteus/tools/__init__.py
CHANGED
|
@@ -16,18 +16,18 @@ from .tool_category import ToolCategory
|
|
|
16
16
|
# --- Re-export specific tools for easier access ---
|
|
17
17
|
|
|
18
18
|
# Functional tools (decorated functions are now instances)
|
|
19
|
-
from .ask_user_input import ask_user_input
|
|
20
19
|
from .pdf_downloader import pdf_downloader
|
|
21
20
|
from .bash.bash_executor import bash_executor
|
|
22
21
|
from .file.file_reader import file_reader
|
|
23
22
|
from .file.file_writer import file_writer
|
|
24
23
|
|
|
25
24
|
# General Class-based tools
|
|
25
|
+
from .google_search import GoogleSearch
|
|
26
26
|
from .image_downloader import ImageDownloader
|
|
27
27
|
from .timer import Timer
|
|
28
|
+
from .multimedia.image_tools import GenerateImageTool, EditImageTool
|
|
28
29
|
|
|
29
30
|
# Standalone Browser tools
|
|
30
|
-
from .browser.standalone.google_search_ui import GoogleSearch
|
|
31
31
|
from .browser.standalone.navigate_to import NavigateTo as StandaloneNavigateTo # Alias to avoid name clash
|
|
32
32
|
from .browser.standalone.webpage_reader import WebPageReader as StandaloneWebPageReader # Alias
|
|
33
33
|
from .browser.standalone.webpage_screenshot_taker import WebPageScreenshotTaker as StandaloneWebPageScreenshotTaker # Alias
|
|
@@ -53,18 +53,19 @@ __all__ = [
|
|
|
53
53
|
"ToolCategory",
|
|
54
54
|
|
|
55
55
|
# Re-exported functional tool instances
|
|
56
|
-
"ask_user_input",
|
|
57
56
|
"pdf_downloader",
|
|
58
57
|
"bash_executor",
|
|
59
58
|
"file_reader",
|
|
60
59
|
"file_writer",
|
|
61
60
|
|
|
62
61
|
# Re-exported general class-based tools
|
|
62
|
+
"GoogleSearch",
|
|
63
63
|
"ImageDownloader",
|
|
64
64
|
"Timer",
|
|
65
|
+
"GenerateImageTool",
|
|
66
|
+
"EditImageTool",
|
|
65
67
|
|
|
66
68
|
# Re-exported Standalone Browser tools
|
|
67
|
-
"GoogleSearch",
|
|
68
69
|
"StandaloneNavigateTo",
|
|
69
70
|
"StandaloneWebPageReader",
|
|
70
71
|
"StandaloneWebPageScreenshotTaker",
|