autobyteus 1.1.4__py3-none-any.whl → 1.1.5__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/__init__.py +4 -2
- autobyteus/agent/context/agent_config.py +0 -4
- autobyteus/agent/context/agent_context_registry.py +73 -0
- autobyteus/agent/events/notifiers.py +4 -0
- autobyteus/agent/handlers/inter_agent_message_event_handler.py +7 -2
- autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
- autobyteus/agent/handlers/user_input_message_event_handler.py +15 -0
- autobyteus/agent/message/send_message_to.py +29 -23
- autobyteus/agent/runtime/agent_runtime.py +10 -2
- autobyteus/agent/sender_type.py +15 -0
- autobyteus/agent/streaming/agent_event_stream.py +6 -0
- autobyteus/agent/streaming/stream_event_payloads.py +12 -0
- autobyteus/agent/streaming/stream_events.py +3 -0
- autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
- autobyteus/agent_team/__init__.py +1 -0
- autobyteus/agent_team/agent_team.py +93 -0
- autobyteus/agent_team/agent_team_builder.py +184 -0
- autobyteus/agent_team/base_agent_team.py +86 -0
- autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
- autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
- autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
- autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
- autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
- autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
- autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
- autobyteus/agent_team/context/__init__.py +17 -0
- autobyteus/agent_team/context/agent_team_config.py +33 -0
- autobyteus/agent_team/context/agent_team_context.py +61 -0
- autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
- autobyteus/agent_team/context/team_manager.py +147 -0
- autobyteus/agent_team/context/team_node_config.py +76 -0
- autobyteus/agent_team/events/__init__.py +29 -0
- autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
- autobyteus/agent_team/events/agent_team_events.py +53 -0
- autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
- autobyteus/agent_team/exceptions.py +8 -0
- autobyteus/agent_team/factory/__init__.py +9 -0
- autobyteus/agent_team/factory/agent_team_factory.py +99 -0
- autobyteus/agent_team/handlers/__init__.py +19 -0
- autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
- autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
- autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
- autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
- autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
- autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
- autobyteus/agent_team/phases/__init__.py +11 -0
- autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
- autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
- autobyteus/agent_team/runtime/__init__.py +13 -0
- autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
- autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
- autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
- autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
- autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
- autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
- autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
- autobyteus/agent_team/streaming/__init__.py +26 -0
- autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
- autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
- autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
- autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
- autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
- autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
- autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
- autobyteus/agent_team/task_notification/__init__.py +11 -0
- autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
- autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
- autobyteus/agent_team/utils/__init__.py +9 -0
- autobyteus/agent_team/utils/wait_for_idle.py +46 -0
- autobyteus/cli/agent_team_tui/__init__.py +4 -0
- autobyteus/cli/agent_team_tui/app.py +210 -0
- autobyteus/cli/agent_team_tui/state.py +180 -0
- autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
- autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
- autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
- autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
- autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
- autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
- autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
- autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
- autobyteus/events/event_types.py +7 -2
- autobyteus/llm/api/autobyteus_llm.py +11 -12
- autobyteus/llm/api/lmstudio_llm.py +10 -13
- autobyteus/llm/api/ollama_llm.py +8 -13
- autobyteus/llm/autobyteus_provider.py +73 -46
- autobyteus/llm/llm_factory.py +102 -140
- autobyteus/llm/lmstudio_provider.py +63 -48
- autobyteus/llm/models.py +83 -53
- autobyteus/llm/ollama_provider.py +69 -61
- autobyteus/llm/ollama_provider_resolver.py +1 -0
- autobyteus/llm/providers.py +13 -13
- autobyteus/llm/runtimes.py +11 -0
- autobyteus/task_management/__init__.py +43 -0
- autobyteus/task_management/base_task_board.py +68 -0
- autobyteus/task_management/converters/__init__.py +11 -0
- autobyteus/task_management/converters/task_board_converter.py +64 -0
- autobyteus/task_management/converters/task_plan_converter.py +48 -0
- autobyteus/task_management/deliverable.py +16 -0
- autobyteus/task_management/deliverables/__init__.py +8 -0
- autobyteus/task_management/deliverables/file_deliverable.py +15 -0
- autobyteus/task_management/events.py +27 -0
- autobyteus/task_management/in_memory_task_board.py +126 -0
- autobyteus/task_management/schemas/__init__.py +15 -0
- autobyteus/task_management/schemas/deliverable_schema.py +13 -0
- autobyteus/task_management/schemas/plan_definition.py +35 -0
- autobyteus/task_management/schemas/task_status_report.py +27 -0
- autobyteus/task_management/task_plan.py +110 -0
- autobyteus/task_management/tools/__init__.py +14 -0
- autobyteus/task_management/tools/get_task_board_status.py +68 -0
- autobyteus/task_management/tools/publish_task_plan.py +113 -0
- autobyteus/task_management/tools/update_task_status.py +135 -0
- autobyteus/tools/bash/bash_executor.py +59 -14
- autobyteus/tools/mcp/config_service.py +63 -58
- autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
- autobyteus/tools/mcp/server_instance_manager.py +30 -4
- autobyteus/tools/mcp/tool_registrar.py +103 -50
- autobyteus/tools/parameter_schema.py +17 -11
- autobyteus/tools/registry/tool_definition.py +24 -29
- autobyteus/tools/tool_category.py +1 -0
- autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
- autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
- autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
- autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
- autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
- autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
- autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
- autobyteus/tools/usage/providers/__init__.py +2 -12
- autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
- autobyteus/tools/usage/registries/__init__.py +7 -12
- autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
- autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
- autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/METADATA +3 -3
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/RECORD +146 -72
- examples/agent_team/__init__.py +1 -0
- examples/run_browser_agent.py +17 -15
- examples/run_google_slides_agent.py +17 -16
- examples/run_poem_writer.py +22 -12
- examples/run_sqlite_agent.py +17 -15
- autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
- autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
- autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
- autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
- autobyteus/tools/usage/providers/json_example_provider.py +0 -32
- autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
- autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
- autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
- autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
- autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
- autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
- autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
- autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
- autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
- examples/workflow/__init__.py +0 -1
- examples/workflow/run_basic_research_workflow.py +0 -189
- examples/workflow/run_code_review_workflow.py +0 -269
- examples/workflow/run_debate_workflow.py +0 -212
- examples/workflow/run_workflow_with_tui.py +0 -153
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
- {autobyteus-1.1.4.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains converters for translating LLM-friendly definition schemas into
|
|
3
|
+
internal task management objects.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from autobyteus.task_management.task_plan import TaskPlan, Task
|
|
8
|
+
from autobyteus.task_management.schemas import TaskPlanDefinitionSchema
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
class TaskPlanConverter:
|
|
13
|
+
"""A converter to transform a TaskPlanDefinitionSchema into a system-ready TaskPlan."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def from_schema(plan_definition_schema: TaskPlanDefinitionSchema) -> TaskPlan:
|
|
17
|
+
"""
|
|
18
|
+
Converts a TaskPlanDefinitionSchema object from an LLM into a fully-hydrated,
|
|
19
|
+
internal TaskPlan object.
|
|
20
|
+
|
|
21
|
+
This process involves:
|
|
22
|
+
1. Converting each TaskDefinitionSchema into a system Task object (which generates a unique task_id).
|
|
23
|
+
2. Assembling these Tasks into a TaskPlan (which generates a unique plan_id).
|
|
24
|
+
3. Hydrating the dependencies by replacing task_name references with the newly generated task_ids.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
plan_definition_schema: The Pydantic model representing the LLM's output.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
A system-ready, internally consistent TaskPlan object.
|
|
31
|
+
"""
|
|
32
|
+
logger.debug(f"Converting TaskPlanDefinitionSchema for goal: '{plan_definition_schema.overall_goal}'")
|
|
33
|
+
|
|
34
|
+
# Step 1: Convert "TaskDefinitionSchema" objects into final, internal Task objects.
|
|
35
|
+
# This automatically generates the system-level 'task_id' for each.
|
|
36
|
+
final_tasks = [Task(**task_def.model_dump()) for task_def in plan_definition_schema.tasks]
|
|
37
|
+
|
|
38
|
+
# Step 2: Create the final TaskPlan object. This generates the 'plan_id'.
|
|
39
|
+
final_plan = TaskPlan(
|
|
40
|
+
overall_goal=plan_definition_schema.overall_goal,
|
|
41
|
+
tasks=final_tasks
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Step 3: Hydrate dependencies: convert task_name references to system task_ids.
|
|
45
|
+
final_plan.hydrate_dependencies()
|
|
46
|
+
|
|
47
|
+
logger.info(f"Successfully converted TaskPlanDefinitionSchema to internal TaskPlan with ID '{final_plan.plan_id}'.")
|
|
48
|
+
return final_plan
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/deliverable.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the core data model for a file deliverable.
|
|
4
|
+
"""
|
|
5
|
+
import datetime
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
class FileDeliverable(BaseModel):
|
|
9
|
+
"""
|
|
10
|
+
Represents the full, internal record of a file deliverable once it has been
|
|
11
|
+
submitted and attached to a task.
|
|
12
|
+
"""
|
|
13
|
+
file_path: str
|
|
14
|
+
summary: str
|
|
15
|
+
author_agent_name: str
|
|
16
|
+
timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines the data models for a file deliverable.
|
|
3
|
+
"""
|
|
4
|
+
import datetime
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
class FileDeliverable(BaseModel):
|
|
8
|
+
"""
|
|
9
|
+
Represents the full, internal record of a file deliverable once it has been
|
|
10
|
+
submitted and attached to a task.
|
|
11
|
+
"""
|
|
12
|
+
file_path: str
|
|
13
|
+
summary: str
|
|
14
|
+
author_agent_name: str
|
|
15
|
+
timestamp: datetime.datetime = Field(default_factory=datetime.datetime.utcnow)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/events.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the Pydantic models for events emitted by a TaskBoard.
|
|
4
|
+
"""
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
from autobyteus.task_management.task_plan import TaskPlan
|
|
9
|
+
from autobyteus.task_management.base_task_board import TaskStatus
|
|
10
|
+
from .deliverable import FileDeliverable
|
|
11
|
+
|
|
12
|
+
class BaseTaskBoardEvent(BaseModel):
|
|
13
|
+
"""Base class for all task board events."""
|
|
14
|
+
team_id: str
|
|
15
|
+
plan_id: Optional[str]
|
|
16
|
+
|
|
17
|
+
class TaskPlanPublishedEvent(BaseTaskBoardEvent):
|
|
18
|
+
"""Payload for when a new task plan is published to the board."""
|
|
19
|
+
plan: TaskPlan
|
|
20
|
+
|
|
21
|
+
class TaskStatusUpdatedEvent(BaseTaskBoardEvent):
|
|
22
|
+
"""Payload for when a task's status is updated."""
|
|
23
|
+
task_id: str
|
|
24
|
+
new_status: TaskStatus
|
|
25
|
+
agent_name: str
|
|
26
|
+
# This field is added to ensure listeners get the full state, including deliverables.
|
|
27
|
+
deliverables: Optional[List[FileDeliverable]] = None
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/in_memory_task_board.py
|
|
2
|
+
"""
|
|
3
|
+
An in-memory implementation of the BaseTaskBoard.
|
|
4
|
+
It tracks task statuses in a simple dictionary and emits events on state changes.
|
|
5
|
+
"""
|
|
6
|
+
import logging
|
|
7
|
+
from typing import Optional, List, Dict, Any
|
|
8
|
+
from enum import Enum
|
|
9
|
+
|
|
10
|
+
from autobyteus.events.event_types import EventType
|
|
11
|
+
from .task_plan import TaskPlan, Task
|
|
12
|
+
from .base_task_board import BaseTaskBoard, TaskStatus
|
|
13
|
+
from .events import TaskPlanPublishedEvent, TaskStatusUpdatedEvent
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class InMemoryTaskBoard(BaseTaskBoard):
|
|
18
|
+
"""
|
|
19
|
+
An in-memory, dictionary-based implementation of the TaskBoard that emits
|
|
20
|
+
events on state changes.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self, team_id: str):
|
|
23
|
+
"""
|
|
24
|
+
Initializes the InMemoryTaskBoard.
|
|
25
|
+
"""
|
|
26
|
+
# BaseTaskBoard now handles EventEmitter initialization
|
|
27
|
+
super().__init__(team_id=team_id)
|
|
28
|
+
self.current_plan: Optional[TaskPlan] = None
|
|
29
|
+
self.task_statuses: Dict[str, TaskStatus] = {}
|
|
30
|
+
self._task_map: Dict[str, Task] = {}
|
|
31
|
+
logger.info(f"InMemoryTaskBoard initialized for team '{self.team_id}'.")
|
|
32
|
+
|
|
33
|
+
def load_task_plan(self, plan: TaskPlan) -> bool:
|
|
34
|
+
"""
|
|
35
|
+
Loads a new plan onto the board, resetting its state and emitting an event.
|
|
36
|
+
"""
|
|
37
|
+
if not isinstance(plan, TaskPlan):
|
|
38
|
+
logger.error(f"Team '{self.team_id}': Failed to load task plan. Provided object is not a TaskPlan.")
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
self.current_plan = plan
|
|
42
|
+
self.task_statuses = {task.task_id: TaskStatus.NOT_STARTED for task in plan.tasks}
|
|
43
|
+
self._task_map = {task.task_id: task for task in plan.tasks}
|
|
44
|
+
|
|
45
|
+
logger.info(f"Team '{self.team_id}': New TaskPlan '{plan.plan_id}' loaded. Emitting event.")
|
|
46
|
+
|
|
47
|
+
# Emit event
|
|
48
|
+
event_payload = TaskPlanPublishedEvent(
|
|
49
|
+
team_id=self.team_id,
|
|
50
|
+
plan_id=plan.plan_id,
|
|
51
|
+
plan=plan
|
|
52
|
+
)
|
|
53
|
+
self.emit(EventType.TASK_BOARD_PLAN_PUBLISHED, payload=event_payload)
|
|
54
|
+
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
def update_task_status(self, task_id: str, status: TaskStatus, agent_name: str) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
Updates the status of a specific task and emits an event.
|
|
60
|
+
"""
|
|
61
|
+
if task_id not in self.task_statuses:
|
|
62
|
+
logger.warning(f"Team '{self.team_id}': Agent '{agent_name}' attempted to update status for non-existent task_id '{task_id}'.")
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
old_status = self.task_statuses.get(task_id, "N/A")
|
|
66
|
+
self.task_statuses[task_id] = status
|
|
67
|
+
log_msg = f"Team '{self.team_id}': Status of task '{task_id}' updated from '{old_status.value if isinstance(old_status, Enum) else old_status}' to '{status.value}' by agent '{agent_name}'."
|
|
68
|
+
logger.info(log_msg)
|
|
69
|
+
|
|
70
|
+
# Find the task to get its deliverables for the event payload
|
|
71
|
+
task = self._task_map.get(task_id)
|
|
72
|
+
task_deliverables = task.file_deliverables if task else None
|
|
73
|
+
|
|
74
|
+
# Emit event
|
|
75
|
+
event_payload = TaskStatusUpdatedEvent(
|
|
76
|
+
team_id=self.team_id,
|
|
77
|
+
plan_id=self.current_plan.plan_id if self.current_plan else None,
|
|
78
|
+
task_id=task_id,
|
|
79
|
+
new_status=status,
|
|
80
|
+
agent_name=agent_name,
|
|
81
|
+
deliverables=task_deliverables
|
|
82
|
+
)
|
|
83
|
+
self.emit(EventType.TASK_BOARD_STATUS_UPDATED, payload=event_payload)
|
|
84
|
+
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
def get_status_overview(self) -> Dict[str, Any]:
|
|
88
|
+
"""
|
|
89
|
+
Returns a serializable dictionary of the board's current state.
|
|
90
|
+
"""
|
|
91
|
+
if not self.current_plan:
|
|
92
|
+
return {
|
|
93
|
+
"plan_id": None,
|
|
94
|
+
"overall_goal": None,
|
|
95
|
+
"task_statuses": {},
|
|
96
|
+
"tasks": []
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
"plan_id": self.current_plan.plan_id,
|
|
101
|
+
"overall_goal": self.current_plan.overall_goal,
|
|
102
|
+
"task_statuses": {task_id: status.value for task_id, status in self.task_statuses.items()},
|
|
103
|
+
"tasks": [task.model_dump() for task in self.current_plan.tasks]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
def get_next_runnable_tasks(self) -> List[Task]:
|
|
107
|
+
"""
|
|
108
|
+
Calculates which tasks can be executed now based on dependencies and statuses.
|
|
109
|
+
"""
|
|
110
|
+
runnable_tasks: List[Task] = []
|
|
111
|
+
if not self.current_plan:
|
|
112
|
+
return runnable_tasks
|
|
113
|
+
|
|
114
|
+
for task_id, status in self.task_statuses.items():
|
|
115
|
+
if status == TaskStatus.NOT_STARTED:
|
|
116
|
+
task = self._task_map.get(task_id)
|
|
117
|
+
if not task: continue
|
|
118
|
+
dependencies = task.dependencies
|
|
119
|
+
if not dependencies:
|
|
120
|
+
runnable_tasks.append(task)
|
|
121
|
+
continue
|
|
122
|
+
dependencies_met = all(self.task_statuses.get(dep_id) == TaskStatus.COMPLETED for dep_id in dependencies)
|
|
123
|
+
if dependencies_met:
|
|
124
|
+
runnable_tasks.append(task)
|
|
125
|
+
|
|
126
|
+
return runnable_tasks
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/schemas/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Exposes the public schema models for the task management module.
|
|
4
|
+
"""
|
|
5
|
+
from .plan_definition import TaskPlanDefinitionSchema, TaskDefinitionSchema
|
|
6
|
+
from .task_status_report import TaskStatusReportSchema, TaskStatusReportItemSchema
|
|
7
|
+
from .deliverable_schema import FileDeliverableSchema
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"TaskPlanDefinitionSchema",
|
|
11
|
+
"TaskDefinitionSchema",
|
|
12
|
+
"TaskStatusReportSchema",
|
|
13
|
+
"TaskStatusReportItemSchema",
|
|
14
|
+
"FileDeliverableSchema",
|
|
15
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/schemas/deliverable_schema.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the Pydantic schema for submitting a file deliverable.
|
|
4
|
+
"""
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
class FileDeliverableSchema(BaseModel):
|
|
8
|
+
"""
|
|
9
|
+
A Pydantic model representing the arguments for submitting a single
|
|
10
|
+
file deliverable. This is used as an input schema for tools.
|
|
11
|
+
"""
|
|
12
|
+
file_path: str = Field(..., description="The relative path to the file being submitted.")
|
|
13
|
+
summary: str = Field(..., description="A summary of the work done on this file, explaining what is new or what was updated.")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defines the Pydantic models for task plan "definitions".
|
|
3
|
+
|
|
4
|
+
These models represent the exact structure that an LLM is expected to generate.
|
|
5
|
+
They serve as a blueprint or definition for a plan, which the system then uses
|
|
6
|
+
to construct the final, internal TaskPlan object. They are used for input
|
|
7
|
+
validation and for programmatically generating a JSON schema for tool descriptions.
|
|
8
|
+
"""
|
|
9
|
+
from typing import List
|
|
10
|
+
from pydantic import BaseModel, Field, field_validator
|
|
11
|
+
|
|
12
|
+
class TaskDefinitionSchema(BaseModel):
|
|
13
|
+
"""A Pydantic model representing a single task as defined by an LLM."""
|
|
14
|
+
task_name: str = Field(..., description="A short, unique, descriptive name for this task within the plan (e.g., 'setup_project', 'implement_scraper'). Used for defining dependencies.")
|
|
15
|
+
assignee_name: str = Field(..., description="The name of the agent or sub-team assigned to this task.")
|
|
16
|
+
description: str = Field(..., description="A detailed description of the task.")
|
|
17
|
+
dependencies: List[str] = Field(
|
|
18
|
+
default_factory=list,
|
|
19
|
+
description="A list of 'task_name' values for tasks that must be completed first."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
class TaskPlanDefinitionSchema(BaseModel):
|
|
23
|
+
"""A Pydantic model representing a full task plan as generated by an LLM."""
|
|
24
|
+
overall_goal: str = Field(..., description="The high-level objective of the entire plan.")
|
|
25
|
+
tasks: List[TaskDefinitionSchema] = Field(..., description="The list of tasks that make up this plan.")
|
|
26
|
+
|
|
27
|
+
@field_validator('tasks')
|
|
28
|
+
def task_names_must_be_unique(cls, tasks: List[TaskDefinitionSchema]) -> List[TaskDefinitionSchema]:
|
|
29
|
+
"""Ensures that the LLM-provided task_names are unique within the plan."""
|
|
30
|
+
seen_names = set()
|
|
31
|
+
for task in tasks:
|
|
32
|
+
if task.task_name in seen_names:
|
|
33
|
+
raise ValueError(f"Duplicate task_name '{task.task_name}' found in task list. Each task_name must be unique.")
|
|
34
|
+
seen_names.add(task.task_name)
|
|
35
|
+
return tasks
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/schemas/task_status_report.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the Pydantic models for LLM-friendly status reports.
|
|
4
|
+
|
|
5
|
+
These models are designed to be returned by tools to the LLM, providing a
|
|
6
|
+
clear and consistent structure that mirrors the input schemas (like TaskPlanDefinitionSchema)
|
|
7
|
+
but includes dynamic state information (like task status).
|
|
8
|
+
"""
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from autobyteus.task_management.base_task_board import TaskStatus
|
|
13
|
+
from autobyteus.task_management.deliverable import FileDeliverable
|
|
14
|
+
|
|
15
|
+
class TaskStatusReportItemSchema(BaseModel):
|
|
16
|
+
"""Represents the status of a single task in an LLM-friendly format."""
|
|
17
|
+
task_name: str = Field(..., description="The unique, descriptive name for this task.")
|
|
18
|
+
assignee_name: str = Field(..., description="The name of the agent or sub-team assigned to this task.")
|
|
19
|
+
description: str = Field(..., description="A detailed description of the task.")
|
|
20
|
+
dependencies: List[str] = Field(..., description="A list of 'task_name' values for tasks that must be completed first.")
|
|
21
|
+
status: TaskStatus = Field(..., description="The current status of this task.")
|
|
22
|
+
file_deliverables: List[FileDeliverable] = Field(default_factory=list, description="A list of files submitted as deliverables for this task.")
|
|
23
|
+
|
|
24
|
+
class TaskStatusReportSchema(BaseModel):
|
|
25
|
+
"""Represents a full task board status report in an LLM-friendly format."""
|
|
26
|
+
overall_goal: str = Field(..., description="The high-level objective of the entire plan.")
|
|
27
|
+
tasks: List[TaskStatusReportItemSchema] = Field(..., description="The list of tasks and their current statuses.")
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/task_plan.py
|
|
2
|
+
"""
|
|
3
|
+
Defines the data structures for a task plan and its constituent tasks.
|
|
4
|
+
These models represent the static, intended structure of a plan of action.
|
|
5
|
+
"""
|
|
6
|
+
import logging
|
|
7
|
+
import uuid
|
|
8
|
+
from typing import List, Dict, Any
|
|
9
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
10
|
+
|
|
11
|
+
# To avoid circular import, we use a string forward reference.
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from autobyteus.task_management.deliverable import FileDeliverable
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
def generate_task_id():
|
|
19
|
+
"""Generates a unique task identifier."""
|
|
20
|
+
return f"task_{uuid.uuid4().hex}"
|
|
21
|
+
|
|
22
|
+
def generate_plan_id():
|
|
23
|
+
"""Generates a unique plan identifier."""
|
|
24
|
+
return f"plan_{uuid.uuid4().hex}"
|
|
25
|
+
|
|
26
|
+
class Task(BaseModel):
|
|
27
|
+
"""
|
|
28
|
+
Represents a single, discrete unit of work within a larger TaskPlan.
|
|
29
|
+
"""
|
|
30
|
+
task_name: str = Field(..., description="A short, unique, descriptive name for this task within the plan (e.g., 'setup_project', 'implement_scraper'). Used for defining dependencies.")
|
|
31
|
+
|
|
32
|
+
task_id: str = Field(default_factory=generate_task_id, description="A unique system-generated identifier for this task within the plan.")
|
|
33
|
+
|
|
34
|
+
assignee_name: str = Field(..., description="The unique name of the agent or sub-team responsible for executing this task (e.g., 'SoftwareEngineer', 'ResearchTeam').")
|
|
35
|
+
description: str = Field(..., description="A clear and concise description of what this task entails.")
|
|
36
|
+
|
|
37
|
+
dependencies: List[str] = Field(
|
|
38
|
+
default_factory=list,
|
|
39
|
+
description="A list of 'task_name' values for tasks that must be completed before this one can be started."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# This is the updated field as per user request.
|
|
43
|
+
file_deliverables: List["FileDeliverable"] = Field(
|
|
44
|
+
default_factory=list,
|
|
45
|
+
description="A list of file deliverables that were produced as a result of completing this task."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@model_validator(mode='before')
|
|
49
|
+
@classmethod
|
|
50
|
+
def handle_local_id_compatibility(cls, data: Any) -> Any:
|
|
51
|
+
"""Handles backward compatibility for the 'local_id' field."""
|
|
52
|
+
if isinstance(data, dict) and 'local_id' in data:
|
|
53
|
+
data['task_name'] = data.pop('local_id')
|
|
54
|
+
# Compatibility for old artifact field
|
|
55
|
+
if isinstance(data, dict) and 'produced_artifact_ids' in data:
|
|
56
|
+
del data['produced_artifact_ids']
|
|
57
|
+
return data
|
|
58
|
+
|
|
59
|
+
def model_post_init(self, __context: Any) -> None:
|
|
60
|
+
"""Called after the model is initialized and validated."""
|
|
61
|
+
logger.debug(f"Task created: Name='{self.task_name}', SystemID='{self.task_id}', Assignee='{self.assignee_name}'")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TaskPlan(BaseModel):
|
|
65
|
+
"""
|
|
66
|
+
Represents a complete, static plan for achieving a high-level goal.
|
|
67
|
+
It is composed of a list of interconnected tasks.
|
|
68
|
+
"""
|
|
69
|
+
plan_id: str = Field(default_factory=generate_plan_id, description="A unique system-generated identifier for this entire plan.")
|
|
70
|
+
|
|
71
|
+
overall_goal: str = Field(..., description="The high-level objective that this plan is designed to achieve.")
|
|
72
|
+
tasks: List[Task] = Field(..., description="The list of tasks that make up this plan.")
|
|
73
|
+
|
|
74
|
+
@field_validator('tasks')
|
|
75
|
+
def task_names_must_be_unique(cls, tasks: List[Task]) -> List[Task]:
|
|
76
|
+
"""Ensures that the LLM-provided task_names are unique within the plan."""
|
|
77
|
+
seen_names = set()
|
|
78
|
+
for task in tasks:
|
|
79
|
+
if task.task_name in seen_names:
|
|
80
|
+
raise ValueError(f"Duplicate task_name '{task.task_name}' found in task list. Each task_name must be unique within the plan.")
|
|
81
|
+
seen_names.add(task.task_name)
|
|
82
|
+
return tasks
|
|
83
|
+
|
|
84
|
+
def hydrate_dependencies(self) -> 'TaskPlan':
|
|
85
|
+
"""
|
|
86
|
+
Converts the dependency list of task_names to system-generated task_ids.
|
|
87
|
+
This makes the plan internally consistent and ready for execution.
|
|
88
|
+
"""
|
|
89
|
+
name_to_system_id_map = {task.task_name: task.task_id for task in self.tasks}
|
|
90
|
+
|
|
91
|
+
for task in self.tasks:
|
|
92
|
+
# Create a new list for the resolved dependency IDs
|
|
93
|
+
resolved_deps = []
|
|
94
|
+
for dep_name in task.dependencies:
|
|
95
|
+
if dep_name not in name_to_system_id_map:
|
|
96
|
+
raise ValueError(f"Task '{task.task_name}' has an invalid dependency: '{dep_name}' does not correspond to any task's name.")
|
|
97
|
+
resolved_deps.append(name_to_system_id_map[dep_name])
|
|
98
|
+
# Replace the old list of names with the new list of system_ids
|
|
99
|
+
task.dependencies = resolved_deps
|
|
100
|
+
|
|
101
|
+
logger.debug(f"TaskPlan '{self.plan_id}' successfully hydrated dependencies.")
|
|
102
|
+
return self
|
|
103
|
+
|
|
104
|
+
def model_post_init(self, __context: Any) -> None:
|
|
105
|
+
"""Called after the model is initialized and validated."""
|
|
106
|
+
logger.debug(f"TaskPlan created: ID='{self.plan_id}', Tasks={len(self.tasks)}")
|
|
107
|
+
|
|
108
|
+
# This is necessary for Pydantic v2 to correctly handle the recursive model
|
|
109
|
+
from autobyteus.task_management.deliverable import FileDeliverable
|
|
110
|
+
Task.model_rebuild()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/tools/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
This package contains the class-based tools related to task and project
|
|
4
|
+
management within an agent team.
|
|
5
|
+
"""
|
|
6
|
+
from .get_task_board_status import GetTaskBoardStatus
|
|
7
|
+
from .publish_task_plan import PublishTaskPlan
|
|
8
|
+
from .update_task_status import UpdateTaskStatus
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"GetTaskBoardStatus",
|
|
12
|
+
"PublishTaskPlan",
|
|
13
|
+
"UpdateTaskStatus",
|
|
14
|
+
]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/tools/get_task_board_status.py
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING, Optional
|
|
5
|
+
|
|
6
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
7
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
8
|
+
from autobyteus.task_management.converters import TaskBoardConverter
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from autobyteus.agent.context import AgentContext
|
|
12
|
+
from autobyteus.agent_team.context import AgentTeamContext
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class GetTaskBoardStatus(BaseTool):
|
|
17
|
+
"""A tool for agents to get a current snapshot of the team's TaskBoard."""
|
|
18
|
+
|
|
19
|
+
CATEGORY = ToolCategory.TASK_MANAGEMENT
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def get_name(cls) -> str:
|
|
23
|
+
return "GetTaskBoardStatus"
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def get_description(cls) -> str:
|
|
27
|
+
return (
|
|
28
|
+
"Retrieves the current status of the team's task board, including the overall goal "
|
|
29
|
+
"and the status of all individual tasks. Returns the status as a structured, LLM-friendly JSON string."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def get_argument_schema(cls) -> Optional[None]:
|
|
34
|
+
# This tool takes no arguments.
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
async def _execute(self, context: 'AgentContext') -> str:
|
|
38
|
+
"""
|
|
39
|
+
Executes the tool by fetching the task board and using a converter to
|
|
40
|
+
generate an LLM-friendly report.
|
|
41
|
+
"""
|
|
42
|
+
logger.info(f"Agent '{context.agent_id}' is executing GetTaskBoardStatus.")
|
|
43
|
+
|
|
44
|
+
team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
|
|
45
|
+
if not team_context:
|
|
46
|
+
error_msg = "Error: Team context is not available to the agent. Cannot access the task board."
|
|
47
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}")
|
|
48
|
+
return error_msg
|
|
49
|
+
|
|
50
|
+
task_board = getattr(team_context.state, 'task_board', None)
|
|
51
|
+
if not task_board:
|
|
52
|
+
error_msg = "Error: Task board has not been initialized for this team."
|
|
53
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}")
|
|
54
|
+
return error_msg
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
status_report_schema = TaskBoardConverter.to_schema(task_board)
|
|
58
|
+
|
|
59
|
+
if not status_report_schema:
|
|
60
|
+
return "The task board is currently empty. No plan has been published."
|
|
61
|
+
|
|
62
|
+
logger.info(f"Agent '{context.agent_id}' successfully retrieved and formatted task board status.")
|
|
63
|
+
return status_report_schema.model_dump_json(indent=2)
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
error_msg = f"An unexpected error occurred while retrieving or formatting task board status: {e}"
|
|
67
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}", exc_info=True)
|
|
68
|
+
return error_msg
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# file: autobyteus/autobyteus/task_management/tools/publish_task_plan.py
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Dict, Any
|
|
5
|
+
|
|
6
|
+
from pydantic import ValidationError
|
|
7
|
+
# No longer need GenerateJsonSchema from pydantic.json_schema
|
|
8
|
+
# from pydantic.json_schema import GenerateJsonSchema
|
|
9
|
+
|
|
10
|
+
from autobyteus.tools.base_tool import BaseTool
|
|
11
|
+
from autobyteus.tools.tool_category import ToolCategory
|
|
12
|
+
from autobyteus.tools.parameter_schema import ParameterSchema, ParameterDefinition, ParameterType
|
|
13
|
+
from autobyteus.task_management.schemas import TaskPlanDefinitionSchema
|
|
14
|
+
from autobyteus.task_management.converters import TaskPlanConverter, TaskBoardConverter
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from autobyteus.agent.context import AgentContext
|
|
18
|
+
from autobyteus.agent_team.context import AgentTeamContext
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
class PublishTaskPlan(BaseTool):
|
|
23
|
+
"""A tool for the coordinator to parse and load a generated plan into the TaskBoard."""
|
|
24
|
+
|
|
25
|
+
CATEGORY = ToolCategory.TASK_MANAGEMENT
|
|
26
|
+
|
|
27
|
+
# The failing custom InlineSchemaGenerator has been removed.
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def get_name(cls) -> str:
|
|
31
|
+
return "PublishTaskPlan"
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def get_description(cls) -> str:
|
|
35
|
+
return (
|
|
36
|
+
"Parses a structured object representing a complete task plan, converts it into a "
|
|
37
|
+
"system-ready format, and loads it onto the team's shared task board. "
|
|
38
|
+
"This action resets the task board with the new plan. Upon success, it returns "
|
|
39
|
+
"the initial status of the newly loaded task board. "
|
|
40
|
+
"This tool should typically only be used by the team coordinator."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def get_argument_schema(cls) -> Optional[ParameterSchema]:
|
|
45
|
+
schema = ParameterSchema()
|
|
46
|
+
|
|
47
|
+
# CORRECTED IMPLEMENTATION:
|
|
48
|
+
# A direct, standard call to model_json_schema(). This generates a valid
|
|
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()
|
|
52
|
+
|
|
53
|
+
schema.add_parameter(ParameterDefinition(
|
|
54
|
+
name="plan",
|
|
55
|
+
param_type=ParameterType.OBJECT,
|
|
56
|
+
description=(
|
|
57
|
+
"A structured object representing a complete task plan. This object defines the overall goal "
|
|
58
|
+
"and a list of tasks with their assignees, descriptions, and dependencies. "
|
|
59
|
+
"Each task must have a unique name within the plan."
|
|
60
|
+
),
|
|
61
|
+
required=True,
|
|
62
|
+
object_schema=object_json_schema
|
|
63
|
+
))
|
|
64
|
+
return schema
|
|
65
|
+
|
|
66
|
+
async def _execute(self, context: 'AgentContext', plan: Dict[str, Any]) -> str:
|
|
67
|
+
"""
|
|
68
|
+
Executes the tool by validating the plan object, using a converter to create a TaskPlan,
|
|
69
|
+
and loading it onto the task board.
|
|
70
|
+
"""
|
|
71
|
+
logger.info(f"Agent '{context.agent_id}' is executing PublishTaskPlan.")
|
|
72
|
+
|
|
73
|
+
team_context: Optional['AgentTeamContext'] = context.custom_data.get("team_context")
|
|
74
|
+
if not team_context:
|
|
75
|
+
error_msg = "Error: Team context is not available. Cannot access the task board."
|
|
76
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}")
|
|
77
|
+
return error_msg
|
|
78
|
+
|
|
79
|
+
task_board = getattr(team_context.state, 'task_board', None)
|
|
80
|
+
if not task_board:
|
|
81
|
+
error_msg = "Error: Task board has not been initialized for this team."
|
|
82
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}")
|
|
83
|
+
return error_msg
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
# Step 1: The input is now a dictionary, so we can directly validate it.
|
|
87
|
+
plan_definition_schema = TaskPlanDefinitionSchema(**plan)
|
|
88
|
+
|
|
89
|
+
# Step 2: Use the dedicated converter to create the internal TaskPlan object.
|
|
90
|
+
final_plan = TaskPlanConverter.from_schema(plan_definition_schema)
|
|
91
|
+
|
|
92
|
+
except (ValidationError, ValueError) as e:
|
|
93
|
+
error_msg = f"Invalid or inconsistent task plan provided: {e}"
|
|
94
|
+
logger.warning(f"Agent '{context.agent_id}' provided an invalid plan for PublishTaskPlan: {error_msg}")
|
|
95
|
+
return f"Error: {error_msg}"
|
|
96
|
+
except Exception as e:
|
|
97
|
+
error_msg = f"An unexpected error occurred during plan parsing or conversion: {e}"
|
|
98
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}", exc_info=True)
|
|
99
|
+
return f"Error: {error_msg}"
|
|
100
|
+
|
|
101
|
+
if task_board.load_task_plan(final_plan):
|
|
102
|
+
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
|
+
status_report_schema = TaskBoardConverter.to_schema(task_board)
|
|
105
|
+
if status_report_schema:
|
|
106
|
+
return status_report_schema.model_dump_json(indent=2)
|
|
107
|
+
else:
|
|
108
|
+
# This is a fallback case, shouldn't happen right after a successful load.
|
|
109
|
+
return "Task plan published successfully, but could not generate status report."
|
|
110
|
+
else:
|
|
111
|
+
error_msg = "Failed to load task plan onto the board. This can happen if the board implementation rejects the plan."
|
|
112
|
+
logger.error(f"Agent '{context.agent_id}': {error_msg}")
|
|
113
|
+
return f"Error: {error_msg}"
|