autobyteus 1.0.6__py3-none-any.whl → 1.1.1__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.
Files changed (270) hide show
  1. autobyteus/agent/agent.py +97 -222
  2. autobyteus/agent/bootstrap_steps/__init__.py +19 -0
  3. autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +88 -0
  4. autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +57 -0
  5. autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +38 -0
  6. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +93 -0
  7. autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +47 -0
  8. autobyteus/agent/context/__init__.py +13 -0
  9. autobyteus/agent/context/agent_config.py +84 -0
  10. autobyteus/agent/context/agent_context.py +129 -0
  11. autobyteus/agent/context/agent_phase_manager.py +264 -0
  12. autobyteus/agent/context/agent_runtime_state.py +89 -0
  13. autobyteus/agent/context/phases.py +49 -0
  14. autobyteus/agent/events/__init__.py +52 -0
  15. autobyteus/agent/events/agent_events.py +110 -0
  16. autobyteus/agent/events/agent_input_event_queue_manager.py +174 -0
  17. autobyteus/agent/events/notifiers.py +122 -0
  18. autobyteus/agent/events/worker_event_dispatcher.py +118 -0
  19. autobyteus/agent/factory/__init__.py +9 -0
  20. autobyteus/agent/factory/agent_factory.py +145 -0
  21. autobyteus/agent/group/agent_group.py +164 -0
  22. autobyteus/agent/group/agent_group_context.py +81 -0
  23. autobyteus/agent/handlers/__init__.py +36 -0
  24. autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +148 -0
  25. autobyteus/agent/handlers/base_event_handler.py +36 -0
  26. autobyteus/agent/handlers/event_handler_registry.py +76 -0
  27. autobyteus/agent/handlers/generic_event_handler.py +46 -0
  28. autobyteus/agent/handlers/inter_agent_message_event_handler.py +76 -0
  29. autobyteus/agent/handlers/lifecycle_event_logger.py +64 -0
  30. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +138 -0
  31. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +140 -0
  32. autobyteus/agent/handlers/tool_execution_approval_event_handler.py +85 -0
  33. autobyteus/agent/handlers/tool_invocation_request_event_handler.py +211 -0
  34. autobyteus/agent/handlers/tool_result_event_handler.py +101 -0
  35. autobyteus/agent/handlers/user_input_message_event_handler.py +77 -0
  36. autobyteus/agent/hooks/__init__.py +16 -0
  37. autobyteus/agent/hooks/base_phase_hook.py +61 -0
  38. autobyteus/agent/hooks/hook_definition.py +36 -0
  39. autobyteus/agent/hooks/hook_meta.py +37 -0
  40. autobyteus/agent/hooks/hook_registry.py +118 -0
  41. autobyteus/agent/input_processor/__init__.py +18 -0
  42. autobyteus/agent/input_processor/base_user_input_processor.py +54 -0
  43. autobyteus/agent/input_processor/content_prefixing_input_processor.py +41 -0
  44. autobyteus/agent/input_processor/metadata_appending_input_processor.py +34 -0
  45. autobyteus/agent/input_processor/passthrough_input_processor.py +33 -0
  46. autobyteus/agent/input_processor/processor_definition.py +42 -0
  47. autobyteus/agent/input_processor/processor_meta.py +46 -0
  48. autobyteus/agent/input_processor/processor_registry.py +117 -0
  49. autobyteus/agent/llm_response_processor/__init__.py +16 -0
  50. autobyteus/agent/llm_response_processor/base_processor.py +53 -0
  51. autobyteus/agent/llm_response_processor/processor_definition.py +36 -0
  52. autobyteus/agent/llm_response_processor/processor_meta.py +37 -0
  53. autobyteus/agent/llm_response_processor/processor_registry.py +113 -0
  54. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +54 -0
  55. autobyteus/agent/message/__init__.py +20 -0
  56. autobyteus/agent/message/agent_input_user_message.py +96 -0
  57. autobyteus/agent/message/context_file.py +82 -0
  58. autobyteus/agent/message/context_file_type.py +63 -0
  59. autobyteus/agent/message/{message.py → inter_agent_message.py} +12 -12
  60. autobyteus/agent/message/{message_types.py → inter_agent_message_type.py} +8 -6
  61. autobyteus/agent/message/send_message_to.py +142 -36
  62. autobyteus/agent/phases/__init__.py +18 -0
  63. autobyteus/agent/phases/discover.py +52 -0
  64. autobyteus/agent/phases/manager.py +265 -0
  65. autobyteus/agent/phases/phase_enum.py +49 -0
  66. autobyteus/agent/phases/transition_decorator.py +40 -0
  67. autobyteus/agent/phases/transition_info.py +33 -0
  68. autobyteus/agent/remote_agent.py +244 -0
  69. autobyteus/agent/runtime/__init__.py +15 -0
  70. autobyteus/agent/runtime/agent_runtime.py +137 -0
  71. autobyteus/agent/runtime/agent_thread_pool_manager.py +88 -0
  72. autobyteus/agent/runtime/agent_worker.py +200 -0
  73. autobyteus/agent/streaming/__init__.py +15 -0
  74. autobyteus/agent/streaming/agent_event_stream.py +173 -0
  75. autobyteus/agent/streaming/queue_streamer.py +58 -0
  76. autobyteus/agent/streaming/stream_event_payloads.py +167 -0
  77. autobyteus/agent/streaming/stream_events.py +126 -0
  78. autobyteus/agent/system_prompt_processor/__init__.py +14 -0
  79. autobyteus/agent/system_prompt_processor/base_processor.py +48 -0
  80. autobyteus/agent/system_prompt_processor/processor_definition.py +40 -0
  81. autobyteus/agent/system_prompt_processor/processor_meta.py +47 -0
  82. autobyteus/agent/system_prompt_processor/processor_registry.py +119 -0
  83. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +79 -0
  84. autobyteus/agent/tool_invocation.py +54 -5
  85. autobyteus/agent/utils/__init__.py +9 -0
  86. autobyteus/agent/utils/wait_for_idle.py +59 -0
  87. autobyteus/agent/workflow/__init__.py +11 -0
  88. autobyteus/agent/workflow/agentic_workflow.py +89 -0
  89. autobyteus/agent/workflow/base_agentic_workflow.py +98 -0
  90. autobyteus/agent/workspace/__init__.py +11 -0
  91. autobyteus/agent/workspace/base_workspace.py +77 -0
  92. autobyteus/agent/workspace/workspace_config.py +160 -0
  93. autobyteus/agent/workspace/workspace_definition.py +36 -0
  94. autobyteus/agent/workspace/workspace_meta.py +37 -0
  95. autobyteus/agent/workspace/workspace_registry.py +72 -0
  96. autobyteus/cli/__init__.py +11 -0
  97. autobyteus/cli/agent_cli.py +117 -0
  98. autobyteus/cli/cli_display.py +205 -0
  99. autobyteus/events/event_emitter.py +33 -56
  100. autobyteus/events/event_manager.py +134 -66
  101. autobyteus/events/event_types.py +43 -14
  102. autobyteus/llm/api/autobyteus_llm.py +13 -25
  103. autobyteus/llm/api/bedrock_llm.py +9 -3
  104. autobyteus/llm/api/claude_llm.py +10 -5
  105. autobyteus/llm/api/deepseek_llm.py +55 -93
  106. autobyteus/llm/api/gemini_llm.py +10 -4
  107. autobyteus/llm/api/grok_llm.py +55 -79
  108. autobyteus/llm/api/groq_llm.py +10 -5
  109. autobyteus/llm/api/mistral_llm.py +13 -8
  110. autobyteus/llm/api/nvidia_llm.py +9 -4
  111. autobyteus/llm/api/ollama_llm.py +58 -50
  112. autobyteus/llm/api/openai_llm.py +20 -14
  113. autobyteus/llm/base_llm.py +95 -34
  114. autobyteus/llm/extensions/base_extension.py +3 -4
  115. autobyteus/llm/extensions/token_usage_tracking_extension.py +13 -4
  116. autobyteus/llm/llm_factory.py +116 -53
  117. autobyteus/llm/models.py +92 -17
  118. autobyteus/llm/ollama_provider.py +6 -2
  119. autobyteus/llm/ollama_provider_resolver.py +44 -0
  120. autobyteus/llm/user_message.py +73 -0
  121. autobyteus/llm/utils/llm_config.py +124 -27
  122. autobyteus/llm/utils/response_types.py +3 -2
  123. autobyteus/llm/utils/token_usage.py +7 -4
  124. autobyteus/rpc/__init__.py +73 -0
  125. autobyteus/rpc/client/__init__.py +17 -0
  126. autobyteus/rpc/client/abstract_client_connection.py +124 -0
  127. autobyteus/rpc/client/client_connection_manager.py +153 -0
  128. autobyteus/rpc/client/sse_client_connection.py +306 -0
  129. autobyteus/rpc/client/stdio_client_connection.py +280 -0
  130. autobyteus/rpc/config/__init__.py +13 -0
  131. autobyteus/rpc/config/agent_server_config.py +153 -0
  132. autobyteus/rpc/config/agent_server_registry.py +152 -0
  133. autobyteus/rpc/hosting.py +244 -0
  134. autobyteus/rpc/protocol.py +244 -0
  135. autobyteus/rpc/server/__init__.py +20 -0
  136. autobyteus/rpc/server/agent_server_endpoint.py +181 -0
  137. autobyteus/rpc/server/base_method_handler.py +40 -0
  138. autobyteus/rpc/server/method_handlers.py +259 -0
  139. autobyteus/rpc/server/sse_server_handler.py +182 -0
  140. autobyteus/rpc/server/stdio_server_handler.py +151 -0
  141. autobyteus/rpc/server_main.py +198 -0
  142. autobyteus/rpc/transport_type.py +13 -0
  143. autobyteus/tools/__init__.py +77 -0
  144. autobyteus/tools/ask_user_input.py +34 -77
  145. autobyteus/tools/base_tool.py +73 -38
  146. autobyteus/tools/bash/__init__.py +2 -0
  147. autobyteus/tools/bash/bash_executor.py +42 -79
  148. autobyteus/tools/browser/__init__.py +2 -0
  149. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +50 -42
  150. autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +7 -4
  151. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +117 -125
  152. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +75 -22
  153. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +94 -28
  154. autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +10 -2
  155. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +18 -2
  156. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +10 -2
  157. autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +4 -3
  158. autobyteus/tools/browser/standalone/__init__.py +7 -0
  159. autobyteus/tools/browser/standalone/factory/google_search_factory.py +17 -2
  160. autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +17 -2
  161. autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +10 -2
  162. autobyteus/tools/browser/standalone/google_search_ui.py +104 -67
  163. autobyteus/tools/browser/standalone/navigate_to.py +52 -28
  164. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +94 -0
  165. autobyteus/tools/browser/standalone/webpage_image_downloader.py +146 -61
  166. autobyteus/tools/browser/standalone/webpage_reader.py +80 -61
  167. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +91 -45
  168. autobyteus/tools/factory/__init__.py +9 -0
  169. autobyteus/tools/factory/tool_factory.py +25 -4
  170. autobyteus/tools/file/file_reader.py +22 -51
  171. autobyteus/tools/file/file_writer.py +25 -56
  172. autobyteus/tools/functional_tool.py +249 -0
  173. autobyteus/tools/image_downloader.py +49 -71
  174. autobyteus/tools/mcp/__init__.py +47 -0
  175. autobyteus/tools/mcp/call_handlers/__init__.py +18 -0
  176. autobyteus/tools/mcp/call_handlers/base_handler.py +40 -0
  177. autobyteus/tools/mcp/call_handlers/sse_handler.py +22 -0
  178. autobyteus/tools/mcp/call_handlers/stdio_handler.py +76 -0
  179. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +55 -0
  180. autobyteus/tools/mcp/config_service.py +237 -0
  181. autobyteus/tools/mcp/factory.py +70 -0
  182. autobyteus/tools/mcp/registrar.py +323 -0
  183. autobyteus/tools/mcp/schema_mapper.py +131 -0
  184. autobyteus/tools/mcp/tool.py +101 -0
  185. autobyteus/tools/mcp/types.py +98 -0
  186. autobyteus/tools/parameter_schema.py +268 -0
  187. autobyteus/tools/pdf_downloader.py +78 -79
  188. autobyteus/tools/registry/__init__.py +0 -2
  189. autobyteus/tools/registry/tool_definition.py +113 -34
  190. autobyteus/tools/registry/tool_registry.py +64 -22
  191. autobyteus/tools/timer.py +150 -102
  192. autobyteus/tools/tool_category.py +11 -0
  193. autobyteus/tools/tool_config.py +117 -0
  194. autobyteus/tools/tool_meta.py +50 -26
  195. autobyteus/tools/tool_state.py +20 -0
  196. autobyteus/tools/usage/__init__.py +6 -0
  197. autobyteus/tools/usage/formatters/__init__.py +31 -0
  198. autobyteus/tools/usage/formatters/anthropic_json_example_formatter.py +18 -0
  199. autobyteus/tools/usage/formatters/anthropic_json_schema_formatter.py +25 -0
  200. autobyteus/tools/usage/formatters/base_formatter.py +42 -0
  201. autobyteus/tools/usage/formatters/default_json_example_formatter.py +42 -0
  202. autobyteus/tools/usage/formatters/default_json_schema_formatter.py +28 -0
  203. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +55 -0
  204. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +46 -0
  205. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +34 -0
  206. autobyteus/tools/usage/formatters/gemini_json_schema_formatter.py +25 -0
  207. autobyteus/tools/usage/formatters/google_json_example_formatter.py +34 -0
  208. autobyteus/tools/usage/formatters/google_json_schema_formatter.py +25 -0
  209. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +49 -0
  210. autobyteus/tools/usage/formatters/openai_json_schema_formatter.py +28 -0
  211. autobyteus/tools/usage/parsers/__init__.py +22 -0
  212. autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +10 -0
  213. autobyteus/tools/usage/parsers/base_parser.py +41 -0
  214. autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +106 -0
  215. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +136 -0
  216. autobyteus/tools/usage/parsers/exceptions.py +13 -0
  217. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +66 -0
  218. autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +196 -0
  219. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +67 -0
  220. autobyteus/tools/usage/providers/__init__.py +22 -0
  221. autobyteus/tools/usage/providers/json_example_provider.py +32 -0
  222. autobyteus/tools/usage/providers/json_schema_provider.py +35 -0
  223. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +28 -0
  224. autobyteus/tools/usage/providers/tool_manifest_provider.py +68 -0
  225. autobyteus/tools/usage/providers/xml_example_provider.py +28 -0
  226. autobyteus/tools/usage/providers/xml_schema_provider.py +29 -0
  227. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +26 -0
  228. autobyteus/tools/usage/registries/__init__.py +20 -0
  229. autobyteus/tools/usage/registries/json_example_formatter_registry.py +51 -0
  230. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +51 -0
  231. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +42 -0
  232. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +30 -0
  233. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +33 -0
  234. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +30 -0
  235. {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.dist-info}/METADATA +23 -5
  236. autobyteus-1.1.1.dist-info/RECORD +296 -0
  237. {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.dist-info}/WHEEL +1 -1
  238. autobyteus/agent/async_agent.py +0 -175
  239. autobyteus/agent/async_group_aware_agent.py +0 -136
  240. autobyteus/agent/group/async_group_aware_agent.py +0 -122
  241. autobyteus/agent/group/coordinator_agent.py +0 -36
  242. autobyteus/agent/group/group_aware_agent.py +0 -121
  243. autobyteus/agent/orchestrator/__init__.py +0 -0
  244. autobyteus/agent/orchestrator/base_agent_orchestrator.py +0 -82
  245. autobyteus/agent/orchestrator/multi_replica_agent_orchestrator.py +0 -72
  246. autobyteus/agent/orchestrator/single_replica_agent_orchestrator.py +0 -43
  247. autobyteus/agent/response_parser/__init__.py +0 -0
  248. autobyteus/agent/response_parser/tool_usage_command_parser.py +0 -100
  249. autobyteus/agent/status.py +0 -12
  250. autobyteus/conversation/__init__.py +0 -0
  251. autobyteus/conversation/conversation.py +0 -54
  252. autobyteus/conversation/user_message.py +0 -59
  253. autobyteus/events/decorators.py +0 -29
  254. autobyteus/prompt/prompt_version_manager.py +0 -58
  255. autobyteus/prompt/storage/__init__.py +0 -0
  256. autobyteus/prompt/storage/prompt_version_model.py +0 -29
  257. autobyteus/prompt/storage/prompt_version_repository.py +0 -83
  258. autobyteus/tools/bash/factory/__init__.py +0 -0
  259. autobyteus/tools/bash/factory/bash_executor_factory.py +0 -6
  260. autobyteus/tools/factory/ask_user_input_factory.py +0 -6
  261. autobyteus/tools/factory/image_downloader_factory.py +0 -9
  262. autobyteus/tools/factory/pdf_downloader_factory.py +0 -9
  263. autobyteus/tools/factory/webpage_image_downloader_factory.py +0 -6
  264. autobyteus/tools/file/factory/__init__.py +0 -0
  265. autobyteus/tools/file/factory/file_reader_factory.py +0 -6
  266. autobyteus/tools/file/factory/file_writer_factory.py +0 -6
  267. autobyteus/tools/web_page_pdf_generator.py +0 -90
  268. autobyteus-1.0.6.dist-info/RECORD +0 -157
  269. {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.dist-info}/licenses/LICENSE +0 -0
  270. {autobyteus-1.0.6.dist-info → autobyteus-1.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,47 @@
1
+ # file: autobyteus/autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py
2
+ import logging
3
+ from typing import TYPE_CHECKING
4
+
5
+ from .base_bootstrap_step import BaseBootstrapStep
6
+
7
+ if TYPE_CHECKING:
8
+ from autobyteus.agent.context import AgentContext
9
+ from autobyteus.agent.phases import AgentPhaseManager
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ class WorkspaceContextInitializationStep(BaseBootstrapStep):
14
+ """
15
+ Bootstrap step for injecting the AgentContext into the agent's workspace instance.
16
+ """
17
+
18
+ def __init__(self):
19
+ logger.debug("WorkspaceContextInitializationStep initialized.")
20
+
21
+ async def execute(self,
22
+ context: 'AgentContext',
23
+ phase_manager: 'AgentPhaseManager') -> bool:
24
+ agent_id = context.agent_id
25
+ logger.info(f"Agent '{agent_id}': Executing WorkspaceContextInitializationStep.")
26
+
27
+ workspace = context.workspace
28
+
29
+ if not workspace:
30
+ logger.debug(f"Agent '{agent_id}': No workspace configured. Skipping context injection.")
31
+ return True
32
+
33
+ try:
34
+ if hasattr(workspace, 'set_context') and callable(getattr(workspace, 'set_context')):
35
+ workspace.set_context(context)
36
+ logger.info(f"Agent '{agent_id}': AgentContext successfully injected into workspace instance of type '{type(workspace).__name__}'.")
37
+ else:
38
+ logger.warning(f"Agent '{agent_id}': Configured workspace of type '{type(workspace).__name__}' does not have a 'set_context' method. "
39
+ "Workspace will not have access to the agent's context.")
40
+
41
+ return True
42
+ except Exception as e:
43
+ error_message = f"Agent '{agent_id}': Critical failure during WorkspaceContextInitializationStep: {e}"
44
+ logger.error(error_message, exc_info=True)
45
+ # No easy way to enqueue an error event here if queues aren't even initialized yet.
46
+ # The failure of a bootstrap step is handled by the bootstrapper, which will log and set error phase.
47
+ return False
@@ -0,0 +1,13 @@
1
+ # file: autobyteus/autobyteus/agent/context/__init__.py
2
+ """
3
+ Components related to the agent's runtime context, state, config, and status management.
4
+ """
5
+ from .agent_config import AgentConfig
6
+ from .agent_runtime_state import AgentRuntimeState
7
+ from .agent_context import AgentContext
8
+
9
+ __all__ = [
10
+ "AgentContext",
11
+ "AgentConfig",
12
+ "AgentRuntimeState",
13
+ ]
@@ -0,0 +1,84 @@
1
+ # file: autobyteus/autobyteus/agent/context/agent_config.py
2
+ import logging
3
+ from typing import List, Optional, Union, Tuple, TYPE_CHECKING, Dict, Any
4
+
5
+ # Correctly import the new master processor and the base class
6
+ from autobyteus.agent.system_prompt_processor import ToolManifestInjectorProcessor, BaseSystemPromptProcessor
7
+ from autobyteus.agent.llm_response_processor import ProviderAwareToolUsageProcessor, BaseLLMResponseProcessor
8
+
9
+
10
+ if TYPE_CHECKING:
11
+ from autobyteus.tools.base_tool import BaseTool
12
+ from autobyteus.agent.input_processor import BaseAgentUserInputMessageProcessor
13
+ from autobyteus.llm.base_llm import BaseLLM
14
+ from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
15
+ from autobyteus.agent.hooks.base_phase_hook import BasePhaseHook
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class AgentConfig:
20
+ """
21
+ Represents the complete, static configuration for an agent instance.
22
+ This is the single source of truth for an agent's definition, including
23
+ its identity, capabilities, and default behaviors.
24
+ """
25
+ # Use the new ProviderAwareToolUsageProcessor as the default
26
+ DEFAULT_LLM_RESPONSE_PROCESSORS = [ProviderAwareToolUsageProcessor()]
27
+ # Use the new, single, unified processor as the default
28
+ DEFAULT_SYSTEM_PROMPT_PROCESSORS = [ToolManifestInjectorProcessor()]
29
+
30
+ def __init__(self,
31
+ name: str,
32
+ role: str,
33
+ description: str,
34
+ llm_instance: 'BaseLLM',
35
+ system_prompt: str,
36
+ tools: List['BaseTool'],
37
+ auto_execute_tools: bool = True,
38
+ use_xml_tool_format: bool = True,
39
+ input_processors: Optional[List['BaseAgentUserInputMessageProcessor']] = None,
40
+ llm_response_processors: Optional[List['BaseLLMResponseProcessor']] = None,
41
+ system_prompt_processors: Optional[List['BaseSystemPromptProcessor']] = None,
42
+ workspace: Optional['BaseAgentWorkspace'] = None,
43
+ phase_hooks: Optional[List['BasePhaseHook']] = None,
44
+ initial_custom_data: Optional[Dict[str, Any]] = None):
45
+ """
46
+ Initializes the AgentConfig.
47
+
48
+ Args:
49
+ name: The agent's name.
50
+ role: The agent's role.
51
+ description: A description of the agent.
52
+ llm_instance: A pre-initialized LLM instance (subclass of BaseLLM).
53
+ The user is responsible for creating and configuring this instance.
54
+ system_prompt: The base system prompt.
55
+ tools: A list of pre-initialized tool instances (subclasses of BaseTool).
56
+ auto_execute_tools: If True, the agent will execute tools without approval.
57
+ use_xml_tool_format: Whether to use XML for tool descriptions and examples.
58
+ input_processors: A list of input processor instances.
59
+ llm_response_processors: A list of LLM response processor instances.
60
+ system_prompt_processors: A list of system prompt processor instances.
61
+ workspace: An optional pre-initialized workspace instance for the agent.
62
+ phase_hooks: An optional list of phase transition hook instances.
63
+ initial_custom_data: An optional dictionary of data to pre-populate
64
+ the agent's runtime state `custom_data`.
65
+ """
66
+ self.name = name
67
+ self.role = role
68
+ self.description = description
69
+ self.llm_instance = llm_instance
70
+ self.system_prompt = system_prompt
71
+ self.tools = tools
72
+ self.workspace = workspace
73
+ self.auto_execute_tools = auto_execute_tools
74
+ self.use_xml_tool_format = use_xml_tool_format
75
+ self.input_processors = input_processors or []
76
+ self.llm_response_processors = llm_response_processors if llm_response_processors is not None else list(self.DEFAULT_LLM_RESPONSE_PROCESSORS)
77
+ self.system_prompt_processors = system_prompt_processors if system_prompt_processors is not None else list(self.DEFAULT_SYSTEM_PROMPT_PROCESSORS)
78
+ self.phase_hooks = phase_hooks or []
79
+ self.initial_custom_data = initial_custom_data
80
+
81
+ logger.debug(f"AgentConfig created for name '{self.name}', role '{self.role}'.")
82
+
83
+ def __repr__(self) -> str:
84
+ return (f"AgentConfig(name='{self.name}', role='{self.role}', llm_instance='{self.llm_instance.__class__.__name__}', workspace_configured={self.workspace is not None})")
@@ -0,0 +1,129 @@
1
+ # file: autobyteus/autobyteus/agent/context/agent_context.py
2
+ import logging
3
+ from typing import TYPE_CHECKING, List, Dict, Any, Optional
4
+
5
+ from autobyteus.agent.phases import AgentOperationalPhase
6
+
7
+ if TYPE_CHECKING:
8
+ from .agent_config import AgentConfig
9
+ from .agent_runtime_state import AgentRuntimeState
10
+ from autobyteus.llm.base_llm import BaseLLM
11
+ from autobyteus.tools.base_tool import BaseTool
12
+ from autobyteus.agent.events.agent_input_event_queue_manager import AgentInputEventQueueManager
13
+ from autobyteus.agent.tool_invocation import ToolInvocation
14
+ # LLMConfig no longer needed here
15
+ from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
16
+ from autobyteus.agent.phases import AgentPhaseManager
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+ class AgentContext:
21
+ """
22
+ Represents the complete operational context for a single agent instance.
23
+ """
24
+ def __init__(self, agent_id: str, config: 'AgentConfig', state: 'AgentRuntimeState'):
25
+ from .agent_config import AgentConfig as AgentConfigClass
26
+ from .agent_runtime_state import AgentRuntimeState as AgentRuntimeStateClass
27
+
28
+ if not agent_id or not isinstance(agent_id, str):
29
+ raise ValueError("AgentContext requires a non-empty string 'agent_id'.")
30
+ if not isinstance(config, AgentConfigClass):
31
+ raise TypeError(f"AgentContext 'config' must be an AgentConfig instance. Got {type(config)}")
32
+ if not isinstance(state, AgentRuntimeStateClass):
33
+ raise TypeError(f"AgentContext 'state' must be an AgentRuntimeState instance. Got {type(state)}")
34
+
35
+ if agent_id != state.agent_id: # pragma: no cover
36
+ logger.warning(f"AgentContext created with mismatched agent_id ('{agent_id}') and state's ID ('{state.agent_id}'). Using context's ID for logging.")
37
+
38
+ self.agent_id: str = agent_id
39
+ self.config: 'AgentConfig' = config
40
+ self.state: 'AgentRuntimeState' = state
41
+
42
+ logger.info(f"AgentContext composed for agent_id '{self.agent_id}'. Config and State linked.")
43
+
44
+ @property
45
+ def tool_instances(self) -> Dict[str, 'BaseTool']:
46
+ return self.state.tool_instances if self.state.tool_instances is not None else {}
47
+
48
+ @property
49
+ def auto_execute_tools(self) -> bool:
50
+ return self.config.auto_execute_tools
51
+
52
+ @property
53
+ def llm_instance(self) -> Optional['BaseLLM']:
54
+ return self.state.llm_instance
55
+
56
+ @llm_instance.setter
57
+ def llm_instance(self, value: Optional['BaseLLM']):
58
+ self.state.llm_instance = value
59
+
60
+ @property
61
+ def input_event_queues(self) -> 'AgentInputEventQueueManager':
62
+ if self.state.input_event_queues is None:
63
+ logger.critical(f"AgentContext for '{self.agent_id}': Attempted to access 'input_event_queues' before they were initialized by AgentWorker.")
64
+ raise RuntimeError(f"Agent '{self.agent_id}': Input event queues have not been initialized. This typically occurs during agent bootstrapping.")
65
+ return self.state.input_event_queues
66
+
67
+ @property
68
+ def current_phase(self) -> 'AgentOperationalPhase':
69
+ return self.state.current_phase
70
+
71
+ @current_phase.setter
72
+ def current_phase(self, value: 'AgentOperationalPhase'):
73
+ if not isinstance(value, AgentOperationalPhase): # pragma: no cover
74
+ raise TypeError(f"current_phase must be an AgentOperationalPhase instance. Got {type(value)}")
75
+ self.state.current_phase = value
76
+
77
+ @property
78
+ def phase_manager(self) -> Optional['AgentPhaseManager']:
79
+ return self.state.phase_manager_ref
80
+
81
+ @property
82
+ def conversation_history(self) -> List[Dict[str, Any]]:
83
+ return self.state.conversation_history
84
+
85
+ @property
86
+ def pending_tool_approvals(self) -> Dict[str, 'ToolInvocation']:
87
+ return self.state.pending_tool_approvals
88
+
89
+ @property
90
+ def custom_data(self) -> Dict[str, Any]:
91
+ return self.state.custom_data
92
+
93
+ @property
94
+ def workspace(self) -> Optional['BaseAgentWorkspace']:
95
+ return self.state.workspace
96
+
97
+ @property
98
+ def processed_system_prompt(self) -> Optional[str]:
99
+ return self.state.processed_system_prompt
100
+
101
+ @processed_system_prompt.setter
102
+ def processed_system_prompt(self, value: Optional[str]):
103
+ self.state.processed_system_prompt = value
104
+
105
+ # final_llm_config_for_creation property removed
106
+
107
+ def add_message_to_history(self, message: Dict[str, Any]) -> None:
108
+ self.state.add_message_to_history(message)
109
+
110
+ def get_tool(self, tool_name: str) -> Optional['BaseTool']:
111
+ tool = self.tool_instances.get(tool_name)
112
+ if not tool: # pragma: no cover
113
+ logger.warning(f"Tool '{tool_name}' not found in AgentContext.state.tool_instances for agent '{self.agent_id}'. "
114
+ f"Available tools: {list(self.tool_instances.keys())}")
115
+ return tool
116
+
117
+ def store_pending_tool_invocation(self, invocation: 'ToolInvocation') -> None:
118
+ self.state.store_pending_tool_invocation(invocation)
119
+
120
+ def retrieve_pending_tool_invocation(self, invocation_id: str) -> Optional['ToolInvocation']:
121
+ return self.state.retrieve_pending_tool_invocation(invocation_id)
122
+
123
+ def __repr__(self) -> str:
124
+ input_q_status = "Initialized" if self.state.input_event_queues is not None else "Pending Init"
125
+ return (f"AgentContext(agent_id='{self.agent_id}', "
126
+ f"current_phase='{self.state.current_phase.value}', "
127
+ f"llm_initialized={self.state.llm_instance is not None}, "
128
+ f"tools_initialized={self.state.tool_instances is not None}, "
129
+ f"input_queues_status='{input_q_status}')")
@@ -0,0 +1,264 @@
1
+ # file: autobyteus/autobyteus/agent/context/agent_phase_manager.py
2
+ import asyncio
3
+ import logging
4
+ from typing import TYPE_CHECKING, Optional, Dict, Any
5
+
6
+ from autobyteus.agent.phases import AgentOperationalPhase, phase_transition
7
+
8
+ if TYPE_CHECKING:
9
+ from autobyteus.agent.context.agent_context import AgentContext
10
+ from autobyteus.agent.tool_invocation import ToolInvocation
11
+ from autobyteus.agent.events.notifiers import AgentExternalEventNotifier
12
+
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ class AgentPhaseManager:
17
+ """
18
+ Manages the operational phase of an agent, uses an AgentExternalEventNotifier
19
+ to signal phase changes externally, and executes phase transition hooks.
20
+ """
21
+ def __init__(self, context: 'AgentContext', notifier: 'AgentExternalEventNotifier'):
22
+ self.context: 'AgentContext' = context
23
+ self.notifier: 'AgentExternalEventNotifier' = notifier
24
+
25
+ self.context.current_phase = AgentOperationalPhase.UNINITIALIZED
26
+
27
+ logger.debug(f"AgentPhaseManager initialized for agent_id '{self.context.agent_id}'. "
28
+ f"Initial phase: {self.context.current_phase.value}. Uses provided notifier.")
29
+
30
+ async def _execute_hooks(self, old_phase: AgentOperationalPhase, new_phase: AgentOperationalPhase):
31
+ """Asynchronously executes hooks that match the given phase transition."""
32
+ hooks_to_run = [
33
+ hook for hook in self.context.config.phase_hooks
34
+ if hook.source_phase == old_phase and hook.target_phase == new_phase
35
+ ]
36
+
37
+ if not hooks_to_run:
38
+ return
39
+
40
+ hook_names = [hook.__class__.__name__ for hook in hooks_to_run]
41
+ logger.info(f"Agent '{self.context.agent_id}': Executing {len(hooks_to_run)} hooks for transition "
42
+ f"'{old_phase.value}' -> '{new_phase.value}': {hook_names}")
43
+
44
+ for hook in hooks_to_run:
45
+ try:
46
+ await hook.execute(self.context)
47
+ logger.debug(f"Agent '{self.context.agent_id}': Hook '{hook.__class__.__name__}' executed successfully.")
48
+ except Exception as e:
49
+ logger.error(f"Agent '{self.context.agent_id}': Error executing phase transition hook "
50
+ f"'{hook.__class__.__name__}' for '{old_phase.value}' -> '{new_phase.value}': {e}",
51
+ exc_info=True)
52
+ # We log the error but do not halt the agent's phase transition.
53
+
54
+ async def _transition_phase(self, new_phase: AgentOperationalPhase,
55
+ notify_method_name: str,
56
+ additional_data: Optional[Dict[str, Any]] = None):
57
+ """
58
+ Private async helper to change the agent's phase, execute hooks, and then
59
+ call the appropriate notifier method. Hooks are now awaited.
60
+ """
61
+ if not isinstance(new_phase, AgentOperationalPhase):
62
+ logger.error(f"AgentPhaseManager for '{self.context.agent_id}' received invalid type for new_phase: {type(new_phase)}. Must be AgentOperationalPhase.")
63
+ return
64
+
65
+ old_phase = self.context.current_phase
66
+
67
+ if old_phase == new_phase:
68
+ logger.debug(f"AgentPhaseManager for '{self.context.agent_id}': already in phase {new_phase.value}. No transition.")
69
+ return
70
+
71
+ logger.info(f"Agent '{self.context.agent_id}' phase transitioning from {old_phase.value} to {new_phase.value}.")
72
+ self.context.current_phase = new_phase
73
+
74
+ # Execute and wait for hooks to complete *before* notifying externally.
75
+ await self._execute_hooks(old_phase, new_phase)
76
+
77
+ notifier_method = getattr(self.notifier, notify_method_name, None)
78
+ if notifier_method and callable(notifier_method):
79
+ notify_args = {"old_phase": old_phase}
80
+ if additional_data:
81
+ notify_args.update(additional_data)
82
+
83
+ notifier_method(**notify_args)
84
+ else:
85
+ logger.error(f"AgentPhaseManager for '{self.context.agent_id}': Notifier method '{notify_method_name}' not found or not callable on {type(self.notifier).__name__}.")
86
+
87
+ @phase_transition(
88
+ source_phases=[AgentOperationalPhase.SHUTDOWN_COMPLETE, AgentOperationalPhase.ERROR],
89
+ target_phase=AgentOperationalPhase.UNINITIALIZED,
90
+ description="Triggered when the agent runtime is started or restarted after being in a terminal state."
91
+ )
92
+ async def notify_runtime_starting_and_uninitialized(self) -> None:
93
+ if self.context.current_phase == AgentOperationalPhase.UNINITIALIZED:
94
+ await self._transition_phase(AgentOperationalPhase.UNINITIALIZED, "notify_phase_uninitialized_entered")
95
+ elif self.context.current_phase.is_terminal():
96
+ await self._transition_phase(AgentOperationalPhase.UNINITIALIZED, "notify_phase_uninitialized_entered")
97
+ else:
98
+ logger.warning(f"Agent '{self.context.agent_id}' notify_runtime_starting_and_uninitialized called in unexpected phase: {self.context.current_phase.value}")
99
+
100
+ @phase_transition(
101
+ source_phases=[AgentOperationalPhase.UNINITIALIZED],
102
+ target_phase=AgentOperationalPhase.BOOTSTRAPPING,
103
+ description="Occurs when the agent's internal bootstrapping process begins."
104
+ )
105
+ async def notify_bootstrapping_started(self) -> None:
106
+ await self._transition_phase(AgentOperationalPhase.BOOTSTRAPPING, "notify_phase_bootstrapping_started")
107
+
108
+ @phase_transition(
109
+ source_phases=[AgentOperationalPhase.BOOTSTRAPPING],
110
+ target_phase=AgentOperationalPhase.IDLE,
111
+ description="Occurs when the agent successfully completes bootstrapping and is ready for input."
112
+ )
113
+ async def notify_initialization_complete(self) -> None:
114
+ if self.context.current_phase.is_initializing() or self.context.current_phase == AgentOperationalPhase.UNINITIALIZED:
115
+ # This will now be a BOOTSTRAPPING -> IDLE transition
116
+ await self._transition_phase(AgentOperationalPhase.IDLE, "notify_phase_idle_entered")
117
+ else:
118
+ logger.warning(f"Agent '{self.context.agent_id}' notify_initialization_complete called in unexpected phase: {self.context.current_phase.value}")
119
+
120
+ @phase_transition(
121
+ source_phases=[
122
+ AgentOperationalPhase.IDLE, AgentOperationalPhase.ANALYZING_LLM_RESPONSE,
123
+ AgentOperationalPhase.PROCESSING_TOOL_RESULT, AgentOperationalPhase.EXECUTING_TOOL,
124
+ AgentOperationalPhase.TOOL_DENIED
125
+ ],
126
+ target_phase=AgentOperationalPhase.PROCESSING_USER_INPUT,
127
+ description="Fires when the agent begins processing a new user message or inter-agent message."
128
+ )
129
+ async def notify_processing_input_started(self, trigger_info: Optional[str] = None) -> None:
130
+ if self.context.current_phase in [AgentOperationalPhase.IDLE, AgentOperationalPhase.ANALYZING_LLM_RESPONSE, AgentOperationalPhase.PROCESSING_TOOL_RESULT, AgentOperationalPhase.EXECUTING_TOOL, AgentOperationalPhase.TOOL_DENIED]:
131
+ data = {"trigger_info": trigger_info} if trigger_info else {}
132
+ await self._transition_phase(AgentOperationalPhase.PROCESSING_USER_INPUT, "notify_phase_processing_user_input_started", additional_data=data)
133
+ elif self.context.current_phase == AgentOperationalPhase.PROCESSING_USER_INPUT:
134
+ logger.debug(f"Agent '{self.context.agent_id}' already in PROCESSING_USER_INPUT phase.")
135
+ else:
136
+ logger.warning(f"Agent '{self.context.agent_id}' notify_processing_input_started called in unexpected phase: {self.context.current_phase.value}")
137
+
138
+ @phase_transition(
139
+ source_phases=[AgentOperationalPhase.PROCESSING_USER_INPUT, AgentOperationalPhase.PROCESSING_TOOL_RESULT],
140
+ target_phase=AgentOperationalPhase.AWAITING_LLM_RESPONSE,
141
+ description="Occurs just before the agent makes a call to the LLM."
142
+ )
143
+ async def notify_awaiting_llm_response(self) -> None:
144
+ await self._transition_phase(AgentOperationalPhase.AWAITING_LLM_RESPONSE, "notify_phase_awaiting_llm_response_started")
145
+
146
+ @phase_transition(
147
+ source_phases=[AgentOperationalPhase.AWAITING_LLM_RESPONSE],
148
+ target_phase=AgentOperationalPhase.ANALYZING_LLM_RESPONSE,
149
+ description="Occurs after the agent has received a complete response from the LLM and begins to analyze it."
150
+ )
151
+ async def notify_analyzing_llm_response(self) -> None:
152
+ await self._transition_phase(AgentOperationalPhase.ANALYZING_LLM_RESPONSE, "notify_phase_analyzing_llm_response_started")
153
+
154
+ @phase_transition(
155
+ source_phases=[AgentOperationalPhase.ANALYZING_LLM_RESPONSE],
156
+ target_phase=AgentOperationalPhase.AWAITING_TOOL_APPROVAL,
157
+ description="Occurs if the agent proposes a tool use that requires manual user approval."
158
+ )
159
+ async def notify_tool_execution_pending_approval(self, tool_invocation: 'ToolInvocation') -> None:
160
+ await self._transition_phase(AgentOperationalPhase.AWAITING_TOOL_APPROVAL, "notify_phase_awaiting_tool_approval_started")
161
+
162
+ @phase_transition(
163
+ source_phases=[AgentOperationalPhase.AWAITING_TOOL_APPROVAL],
164
+ target_phase=AgentOperationalPhase.EXECUTING_TOOL,
165
+ description="Occurs after a pending tool use has been approved and is about to be executed."
166
+ )
167
+ async def notify_tool_execution_resumed_after_approval(self, approved: bool, tool_name: Optional[str]) -> None:
168
+ if approved and tool_name:
169
+ await self._transition_phase(AgentOperationalPhase.EXECUTING_TOOL, "notify_phase_executing_tool_started", additional_data={"tool_name": tool_name})
170
+ else:
171
+ logger.info(f"Agent '{self.context.agent_id}' tool execution denied for '{tool_name}'. Transitioning to allow LLM to process denial.")
172
+ await self.notify_tool_denied(tool_name)
173
+
174
+ @phase_transition(
175
+ source_phases=[AgentOperationalPhase.AWAITING_TOOL_APPROVAL],
176
+ target_phase=AgentOperationalPhase.TOOL_DENIED,
177
+ description="Occurs after a pending tool use has been denied by the user."
178
+ )
179
+ async def notify_tool_denied(self, tool_name: Optional[str]) -> None:
180
+ """Notifies that a tool execution has been denied."""
181
+ await self._transition_phase(
182
+ AgentOperationalPhase.TOOL_DENIED,
183
+ "notify_phase_tool_denied_started",
184
+ additional_data={"tool_name": tool_name, "denial_for_tool": tool_name}
185
+ )
186
+
187
+ @phase_transition(
188
+ source_phases=[AgentOperationalPhase.ANALYZING_LLM_RESPONSE],
189
+ target_phase=AgentOperationalPhase.EXECUTING_TOOL,
190
+ description="Occurs when an agent with auto-approval executes a tool."
191
+ )
192
+ async def notify_tool_execution_started(self, tool_name: str) -> None:
193
+ await self._transition_phase(AgentOperationalPhase.EXECUTING_TOOL, "notify_phase_executing_tool_started", additional_data={"tool_name": tool_name})
194
+
195
+ @phase_transition(
196
+ source_phases=[AgentOperationalPhase.EXECUTING_TOOL],
197
+ target_phase=AgentOperationalPhase.PROCESSING_TOOL_RESULT,
198
+ description="Fires after a tool has finished executing and the agent begins processing its result."
199
+ )
200
+ async def notify_processing_tool_result(self, tool_name: str) -> None:
201
+ await self._transition_phase(AgentOperationalPhase.PROCESSING_TOOL_RESULT, "notify_phase_processing_tool_result_started", additional_data={"tool_name": tool_name})
202
+
203
+ @phase_transition(
204
+ source_phases=[
205
+ AgentOperationalPhase.PROCESSING_USER_INPUT, AgentOperationalPhase.ANALYZING_LLM_RESPONSE,
206
+ AgentOperationalPhase.PROCESSING_TOOL_RESULT
207
+ ],
208
+ target_phase=AgentOperationalPhase.IDLE,
209
+ description="Occurs when an agent completes a processing cycle and is waiting for new input."
210
+ )
211
+ async def notify_processing_complete_and_idle(self) -> None:
212
+ if not self.context.current_phase.is_terminal() and self.context.current_phase != AgentOperationalPhase.IDLE:
213
+ await self._transition_phase(AgentOperationalPhase.IDLE, "notify_phase_idle_entered")
214
+ elif self.context.current_phase == AgentOperationalPhase.IDLE:
215
+ logger.debug(f"Agent '{self.context.agent_id}' processing complete, already IDLE.")
216
+ else:
217
+ logger.warning(f"Agent '{self.context.agent_id}' notify_processing_complete_and_idle called in unexpected phase: {self.context.current_phase.value}")
218
+
219
+ @phase_transition(
220
+ source_phases=[
221
+ AgentOperationalPhase.UNINITIALIZED, AgentOperationalPhase.BOOTSTRAPPING, AgentOperationalPhase.IDLE,
222
+ AgentOperationalPhase.PROCESSING_USER_INPUT, AgentOperationalPhase.AWAITING_LLM_RESPONSE,
223
+ AgentOperationalPhase.ANALYZING_LLM_RESPONSE, AgentOperationalPhase.AWAITING_TOOL_APPROVAL,
224
+ AgentOperationalPhase.TOOL_DENIED, AgentOperationalPhase.EXECUTING_TOOL,
225
+ AgentOperationalPhase.PROCESSING_TOOL_RESULT, AgentOperationalPhase.SHUTTING_DOWN
226
+ ],
227
+ target_phase=AgentOperationalPhase.ERROR,
228
+ description="A catch-all transition that can occur from any non-terminal state if an unrecoverable error happens."
229
+ )
230
+ async def notify_error_occurred(self, error_message: str, error_details: Optional[str] = None) -> None:
231
+ if self.context.current_phase != AgentOperationalPhase.ERROR:
232
+ data = {"error_message": error_message, "error_details": error_details}
233
+ await self._transition_phase(AgentOperationalPhase.ERROR, "notify_phase_error_entered", additional_data=data)
234
+ else:
235
+ logger.debug(f"Agent '{self.context.agent_id}' already in ERROR phase when another error notified: {error_message}")
236
+
237
+ @phase_transition(
238
+ source_phases=[
239
+ AgentOperationalPhase.UNINITIALIZED, AgentOperationalPhase.BOOTSTRAPPING, AgentOperationalPhase.IDLE,
240
+ AgentOperationalPhase.PROCESSING_USER_INPUT, AgentOperationalPhase.AWAITING_LLM_RESPONSE,
241
+ AgentOperationalPhase.ANALYZING_LLM_RESPONSE, AgentOperationalPhase.AWAITING_TOOL_APPROVAL,
242
+ AgentOperationalPhase.TOOL_DENIED, AgentOperationalPhase.EXECUTING_TOOL,
243
+ AgentOperationalPhase.PROCESSING_TOOL_RESULT
244
+ ],
245
+ target_phase=AgentOperationalPhase.SHUTTING_DOWN,
246
+ description="Fires when the agent begins its graceful shutdown sequence."
247
+ )
248
+ async def notify_shutdown_initiated(self) -> None:
249
+ if not self.context.current_phase.is_terminal():
250
+ await self._transition_phase(AgentOperationalPhase.SHUTTING_DOWN, "notify_phase_shutting_down_started")
251
+ else:
252
+ logger.debug(f"Agent '{self.context.agent_id}' shutdown initiated but already in a terminal phase: {self.context.current_phase.value}")
253
+
254
+ @phase_transition(
255
+ source_phases=[AgentOperationalPhase.SHUTTING_DOWN],
256
+ target_phase=AgentOperationalPhase.SHUTDOWN_COMPLETE,
257
+ description="The final transition when the agent has successfully shut down and released its resources."
258
+ )
259
+ async def notify_final_shutdown_complete(self) -> None:
260
+ final_phase = AgentOperationalPhase.ERROR if self.context.current_phase == AgentOperationalPhase.ERROR else AgentOperationalPhase.SHUTDOWN_COMPLETE
261
+ if final_phase == AgentOperationalPhase.ERROR:
262
+ await self._transition_phase(AgentOperationalPhase.ERROR, "notify_phase_error_entered", additional_data={"error_message": "Shutdown completed with agent in error state."})
263
+ else:
264
+ await self._transition_phase(AgentOperationalPhase.SHUTDOWN_COMPLETE, "notify_phase_shutdown_completed")
@@ -0,0 +1,89 @@
1
+ # file: autobyteus/autobyteus/agent/context/agent_runtime_state.py
2
+ import logging
3
+ from typing import List, Dict, Any, Optional, TYPE_CHECKING
4
+
5
+ from autobyteus.agent.events.agent_input_event_queue_manager import AgentInputEventQueueManager
6
+ # AgentOutputDataManager is no longer part of AgentRuntimeState
7
+ # from autobyteus.agent.events.agent_output_data_manager import AgentOutputDataManager
8
+
9
+ from autobyteus.llm.base_llm import BaseLLM
10
+ from autobyteus.agent.phases import AgentOperationalPhase
11
+ from autobyteus.agent.workspace.base_workspace import BaseAgentWorkspace
12
+ from autobyteus.agent.tool_invocation import ToolInvocation
13
+ # LLMConfig is no longer needed here
14
+ # from autobyteus.llm.utils.llm_config import LLMConfig
15
+
16
+ if TYPE_CHECKING:
17
+ from autobyteus.agent.phases import AgentPhaseManager
18
+ from autobyteus.tools.base_tool import BaseTool
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ class AgentRuntimeState:
23
+ """
24
+ Encapsulates the dynamic, stateful data of an agent instance.
25
+ Input event queues are initialized by the AgentWorker via a bootstrap step.
26
+ Output data is now handled by emitting events via AgentExternalEventNotifier.
27
+ """
28
+ def __init__(self,
29
+ agent_id: str,
30
+ workspace: Optional[BaseAgentWorkspace] = None,
31
+ conversation_history: Optional[List[Dict[str, Any]]] = None,
32
+ custom_data: Optional[Dict[str, Any]] = None):
33
+ if not agent_id or not isinstance(agent_id, str):
34
+ raise ValueError("AgentRuntimeState requires a non-empty string 'agent_id'.")
35
+ if workspace is not None and not isinstance(workspace, BaseAgentWorkspace): # pragma: no cover
36
+ raise TypeError(f"AgentRuntimeState 'workspace' must be a BaseAgentWorkspace or None. Got {type(workspace)}")
37
+
38
+ self.agent_id: str = agent_id
39
+ self.current_phase: AgentOperationalPhase = AgentOperationalPhase.UNINITIALIZED
40
+ self.llm_instance: Optional[BaseLLM] = None
41
+ self.tool_instances: Optional[Dict[str, 'BaseTool']] = None
42
+
43
+ self.input_event_queues: Optional[AgentInputEventQueueManager] = None
44
+ # REMOVED: self.output_data_queues attribute
45
+
46
+ self.workspace: Optional[BaseAgentWorkspace] = workspace
47
+ self.conversation_history: List[Dict[str, Any]] = conversation_history or []
48
+ self.pending_tool_approvals: Dict[str, ToolInvocation] = {}
49
+ self.custom_data: Dict[str, Any] = custom_data or {}
50
+
51
+ self.processed_system_prompt: Optional[str] = None
52
+ # self.final_llm_config_for_creation removed
53
+
54
+ self.phase_manager_ref: Optional['AgentPhaseManager'] = None
55
+
56
+ logger.info(f"AgentRuntimeState initialized for agent_id '{self.agent_id}'. Initial phase: {self.current_phase.value}. Workspace linked. InputQueues pending initialization. Output data via notifier.")
57
+
58
+ def add_message_to_history(self, message: Dict[str, Any]) -> None:
59
+ if not isinstance(message, dict) or "role" not in message: # pragma: no cover
60
+ logger.warning(f"Attempted to add malformed message to history for agent '{self.agent_id}': {message}")
61
+ return
62
+ self.conversation_history.append(message)
63
+ logger.debug(f"Message added to history for agent '{self.agent_id}': role={message['role']}")
64
+
65
+ def store_pending_tool_invocation(self, invocation: ToolInvocation) -> None:
66
+ if not isinstance(invocation, ToolInvocation) or not invocation.id: # pragma: no cover
67
+ logger.error(f"Agent '{self.agent_id}': Attempted to store invalid ToolInvocation for approval: {invocation}")
68
+ return
69
+ self.pending_tool_approvals[invocation.id] = invocation
70
+ logger.info(f"Agent '{self.agent_id}': Stored pending tool invocation '{invocation.id}' ({invocation.name}).")
71
+
72
+ def retrieve_pending_tool_invocation(self, invocation_id: str) -> Optional[ToolInvocation]:
73
+ invocation = self.pending_tool_approvals.pop(invocation_id, None)
74
+ if invocation:
75
+ logger.info(f"Agent '{self.agent_id}': Retrieved pending tool invocation '{invocation_id}' ({invocation.name}).")
76
+ else: # pragma: no cover
77
+ logger.warning(f"Agent '{self.agent_id}': Pending tool invocation '{invocation_id}' not found.")
78
+ return invocation
79
+
80
+ def __repr__(self) -> str:
81
+ phase_repr = self.current_phase.value
82
+ llm_status = "Initialized" if self.llm_instance else "Not Initialized"
83
+ tools_status = f"{len(self.tool_instances)} Initialized" if self.tool_instances is not None else "Not Initialized"
84
+ input_queues_status = "Initialized" if self.input_event_queues else "Not Initialized"
85
+ # REMOVED output_queues_status from repr
86
+ return (f"AgentRuntimeState(agent_id='{self.agent_id}', current_phase='{phase_repr}', "
87
+ f"llm_status='{llm_status}', tools_status='{tools_status}', "
88
+ f"input_queues_status='{input_queues_status}', "
89
+ f"pending_approvals={len(self.pending_tool_approvals)}, history_len={len(self.conversation_history)})")