autobyteus 1.2.1__py3-none-any.whl → 1.2.3__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 (466) hide show
  1. autobyteus/agent/agent.py +15 -5
  2. autobyteus/agent/bootstrap_steps/__init__.py +1 -3
  3. autobyteus/agent/bootstrap_steps/agent_bootstrapper.py +3 -59
  4. autobyteus/agent/bootstrap_steps/base_bootstrap_step.py +1 -4
  5. autobyteus/agent/bootstrap_steps/mcp_server_prewarming_step.py +1 -3
  6. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +16 -13
  7. autobyteus/agent/bootstrap_steps/workspace_context_initialization_step.py +2 -4
  8. autobyteus/agent/context/agent_config.py +43 -20
  9. autobyteus/agent/context/agent_context.py +23 -18
  10. autobyteus/agent/context/agent_runtime_state.py +19 -19
  11. autobyteus/agent/events/__init__.py +16 -1
  12. autobyteus/agent/events/agent_events.py +43 -3
  13. autobyteus/agent/events/agent_input_event_queue_manager.py +79 -26
  14. autobyteus/agent/events/event_store.py +57 -0
  15. autobyteus/agent/events/notifiers.py +69 -59
  16. autobyteus/agent/events/worker_event_dispatcher.py +21 -64
  17. autobyteus/agent/factory/agent_factory.py +52 -0
  18. autobyteus/agent/handlers/__init__.py +2 -0
  19. autobyteus/agent/handlers/approved_tool_invocation_event_handler.py +51 -34
  20. autobyteus/agent/handlers/bootstrap_event_handler.py +155 -0
  21. autobyteus/agent/handlers/inter_agent_message_event_handler.py +10 -0
  22. autobyteus/agent/handlers/lifecycle_event_logger.py +19 -11
  23. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +10 -15
  24. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +188 -48
  25. autobyteus/agent/handlers/tool_execution_approval_event_handler.py +0 -10
  26. autobyteus/agent/handlers/tool_invocation_request_event_handler.py +53 -48
  27. autobyteus/agent/handlers/tool_result_event_handler.py +7 -8
  28. autobyteus/agent/handlers/user_input_message_event_handler.py +10 -3
  29. autobyteus/agent/input_processor/memory_ingest_input_processor.py +40 -0
  30. autobyteus/agent/lifecycle/__init__.py +12 -0
  31. autobyteus/agent/lifecycle/base_processor.py +109 -0
  32. autobyteus/agent/lifecycle/events.py +35 -0
  33. autobyteus/agent/lifecycle/processor_definition.py +36 -0
  34. autobyteus/agent/lifecycle/processor_registry.py +106 -0
  35. autobyteus/agent/llm_request_assembler.py +98 -0
  36. autobyteus/agent/llm_response_processor/__init__.py +1 -8
  37. autobyteus/agent/message/context_file_type.py +1 -1
  38. autobyteus/agent/runtime/agent_runtime.py +29 -21
  39. autobyteus/agent/runtime/agent_worker.py +98 -19
  40. autobyteus/agent/shutdown_steps/__init__.py +2 -0
  41. autobyteus/agent/shutdown_steps/agent_shutdown_orchestrator.py +2 -0
  42. autobyteus/agent/shutdown_steps/tool_cleanup_step.py +58 -0
  43. autobyteus/agent/status/__init__.py +14 -0
  44. autobyteus/agent/status/manager.py +93 -0
  45. autobyteus/agent/status/status_deriver.py +96 -0
  46. autobyteus/agent/{phases/phase_enum.py → status/status_enum.py} +16 -16
  47. autobyteus/agent/status/status_update_utils.py +73 -0
  48. autobyteus/agent/streaming/__init__.py +52 -5
  49. autobyteus/agent/streaming/adapters/__init__.py +18 -0
  50. autobyteus/agent/streaming/adapters/invocation_adapter.py +184 -0
  51. autobyteus/agent/streaming/adapters/tool_call_parsing.py +163 -0
  52. autobyteus/agent/streaming/adapters/tool_syntax_registry.py +67 -0
  53. autobyteus/agent/streaming/agent_event_stream.py +3 -183
  54. autobyteus/agent/streaming/api_tool_call/__init__.py +16 -0
  55. autobyteus/agent/streaming/api_tool_call/file_content_streamer.py +56 -0
  56. autobyteus/agent/streaming/api_tool_call/json_string_field_extractor.py +175 -0
  57. autobyteus/agent/streaming/api_tool_call_streaming_response_handler.py +4 -0
  58. autobyteus/agent/streaming/events/__init__.py +6 -0
  59. autobyteus/agent/streaming/events/stream_event_payloads.py +284 -0
  60. autobyteus/agent/streaming/events/stream_events.py +141 -0
  61. autobyteus/agent/streaming/handlers/__init__.py +15 -0
  62. autobyteus/agent/streaming/handlers/api_tool_call_streaming_response_handler.py +303 -0
  63. autobyteus/agent/streaming/handlers/parsing_streaming_response_handler.py +107 -0
  64. autobyteus/agent/streaming/handlers/pass_through_streaming_response_handler.py +107 -0
  65. autobyteus/agent/streaming/handlers/streaming_handler_factory.py +177 -0
  66. autobyteus/agent/streaming/handlers/streaming_response_handler.py +58 -0
  67. autobyteus/agent/streaming/parser/__init__.py +61 -0
  68. autobyteus/agent/streaming/parser/event_emitter.py +181 -0
  69. autobyteus/agent/streaming/parser/events.py +4 -0
  70. autobyteus/agent/streaming/parser/invocation_adapter.py +4 -0
  71. autobyteus/agent/streaming/parser/json_parsing_strategies/__init__.py +19 -0
  72. autobyteus/agent/streaming/parser/json_parsing_strategies/base.py +32 -0
  73. autobyteus/agent/streaming/parser/json_parsing_strategies/default.py +34 -0
  74. autobyteus/agent/streaming/parser/json_parsing_strategies/gemini.py +31 -0
  75. autobyteus/agent/streaming/parser/json_parsing_strategies/openai.py +64 -0
  76. autobyteus/agent/streaming/parser/json_parsing_strategies/registry.py +75 -0
  77. autobyteus/agent/streaming/parser/parser_context.py +227 -0
  78. autobyteus/agent/streaming/parser/parser_factory.py +132 -0
  79. autobyteus/agent/streaming/parser/sentinel_format.py +7 -0
  80. autobyteus/agent/streaming/parser/state_factory.py +62 -0
  81. autobyteus/agent/streaming/parser/states/__init__.py +1 -0
  82. autobyteus/agent/streaming/parser/states/base_state.py +60 -0
  83. autobyteus/agent/streaming/parser/states/custom_xml_tag_run_bash_parsing_state.py +38 -0
  84. autobyteus/agent/streaming/parser/states/custom_xml_tag_write_file_parsing_state.py +55 -0
  85. autobyteus/agent/streaming/parser/states/delimited_content_state.py +146 -0
  86. autobyteus/agent/streaming/parser/states/json_initialization_state.py +144 -0
  87. autobyteus/agent/streaming/parser/states/json_tool_parsing_state.py +137 -0
  88. autobyteus/agent/streaming/parser/states/sentinel_content_state.py +30 -0
  89. autobyteus/agent/streaming/parser/states/sentinel_initialization_state.py +117 -0
  90. autobyteus/agent/streaming/parser/states/text_state.py +78 -0
  91. autobyteus/agent/streaming/parser/states/xml_patch_file_tool_parsing_state.py +328 -0
  92. autobyteus/agent/streaming/parser/states/xml_run_bash_tool_parsing_state.py +129 -0
  93. autobyteus/agent/streaming/parser/states/xml_tag_initialization_state.py +151 -0
  94. autobyteus/agent/streaming/parser/states/xml_tool_parsing_state.py +63 -0
  95. autobyteus/agent/streaming/parser/states/xml_write_file_tool_parsing_state.py +343 -0
  96. autobyteus/agent/streaming/parser/strategies/__init__.py +17 -0
  97. autobyteus/agent/streaming/parser/strategies/base.py +24 -0
  98. autobyteus/agent/streaming/parser/strategies/json_tool_strategy.py +26 -0
  99. autobyteus/agent/streaming/parser/strategies/registry.py +28 -0
  100. autobyteus/agent/streaming/parser/strategies/sentinel_strategy.py +23 -0
  101. autobyteus/agent/streaming/parser/strategies/xml_tag_strategy.py +21 -0
  102. autobyteus/agent/streaming/parser/stream_scanner.py +167 -0
  103. autobyteus/agent/streaming/parser/streaming_parser.py +212 -0
  104. autobyteus/agent/streaming/parser/tool_call_parsing.py +4 -0
  105. autobyteus/agent/streaming/parser/tool_constants.py +7 -0
  106. autobyteus/agent/streaming/parser/tool_syntax_registry.py +4 -0
  107. autobyteus/agent/streaming/parser/xml_tool_parsing_state_registry.py +55 -0
  108. autobyteus/agent/streaming/parsing_streaming_response_handler.py +4 -0
  109. autobyteus/agent/streaming/pass_through_streaming_response_handler.py +4 -0
  110. autobyteus/agent/streaming/queue_streamer.py +3 -57
  111. autobyteus/agent/streaming/segments/__init__.py +5 -0
  112. autobyteus/agent/streaming/segments/segment_events.py +81 -0
  113. autobyteus/agent/streaming/stream_event_payloads.py +2 -223
  114. autobyteus/agent/streaming/stream_events.py +3 -140
  115. autobyteus/agent/streaming/streaming_handler_factory.py +4 -0
  116. autobyteus/agent/streaming/streaming_response_handler.py +4 -0
  117. autobyteus/agent/streaming/streams/__init__.py +5 -0
  118. autobyteus/agent/streaming/streams/agent_event_stream.py +197 -0
  119. autobyteus/agent/streaming/utils/__init__.py +5 -0
  120. autobyteus/agent/streaming/utils/queue_streamer.py +59 -0
  121. autobyteus/agent/system_prompt_processor/__init__.py +2 -0
  122. autobyteus/agent/system_prompt_processor/available_skills_processor.py +96 -0
  123. autobyteus/agent/system_prompt_processor/base_processor.py +1 -1
  124. autobyteus/agent/system_prompt_processor/processor_meta.py +15 -2
  125. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +39 -58
  126. autobyteus/agent/token_budget.py +56 -0
  127. autobyteus/agent/tool_execution_result_processor/memory_ingest_tool_result_processor.py +29 -0
  128. autobyteus/agent/tool_invocation.py +16 -40
  129. autobyteus/agent/tool_invocation_preprocessor/__init__.py +9 -0
  130. autobyteus/agent/tool_invocation_preprocessor/base_preprocessor.py +45 -0
  131. autobyteus/agent/tool_invocation_preprocessor/processor_definition.py +15 -0
  132. autobyteus/agent/tool_invocation_preprocessor/processor_meta.py +33 -0
  133. autobyteus/agent/tool_invocation_preprocessor/processor_registry.py +60 -0
  134. autobyteus/agent/utils/wait_for_idle.py +12 -14
  135. autobyteus/agent/workspace/base_workspace.py +6 -27
  136. autobyteus/agent_team/agent_team.py +3 -3
  137. autobyteus/agent_team/agent_team_builder.py +1 -41
  138. autobyteus/agent_team/bootstrap_steps/__init__.py +0 -4
  139. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +8 -18
  140. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +4 -16
  141. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +1 -2
  142. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +1 -2
  143. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +1 -2
  144. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +4 -4
  145. autobyteus/agent_team/context/agent_team_config.py +6 -3
  146. autobyteus/agent_team/context/agent_team_context.py +25 -3
  147. autobyteus/agent_team/context/agent_team_runtime_state.py +9 -6
  148. autobyteus/agent_team/events/__init__.py +11 -0
  149. autobyteus/agent_team/events/agent_team_event_dispatcher.py +22 -9
  150. autobyteus/agent_team/events/agent_team_events.py +16 -0
  151. autobyteus/agent_team/events/event_store.py +57 -0
  152. autobyteus/agent_team/factory/agent_team_factory.py +8 -0
  153. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +18 -2
  154. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +21 -5
  155. autobyteus/agent_team/handlers/process_user_message_event_handler.py +17 -8
  156. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +19 -4
  157. autobyteus/agent_team/runtime/agent_team_runtime.py +41 -10
  158. autobyteus/agent_team/runtime/agent_team_worker.py +69 -5
  159. autobyteus/agent_team/status/__init__.py +14 -0
  160. autobyteus/agent_team/status/agent_team_status.py +18 -0
  161. autobyteus/agent_team/status/agent_team_status_manager.py +33 -0
  162. autobyteus/agent_team/status/status_deriver.py +62 -0
  163. autobyteus/agent_team/status/status_update_utils.py +42 -0
  164. autobyteus/agent_team/streaming/__init__.py +2 -2
  165. autobyteus/agent_team/streaming/agent_team_event_notifier.py +6 -6
  166. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +4 -4
  167. autobyteus/agent_team/streaming/agent_team_stream_events.py +3 -3
  168. autobyteus/agent_team/system_prompt_processor/__init__.py +6 -0
  169. autobyteus/agent_team/system_prompt_processor/team_manifest_injector_processor.py +76 -0
  170. autobyteus/agent_team/task_notification/task_notification_mode.py +19 -0
  171. autobyteus/agent_team/utils/wait_for_idle.py +4 -4
  172. autobyteus/cli/agent_cli.py +18 -10
  173. autobyteus/cli/agent_team_tui/app.py +14 -11
  174. autobyteus/cli/agent_team_tui/state.py +13 -15
  175. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +15 -15
  176. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +143 -36
  177. autobyteus/cli/agent_team_tui/widgets/renderables.py +1 -1
  178. autobyteus/cli/agent_team_tui/widgets/shared.py +25 -25
  179. autobyteus/cli/cli_display.py +193 -44
  180. autobyteus/cli/workflow_tui/app.py +9 -10
  181. autobyteus/cli/workflow_tui/state.py +14 -16
  182. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +15 -15
  183. autobyteus/cli/workflow_tui/widgets/focus_pane.py +137 -35
  184. autobyteus/cli/workflow_tui/widgets/renderables.py +1 -1
  185. autobyteus/cli/workflow_tui/widgets/shared.py +25 -25
  186. autobyteus/clients/autobyteus_client.py +94 -1
  187. autobyteus/events/event_types.py +11 -18
  188. autobyteus/llm/api/autobyteus_llm.py +33 -29
  189. autobyteus/llm/api/claude_llm.py +142 -36
  190. autobyteus/llm/api/gemini_llm.py +163 -59
  191. autobyteus/llm/api/grok_llm.py +1 -1
  192. autobyteus/llm/api/minimax_llm.py +26 -0
  193. autobyteus/llm/api/mistral_llm.py +113 -87
  194. autobyteus/llm/api/ollama_llm.py +9 -42
  195. autobyteus/llm/api/openai_compatible_llm.py +127 -91
  196. autobyteus/llm/api/openai_llm.py +3 -3
  197. autobyteus/llm/api/openai_responses_llm.py +324 -0
  198. autobyteus/llm/api/zhipu_llm.py +21 -2
  199. autobyteus/llm/autobyteus_provider.py +70 -60
  200. autobyteus/llm/base_llm.py +85 -81
  201. autobyteus/llm/converters/__init__.py +14 -0
  202. autobyteus/llm/converters/anthropic_tool_call_converter.py +37 -0
  203. autobyteus/llm/converters/gemini_tool_call_converter.py +57 -0
  204. autobyteus/llm/converters/mistral_tool_call_converter.py +37 -0
  205. autobyteus/llm/converters/openai_tool_call_converter.py +38 -0
  206. autobyteus/llm/extensions/base_extension.py +6 -12
  207. autobyteus/llm/extensions/token_usage_tracking_extension.py +45 -18
  208. autobyteus/llm/llm_factory.py +282 -204
  209. autobyteus/llm/lmstudio_provider.py +60 -49
  210. autobyteus/llm/models.py +35 -2
  211. autobyteus/llm/ollama_provider.py +60 -49
  212. autobyteus/llm/ollama_provider_resolver.py +0 -1
  213. autobyteus/llm/prompt_renderers/__init__.py +19 -0
  214. autobyteus/llm/prompt_renderers/anthropic_prompt_renderer.py +104 -0
  215. autobyteus/llm/prompt_renderers/autobyteus_prompt_renderer.py +19 -0
  216. autobyteus/llm/prompt_renderers/base_prompt_renderer.py +10 -0
  217. autobyteus/llm/prompt_renderers/gemini_prompt_renderer.py +63 -0
  218. autobyteus/llm/prompt_renderers/mistral_prompt_renderer.py +87 -0
  219. autobyteus/llm/prompt_renderers/ollama_prompt_renderer.py +51 -0
  220. autobyteus/llm/prompt_renderers/openai_chat_renderer.py +97 -0
  221. autobyteus/llm/prompt_renderers/openai_responses_renderer.py +101 -0
  222. autobyteus/llm/providers.py +1 -3
  223. autobyteus/llm/token_counter/claude_token_counter.py +56 -25
  224. autobyteus/llm/token_counter/mistral_token_counter.py +12 -8
  225. autobyteus/llm/token_counter/openai_token_counter.py +24 -5
  226. autobyteus/llm/token_counter/token_counter_factory.py +12 -5
  227. autobyteus/llm/utils/llm_config.py +6 -12
  228. autobyteus/llm/utils/media_payload_formatter.py +27 -20
  229. autobyteus/llm/utils/messages.py +55 -3
  230. autobyteus/llm/utils/response_types.py +3 -0
  231. autobyteus/llm/utils/tool_call_delta.py +31 -0
  232. autobyteus/memory/__init__.py +32 -0
  233. autobyteus/memory/active_transcript.py +69 -0
  234. autobyteus/memory/compaction/__init__.py +9 -0
  235. autobyteus/memory/compaction/compaction_result.py +8 -0
  236. autobyteus/memory/compaction/compactor.py +89 -0
  237. autobyteus/memory/compaction/summarizer.py +11 -0
  238. autobyteus/memory/compaction_snapshot_builder.py +84 -0
  239. autobyteus/memory/memory_manager.py +183 -0
  240. autobyteus/memory/models/__init__.py +14 -0
  241. autobyteus/memory/models/episodic_item.py +41 -0
  242. autobyteus/memory/models/memory_types.py +7 -0
  243. autobyteus/memory/models/raw_trace_item.py +79 -0
  244. autobyteus/memory/models/semantic_item.py +41 -0
  245. autobyteus/memory/models/tool_interaction.py +20 -0
  246. autobyteus/memory/policies/__init__.py +5 -0
  247. autobyteus/memory/policies/compaction_policy.py +16 -0
  248. autobyteus/memory/retrieval/__init__.py +7 -0
  249. autobyteus/memory/retrieval/memory_bundle.py +11 -0
  250. autobyteus/memory/retrieval/retriever.py +13 -0
  251. autobyteus/memory/store/__init__.py +7 -0
  252. autobyteus/memory/store/base_store.py +14 -0
  253. autobyteus/memory/store/file_store.py +98 -0
  254. autobyteus/memory/tool_interaction_builder.py +46 -0
  255. autobyteus/memory/turn_tracker.py +9 -0
  256. autobyteus/multimedia/audio/api/autobyteus_audio_client.py +19 -5
  257. autobyteus/multimedia/audio/api/gemini_audio_client.py +108 -16
  258. autobyteus/multimedia/audio/audio_client_factory.py +47 -9
  259. autobyteus/multimedia/audio/audio_model.py +2 -1
  260. autobyteus/multimedia/image/api/autobyteus_image_client.py +19 -5
  261. autobyteus/multimedia/image/api/gemini_image_client.py +38 -17
  262. autobyteus/multimedia/image/api/openai_image_client.py +125 -43
  263. autobyteus/multimedia/image/autobyteus_image_provider.py +2 -1
  264. autobyteus/multimedia/image/image_client_factory.py +47 -15
  265. autobyteus/multimedia/image/image_model.py +5 -2
  266. autobyteus/multimedia/providers.py +3 -2
  267. autobyteus/skills/loader.py +71 -0
  268. autobyteus/skills/model.py +11 -0
  269. autobyteus/skills/registry.py +70 -0
  270. autobyteus/task_management/tools/todo_tools/add_todo.py +2 -2
  271. autobyteus/task_management/tools/todo_tools/create_todo_list.py +2 -2
  272. autobyteus/task_management/tools/todo_tools/update_todo_status.py +2 -2
  273. autobyteus/tools/__init__.py +34 -47
  274. autobyteus/tools/base_tool.py +7 -0
  275. autobyteus/tools/file/__init__.py +2 -6
  276. autobyteus/tools/file/patch_file.py +149 -0
  277. autobyteus/tools/file/read_file.py +36 -5
  278. autobyteus/tools/file/write_file.py +4 -1
  279. autobyteus/tools/functional_tool.py +43 -6
  280. autobyteus/tools/mcp/__init__.py +2 -0
  281. autobyteus/tools/mcp/config_service.py +5 -1
  282. autobyteus/tools/mcp/server/__init__.py +2 -0
  283. autobyteus/tools/mcp/server/http_managed_mcp_server.py +1 -1
  284. autobyteus/tools/mcp/server/websocket_managed_mcp_server.py +141 -0
  285. autobyteus/tools/mcp/server_instance_manager.py +8 -1
  286. autobyteus/tools/mcp/types.py +61 -0
  287. autobyteus/tools/multimedia/audio_tools.py +70 -17
  288. autobyteus/tools/multimedia/download_media_tool.py +18 -4
  289. autobyteus/tools/multimedia/image_tools.py +246 -62
  290. autobyteus/tools/operation_executor/journal_manager.py +107 -0
  291. autobyteus/tools/operation_executor/operation_event_buffer.py +57 -0
  292. autobyteus/tools/operation_executor/operation_event_producer.py +29 -0
  293. autobyteus/tools/operation_executor/operation_executor.py +58 -0
  294. autobyteus/tools/registry/tool_definition.py +43 -2
  295. autobyteus/tools/skill/load_skill.py +50 -0
  296. autobyteus/tools/terminal/__init__.py +45 -0
  297. autobyteus/tools/terminal/ansi_utils.py +32 -0
  298. autobyteus/tools/terminal/background_process_manager.py +233 -0
  299. autobyteus/tools/terminal/output_buffer.py +105 -0
  300. autobyteus/tools/terminal/prompt_detector.py +63 -0
  301. autobyteus/tools/terminal/pty_session.py +241 -0
  302. autobyteus/tools/terminal/session_factory.py +20 -0
  303. autobyteus/tools/terminal/terminal_session_manager.py +226 -0
  304. autobyteus/tools/terminal/tools/__init__.py +13 -0
  305. autobyteus/tools/terminal/tools/get_process_output.py +81 -0
  306. autobyteus/tools/terminal/tools/run_bash.py +109 -0
  307. autobyteus/tools/terminal/tools/start_background_process.py +104 -0
  308. autobyteus/tools/terminal/tools/stop_background_process.py +67 -0
  309. autobyteus/tools/terminal/types.py +54 -0
  310. autobyteus/tools/terminal/wsl_tmux_session.py +221 -0
  311. autobyteus/tools/terminal/wsl_utils.py +156 -0
  312. autobyteus/tools/transaction_management/backup_handler.py +48 -0
  313. autobyteus/tools/transaction_management/operation_lifecycle_manager.py +62 -0
  314. autobyteus/tools/usage/__init__.py +1 -2
  315. autobyteus/tools/usage/formatters/__init__.py +17 -1
  316. autobyteus/tools/usage/formatters/base_formatter.py +8 -0
  317. autobyteus/tools/usage/formatters/default_xml_schema_formatter.py +2 -2
  318. autobyteus/tools/usage/formatters/mistral_json_schema_formatter.py +18 -0
  319. autobyteus/tools/usage/formatters/patch_file_xml_example_formatter.py +64 -0
  320. autobyteus/tools/usage/formatters/patch_file_xml_schema_formatter.py +31 -0
  321. autobyteus/tools/usage/formatters/run_bash_xml_example_formatter.py +32 -0
  322. autobyteus/tools/usage/formatters/run_bash_xml_schema_formatter.py +36 -0
  323. autobyteus/tools/usage/formatters/write_file_xml_example_formatter.py +53 -0
  324. autobyteus/tools/usage/formatters/write_file_xml_schema_formatter.py +31 -0
  325. autobyteus/tools/usage/providers/tool_manifest_provider.py +10 -10
  326. autobyteus/tools/usage/registries/__init__.py +1 -3
  327. autobyteus/tools/usage/registries/tool_formatting_registry.py +115 -8
  328. autobyteus/tools/usage/tool_schema_provider.py +51 -0
  329. autobyteus/tools/web/__init__.py +4 -0
  330. autobyteus/tools/web/read_url_tool.py +80 -0
  331. autobyteus/utils/diff_utils.py +271 -0
  332. autobyteus/utils/download_utils.py +109 -0
  333. autobyteus/utils/file_utils.py +57 -2
  334. autobyteus/utils/gemini_helper.py +56 -0
  335. autobyteus/utils/gemini_model_mapping.py +71 -0
  336. autobyteus/utils/llm_output_formatter.py +75 -0
  337. autobyteus/utils/tool_call_format.py +36 -0
  338. autobyteus/workflow/agentic_workflow.py +3 -3
  339. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +2 -2
  340. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +2 -2
  341. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +2 -2
  342. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +3 -9
  343. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +6 -6
  344. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +2 -2
  345. autobyteus/workflow/context/workflow_context.py +3 -3
  346. autobyteus/workflow/context/workflow_runtime_state.py +5 -5
  347. autobyteus/workflow/events/workflow_event_dispatcher.py +5 -5
  348. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +3 -3
  349. autobyteus/workflow/handlers/process_user_message_event_handler.py +5 -5
  350. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +2 -2
  351. autobyteus/workflow/runtime/workflow_runtime.py +8 -8
  352. autobyteus/workflow/runtime/workflow_worker.py +3 -3
  353. autobyteus/workflow/status/__init__.py +11 -0
  354. autobyteus/workflow/status/workflow_status.py +19 -0
  355. autobyteus/workflow/status/workflow_status_manager.py +48 -0
  356. autobyteus/workflow/streaming/__init__.py +2 -2
  357. autobyteus/workflow/streaming/workflow_event_notifier.py +7 -7
  358. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +4 -4
  359. autobyteus/workflow/streaming/workflow_stream_events.py +3 -3
  360. autobyteus/workflow/utils/wait_for_idle.py +4 -4
  361. autobyteus-1.2.3.dist-info/METADATA +293 -0
  362. autobyteus-1.2.3.dist-info/RECORD +600 -0
  363. {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/WHEEL +1 -1
  364. {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/top_level.txt +0 -1
  365. autobyteus/agent/bootstrap_steps/agent_runtime_queue_initialization_step.py +0 -57
  366. autobyteus/agent/hooks/__init__.py +0 -16
  367. autobyteus/agent/hooks/base_phase_hook.py +0 -78
  368. autobyteus/agent/hooks/hook_definition.py +0 -36
  369. autobyteus/agent/hooks/hook_meta.py +0 -37
  370. autobyteus/agent/hooks/hook_registry.py +0 -106
  371. autobyteus/agent/llm_response_processor/provider_aware_tool_usage_processor.py +0 -103
  372. autobyteus/agent/phases/__init__.py +0 -18
  373. autobyteus/agent/phases/discover.py +0 -53
  374. autobyteus/agent/phases/manager.py +0 -265
  375. autobyteus/agent/phases/transition_decorator.py +0 -40
  376. autobyteus/agent/phases/transition_info.py +0 -33
  377. autobyteus/agent/remote_agent.py +0 -244
  378. autobyteus/agent/workspace/workspace_definition.py +0 -36
  379. autobyteus/agent/workspace/workspace_meta.py +0 -37
  380. autobyteus/agent/workspace/workspace_registry.py +0 -72
  381. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +0 -25
  382. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +0 -85
  383. autobyteus/agent_team/phases/__init__.py +0 -11
  384. autobyteus/agent_team/phases/agent_team_operational_phase.py +0 -19
  385. autobyteus/agent_team/phases/agent_team_phase_manager.py +0 -48
  386. autobyteus/llm/api/bedrock_llm.py +0 -92
  387. autobyteus/llm/api/groq_llm.py +0 -94
  388. autobyteus/llm/api/nvidia_llm.py +0 -108
  389. autobyteus/llm/utils/token_pricing_config.py +0 -87
  390. autobyteus/rpc/__init__.py +0 -73
  391. autobyteus/rpc/client/__init__.py +0 -17
  392. autobyteus/rpc/client/abstract_client_connection.py +0 -124
  393. autobyteus/rpc/client/client_connection_manager.py +0 -153
  394. autobyteus/rpc/client/sse_client_connection.py +0 -306
  395. autobyteus/rpc/client/stdio_client_connection.py +0 -280
  396. autobyteus/rpc/config/__init__.py +0 -13
  397. autobyteus/rpc/config/agent_server_config.py +0 -153
  398. autobyteus/rpc/config/agent_server_registry.py +0 -152
  399. autobyteus/rpc/hosting.py +0 -244
  400. autobyteus/rpc/protocol.py +0 -244
  401. autobyteus/rpc/server/__init__.py +0 -20
  402. autobyteus/rpc/server/agent_server_endpoint.py +0 -181
  403. autobyteus/rpc/server/base_method_handler.py +0 -40
  404. autobyteus/rpc/server/method_handlers.py +0 -259
  405. autobyteus/rpc/server/sse_server_handler.py +0 -182
  406. autobyteus/rpc/server/stdio_server_handler.py +0 -151
  407. autobyteus/rpc/server_main.py +0 -198
  408. autobyteus/rpc/transport_type.py +0 -13
  409. autobyteus/tools/bash/__init__.py +0 -2
  410. autobyteus/tools/bash/bash_executor.py +0 -100
  411. autobyteus/tools/browser/__init__.py +0 -2
  412. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +0 -75
  413. autobyteus/tools/browser/session_aware/browser_session_aware_tool.py +0 -30
  414. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +0 -154
  415. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +0 -89
  416. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +0 -107
  417. autobyteus/tools/browser/session_aware/factory/browser_session_aware_web_element_trigger_factory.py +0 -14
  418. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_reader_factory.py +0 -26
  419. autobyteus/tools/browser/session_aware/factory/browser_session_aware_webpage_screenshot_taker_factory.py +0 -14
  420. autobyteus/tools/browser/session_aware/shared_browser_session.py +0 -11
  421. autobyteus/tools/browser/session_aware/shared_browser_session_manager.py +0 -25
  422. autobyteus/tools/browser/session_aware/web_element_action.py +0 -20
  423. autobyteus/tools/browser/standalone/__init__.py +0 -6
  424. autobyteus/tools/browser/standalone/factory/__init__.py +0 -0
  425. autobyteus/tools/browser/standalone/factory/webpage_reader_factory.py +0 -25
  426. autobyteus/tools/browser/standalone/factory/webpage_screenshot_taker_factory.py +0 -14
  427. autobyteus/tools/browser/standalone/navigate_to.py +0 -84
  428. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +0 -101
  429. autobyteus/tools/browser/standalone/webpage_image_downloader.py +0 -169
  430. autobyteus/tools/browser/standalone/webpage_reader.py +0 -105
  431. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +0 -105
  432. autobyteus/tools/file/edit_file.py +0 -200
  433. autobyteus/tools/file/list_directory.py +0 -168
  434. autobyteus/tools/file/search_files.py +0 -188
  435. autobyteus/tools/timer.py +0 -175
  436. autobyteus/tools/usage/parsers/__init__.py +0 -22
  437. autobyteus/tools/usage/parsers/_json_extractor.py +0 -99
  438. autobyteus/tools/usage/parsers/_string_decoders.py +0 -18
  439. autobyteus/tools/usage/parsers/anthropic_xml_tool_usage_parser.py +0 -10
  440. autobyteus/tools/usage/parsers/base_parser.py +0 -41
  441. autobyteus/tools/usage/parsers/default_json_tool_usage_parser.py +0 -83
  442. autobyteus/tools/usage/parsers/default_xml_tool_usage_parser.py +0 -316
  443. autobyteus/tools/usage/parsers/exceptions.py +0 -13
  444. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +0 -77
  445. autobyteus/tools/usage/parsers/openai_json_tool_usage_parser.py +0 -149
  446. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +0 -59
  447. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +0 -62
  448. autobyteus/workflow/phases/__init__.py +0 -11
  449. autobyteus/workflow/phases/workflow_operational_phase.py +0 -19
  450. autobyteus/workflow/phases/workflow_phase_manager.py +0 -48
  451. autobyteus-1.2.1.dist-info/METADATA +0 -205
  452. autobyteus-1.2.1.dist-info/RECORD +0 -511
  453. examples/__init__.py +0 -1
  454. examples/agent_team/__init__.py +0 -1
  455. examples/discover_phase_transitions.py +0 -104
  456. examples/run_agentic_software_engineer.py +0 -239
  457. examples/run_browser_agent.py +0 -262
  458. examples/run_google_slides_agent.py +0 -287
  459. examples/run_mcp_browser_client.py +0 -174
  460. examples/run_mcp_google_slides_client.py +0 -270
  461. examples/run_mcp_list_tools.py +0 -189
  462. examples/run_poem_writer.py +0 -284
  463. examples/run_sqlite_agent.py +0 -295
  464. /autobyteus/{tools/browser/session_aware → skills}/__init__.py +0 -0
  465. /autobyteus/tools/{browser/session_aware/factory → skill}/__init__.py +0 -0
  466. {autobyteus-1.2.1.dist-info → autobyteus-1.2.3.dist-info}/licenses/LICENSE +0 -0
@@ -4,8 +4,9 @@ from autobyteus.llm.models import LLMModel
4
4
  from autobyteus.llm.utils.llm_config import LLMConfig
5
5
  from autobyteus.llm.utils.token_usage import TokenUsage
6
6
  from autobyteus.llm.utils.response_types import CompleteResponse, ChunkResponse
7
- from autobyteus.llm.user_message import LLMUserMessage
7
+ from autobyteus.llm.utils.messages import Message
8
8
  from autobyteus.clients import AutobyteusClient
9
+ from autobyteus.llm.prompt_renderers.autobyteus_prompt_renderer import AutobyteusPromptRenderer
9
10
  import logging
10
11
  import uuid
11
12
 
@@ -20,26 +21,34 @@ class AutobyteusLLM(BaseLLM):
20
21
 
21
22
  self.client = AutobyteusClient(server_url=self.model.host_url)
22
23
  self.conversation_id = str(uuid.uuid4())
24
+ self._renderer = AutobyteusPromptRenderer()
23
25
  logger.info(f"AutobyteusLLM initialized for model '{self.model.model_identifier}' with conversation ID: {self.conversation_id}")
24
26
 
25
- async def _send_user_message_to_llm(
27
+ async def _send_messages_to_llm(
26
28
  self,
27
- user_message: LLMUserMessage,
29
+ messages: List[Message],
28
30
  **kwargs
29
31
  ) -> CompleteResponse:
30
- self.add_user_message(user_message)
32
+ rendered = await self._renderer.render(messages)
33
+ if not rendered:
34
+ raise ValueError("AutobyteusLLM requires at least one user message.")
35
+ payload = rendered[0]
31
36
  try:
32
37
  response = await self.client.send_message(
33
38
  conversation_id=self.conversation_id,
34
39
  model_name=self.model.name,
35
- user_message=user_message.content,
36
- image_urls=user_message.image_urls,
37
- audio_urls=user_message.audio_urls,
38
- video_urls=user_message.video_urls
40
+ user_message=payload.get("content", ""),
41
+ image_urls=payload.get("image_urls", []),
42
+ audio_urls=payload.get("audio_urls", []),
43
+ video_urls=payload.get("video_urls", []),
39
44
  )
40
45
 
41
- assistant_message = response['response']
42
- self.add_assistant_message(assistant_message)
46
+ assistant_message = (
47
+ response.get("response")
48
+ or response.get("content")
49
+ or response.get("message")
50
+ or ""
51
+ )
43
52
 
44
53
  token_usage_data = response.get('token_usage') or {}
45
54
  token_usage = TokenUsage(
@@ -54,25 +63,27 @@ class AutobyteusLLM(BaseLLM):
54
63
  )
55
64
  except Exception as e:
56
65
  logger.error(f"Error processing message: {str(e)}")
57
- await self._handle_error_cleanup()
58
66
  raise
59
67
 
60
- async def _stream_user_message_to_llm(
68
+ async def _stream_messages_to_llm(
61
69
  self,
62
- user_message: LLMUserMessage,
70
+ messages: List[Message],
63
71
  **kwargs
64
72
  ) -> AsyncGenerator[ChunkResponse, None]:
65
- self.add_user_message(user_message)
73
+ rendered = await self._renderer.render(messages)
74
+ if not rendered:
75
+ raise ValueError("AutobyteusLLM requires at least one user message.")
76
+ payload = rendered[0]
66
77
  complete_response = ""
67
78
 
68
79
  try:
69
80
  async for chunk in self.client.stream_message(
70
81
  conversation_id=self.conversation_id,
71
82
  model_name=self.model.name,
72
- user_message=user_message.content,
73
- image_urls=user_message.image_urls,
74
- audio_urls=user_message.audio_urls,
75
- video_urls=user_message.video_urls
83
+ user_message=payload.get("content", ""),
84
+ image_urls=payload.get("image_urls", []),
85
+ audio_urls=payload.get("audio_urls", []),
86
+ video_urls=payload.get("video_urls", []),
76
87
  ):
77
88
  if 'error' in chunk:
78
89
  raise RuntimeError(chunk['error'])
@@ -100,24 +111,17 @@ class AutobyteusLLM(BaseLLM):
100
111
  video_urls=chunk.get('video_urls', []),
101
112
  usage=token_usage
102
113
  )
103
-
104
- self.add_assistant_message(complete_response)
105
114
  except Exception as e:
106
115
  logger.error(f"Error streaming message: {str(e)}")
107
- await self._handle_error_cleanup()
108
116
  raise
109
117
 
110
118
  async def cleanup(self):
119
+ """
120
+ Clean up the remote conversation. The owning agent controls the HTTP
121
+ client lifecycle.
122
+ """
111
123
  try:
112
124
  await self.client.cleanup(self.conversation_id)
113
125
  await super().cleanup()
114
126
  except Exception as e:
115
127
  logger.error(f"Error during cleanup: {str(e)}")
116
- finally:
117
- await self.client.close()
118
-
119
- async def _handle_error_cleanup(self):
120
- try:
121
- await self.cleanup()
122
- except Exception as cleanup_error:
123
- logger.error(f"Error during error cleanup: {str(cleanup_error)}")
@@ -1,27 +1,84 @@
1
- from typing import Dict, Optional, List, AsyncGenerator, Tuple
2
1
  import anthropic
3
2
  import os
4
3
  import logging
4
+ from typing import Dict, Optional, List, AsyncGenerator, Tuple
5
+
5
6
  from autobyteus.llm.models import LLMModel
6
7
  from autobyteus.llm.base_llm import BaseLLM
7
8
  from autobyteus.llm.utils.llm_config import LLMConfig
8
9
  from autobyteus.llm.utils.messages import MessageRole, Message
9
10
  from autobyteus.llm.utils.token_usage import TokenUsage
10
11
  from autobyteus.llm.utils.response_types import CompleteResponse, ChunkResponse
11
- from autobyteus.llm.user_message import LLMUserMessage
12
+ from autobyteus.llm.converters import convert_anthropic_tool_call
13
+ from autobyteus.llm.prompt_renderers.anthropic_prompt_renderer import AnthropicPromptRenderer
12
14
 
13
15
  logger = logging.getLogger(__name__)
14
16
 
17
+ def _build_thinking_param(extra_params: Dict) -> Optional[Dict]:
18
+ enabled = extra_params.get("thinking_enabled", False)
19
+ if not isinstance(enabled, bool) or not enabled:
20
+ return None
21
+
22
+ budget = extra_params.get("thinking_budget_tokens", 1024)
23
+ try:
24
+ budget_int = int(budget)
25
+ except (TypeError, ValueError):
26
+ budget_int = 1024
27
+
28
+ return {"type": "enabled", "budget_tokens": budget_int}
29
+
30
+
31
+ def _split_claude_content_blocks(blocks: List) -> Tuple[str, str]:
32
+ """Split Claude content blocks into visible text and thinking summaries."""
33
+ content_segments: List[str] = []
34
+ thinking_segments: List[str] = []
35
+
36
+ for block in blocks or []:
37
+ block_type = getattr(block, "type", None)
38
+ if block_type is None and isinstance(block, dict):
39
+ block_type = block.get("type")
40
+
41
+ if block_type == "text":
42
+ text = getattr(block, "text", None)
43
+ if text is None and isinstance(block, dict):
44
+ text = block.get("text")
45
+ if text:
46
+ content_segments.append(text)
47
+ elif block_type == "thinking":
48
+ thinking = getattr(block, "thinking", None)
49
+ if thinking is None and isinstance(block, dict):
50
+ thinking = block.get("thinking")
51
+ if thinking:
52
+ thinking_segments.append(thinking)
53
+ elif block_type == "redacted_thinking":
54
+ redacted = getattr(block, "redacted_thinking", None)
55
+ if redacted is None and isinstance(block, dict):
56
+ redacted = block.get("redacted_thinking")
57
+ if redacted:
58
+ thinking_segments.append(redacted)
59
+
60
+ return "".join(content_segments), "".join(thinking_segments)
61
+
62
+
63
+ def _split_system_message(messages: List[Message]) -> Tuple[Optional[str], List[Message]]:
64
+ system_parts = [msg.content for msg in messages if msg.role == MessageRole.SYSTEM and msg.content]
65
+ system_prompt = "\n".join(system_parts) if system_parts else None
66
+ remaining = [msg for msg in messages if msg.role != MessageRole.SYSTEM]
67
+ return system_prompt, remaining
68
+
69
+
15
70
  class ClaudeLLM(BaseLLM):
16
71
  def __init__(self, model: LLMModel = None, llm_config: LLMConfig = None):
17
72
  if model is None:
18
- model = LLMModel['claude-4-sonnet']
73
+ model = LLMModel['claude-4.5-sonnet']
19
74
  if llm_config is None:
20
75
  llm_config = LLMConfig()
21
76
 
22
77
  super().__init__(model=model, llm_config=llm_config)
23
78
  self.client = self.initialize()
24
- self.max_tokens = 8000
79
+ self._renderer = AnthropicPromptRenderer()
80
+ # Claude Sonnet 4.5 currently allows up to ~8k output tokens; let config override.
81
+ self.max_tokens = llm_config.max_tokens if llm_config.max_tokens is not None else 8192
25
82
 
26
83
  @classmethod
27
84
  def initialize(cls):
@@ -36,10 +93,6 @@ class ClaudeLLM(BaseLLM):
36
93
  except Exception as e:
37
94
  raise ValueError(f"Failed to initialize Anthropic client: {str(e)}")
38
95
 
39
- def _get_non_system_messages(self) -> List[Dict]:
40
- # NOTE: This will need to be updated to handle multimodal messages for Claude
41
- return [msg.to_dict() for msg in self.messages if msg.role != MessageRole.SYSTEM]
42
-
43
96
  def _create_token_usage(self, input_tokens: int, output_tokens: int) -> TokenUsage:
44
97
  return TokenUsage(
45
98
  prompt_tokens=input_tokens,
@@ -47,23 +100,37 @@ class ClaudeLLM(BaseLLM):
47
100
  total_tokens=input_tokens + output_tokens
48
101
  )
49
102
 
50
- async def _send_user_message_to_llm(self, user_message: LLMUserMessage, **kwargs) -> CompleteResponse:
51
- self.add_user_message(user_message)
103
+ async def _send_messages_to_llm(self, messages: List[Message], **kwargs) -> CompleteResponse:
104
+ try:
105
+ system_prompt, non_system = _split_system_message(messages)
106
+ formatted_messages = await self._renderer.render(non_system)
107
+ thinking_param = _build_thinking_param(self.config.extra_params)
52
108
 
53
- # NOTE: This implementation does not yet support multimodal inputs for Claude.
54
- # It will only send the text content.
109
+ request_kwargs = {
110
+ "model": self.model.value,
111
+ "max_tokens": self.max_tokens,
112
+ "messages": formatted_messages,
113
+ }
114
+ if system_prompt:
115
+ request_kwargs["system"] = system_prompt
116
+ if thinking_param:
117
+ # Extended thinking is not compatible with temperature modifications
118
+ request_kwargs["thinking"] = thinking_param
119
+ else:
120
+ request_kwargs["temperature"] = 0
55
121
 
56
- try:
57
122
  response = self.client.messages.create(
58
- model=self.model.value,
59
- max_tokens=self.max_tokens,
60
- temperature=0,
61
- system=self.system_message,
62
- messages=self._get_non_system_messages()
123
+ **request_kwargs
63
124
  )
64
125
 
65
- assistant_message = response.content[0].text
66
- self.add_assistant_message(assistant_message)
126
+ assistant_message = getattr(response, "text", "") or ""
127
+ reasoning_summary = None
128
+ if response.content:
129
+ parsed_text, parsed_thinking = _split_claude_content_blocks(response.content)
130
+ if parsed_text:
131
+ assistant_message = parsed_text
132
+ if parsed_thinking:
133
+ reasoning_summary = parsed_thinking
67
134
 
68
135
  token_usage = self._create_token_usage(
69
136
  response.usage.input_tokens,
@@ -74,35 +141,72 @@ class ClaudeLLM(BaseLLM):
74
141
 
75
142
  return CompleteResponse(
76
143
  content=assistant_message,
144
+ reasoning=reasoning_summary,
77
145
  usage=token_usage
78
146
  )
79
147
  except anthropic.APIError as e:
80
148
  logger.error(f"Error in Claude API call: {str(e)}")
81
149
  raise ValueError(f"Error in Claude API call: {str(e)}")
82
150
 
83
- async def _stream_user_message_to_llm(
84
- self, user_message: LLMUserMessage, **kwargs
151
+ async def _stream_messages_to_llm(
152
+ self, messages: List[Message], **kwargs
85
153
  ) -> AsyncGenerator[ChunkResponse, None]:
86
- self.add_user_message(user_message)
87
154
  complete_response = ""
155
+ complete_reasoning = ""
88
156
  final_message = None
89
157
 
90
- # NOTE: This implementation does not yet support multimodal inputs for Claude.
91
- # It will only send the text content.
158
+ # Extract tools if provided
159
+ tools = kwargs.get("tools")
92
160
 
93
161
  try:
94
- with self.client.messages.stream(
95
- model=self.model.value,
96
- max_tokens=self.max_tokens,
97
- temperature=0,
98
- system=self.system_message,
99
- messages=self._get_non_system_messages(),
100
- ) as stream:
162
+ # Prepare arguments for stream
163
+ system_prompt, non_system = _split_system_message(messages)
164
+ formatted_messages = await self._renderer.render(non_system)
165
+ thinking_param = _build_thinking_param(self.config.extra_params)
166
+ stream_kwargs = {
167
+ "model": self.model.value,
168
+ "max_tokens": self.max_tokens,
169
+ "messages": formatted_messages,
170
+ }
171
+ if system_prompt:
172
+ stream_kwargs["system"] = system_prompt
173
+ if thinking_param:
174
+ # Extended thinking is not compatible with temperature modifications
175
+ stream_kwargs["thinking"] = thinking_param
176
+ else:
177
+ stream_kwargs["temperature"] = 0
178
+
179
+ if tools:
180
+ stream_kwargs["tools"] = tools
181
+
182
+ with self.client.messages.stream(**stream_kwargs) as stream:
101
183
  for event in stream:
102
- if event.type == "content_block_delta" and event.delta.type == "text_delta":
103
- complete_response += event.delta.text
184
+
185
+ # Handle text content
186
+ if event.type == "content_block_delta":
187
+ delta_type = getattr(event.delta, "type", None)
188
+ if delta_type == "text_delta":
189
+ complete_response += event.delta.text
190
+ yield ChunkResponse(
191
+ content=event.delta.text,
192
+ is_complete=False
193
+ )
194
+ elif delta_type == "thinking_delta":
195
+ thinking_delta = getattr(event.delta, "thinking", None)
196
+ if thinking_delta:
197
+ complete_reasoning += thinking_delta
198
+ yield ChunkResponse(
199
+ content="",
200
+ reasoning=thinking_delta,
201
+ is_complete=False
202
+ )
203
+
204
+ # Handle tool calls using common converter
205
+ tool_calls = convert_anthropic_tool_call(event)
206
+ if tool_calls:
104
207
  yield ChunkResponse(
105
- content=event.delta.text,
208
+ content="",
209
+ tool_calls=tool_calls,
106
210
  is_complete=False
107
211
  )
108
212
 
@@ -120,7 +224,9 @@ class ClaudeLLM(BaseLLM):
120
224
  usage=token_usage
121
225
  )
122
226
 
123
- self.add_assistant_message(complete_response)
227
+ # Only add assistant message if there's actual content.
228
+ # Tool-call-only responses should not add empty messages, as Claude API
229
+ # rejects subsequent requests with "all messages must have non-empty content".
124
230
  except anthropic.APIError as e:
125
231
  logger.error(f"Error in Claude API streaming: {str(e)}")
126
232
  raise ValueError(f"Error in Claude API streaming: {str(e)}")
@@ -1,29 +1,41 @@
1
1
  import logging
2
- from typing import Dict, Optional, List, AsyncGenerator, Any
3
- from google import genai
2
+ import asyncio
3
+ from typing import Dict, List, AsyncGenerator, Any, Optional
4
4
  from google.genai import types as genai_types
5
- import os
6
5
  from autobyteus.llm.models import LLMModel
7
6
  from autobyteus.llm.base_llm import BaseLLM
8
7
  from autobyteus.llm.utils.llm_config import LLMConfig
9
8
  from autobyteus.llm.utils.messages import MessageRole, Message
10
9
  from autobyteus.llm.utils.token_usage import TokenUsage
11
10
  from autobyteus.llm.utils.response_types import CompleteResponse, ChunkResponse
12
- from autobyteus.llm.user_message import LLMUserMessage
11
+ from autobyteus.utils.gemini_helper import initialize_gemini_client_with_runtime
12
+ from autobyteus.utils.gemini_model_mapping import resolve_model_for_runtime
13
+ from autobyteus.llm.converters import convert_gemini_tool_calls
14
+ from autobyteus.llm.prompt_renderers.gemini_prompt_renderer import GeminiPromptRenderer
13
15
 
14
16
  logger = logging.getLogger(__name__)
15
17
 
16
- def _format_gemini_history(messages: List[Message]) -> List[Dict[str, Any]]:
17
- """Formats internal message history for the Gemini API."""
18
- history = []
19
- # System message is handled separately in the new API
20
- for msg in messages:
21
- if msg.role in [MessageRole.USER, MessageRole.ASSISTANT]:
22
- # NOTE: This history conversion will need to be updated for multimodal messages
23
- role = 'model' if msg.role == MessageRole.ASSISTANT else 'user'
24
- # The `parts` must be a list of dictionaries (Part objects), not a list of strings.
25
- history.append({"role": role, "parts": [{"text": msg.content}]})
26
- return history
18
+ def _split_gemini_parts(parts: List[Any]) -> tuple[str, str]:
19
+ """Split Gemini content parts into visible text and thought summaries."""
20
+ content_segments: List[str] = []
21
+ thought_segments: List[str] = []
22
+ for part in parts or []:
23
+ text = getattr(part, "text", None)
24
+ if not text:
25
+ continue
26
+ if getattr(part, "thought", False):
27
+ thought_segments.append(text)
28
+ else:
29
+ content_segments.append(text)
30
+ return "".join(content_segments), "".join(thought_segments)
31
+
32
+
33
+ def _split_system_message(messages: List[Message]) -> tuple[Optional[str], List[Message]]:
34
+ system_parts = [msg.content for msg in messages if msg.role == MessageRole.SYSTEM and msg.content]
35
+ system_prompt = "\n".join(system_parts) if system_parts else None
36
+ remaining = [msg for msg in messages if msg.role != MessageRole.SYSTEM]
37
+ return system_prompt, remaining
38
+
27
39
 
28
40
  class GeminiLLM(BaseLLM):
29
41
  def __init__(self, model: LLMModel = None, llm_config: LLMConfig = None):
@@ -32,59 +44,84 @@ class GeminiLLM(BaseLLM):
32
44
  }
33
45
 
34
46
  if model is None:
35
- model = LLMModel['gemini-2.5-flash']
47
+ # Default to the latest low-latency Gemini LLM.
48
+ model = LLMModel['gemini-3-flash-preview']
36
49
  if llm_config is None:
37
50
  llm_config = LLMConfig()
38
51
 
39
52
  super().__init__(model=model, llm_config=llm_config)
40
- self.client = self.initialize()
41
- self.async_client = self.client.aio
42
-
43
- @classmethod
44
- def initialize(cls) -> genai.client.Client:
45
- api_key = os.environ.get("GEMINI_API_KEY")
46
- if not api_key:
47
- logger.error("GEMINI_API_KEY environment variable is not set.")
48
- raise ValueError("GEMINI_API_KEY environment variable is not set.")
53
+
49
54
  try:
50
- return genai.Client()
55
+ self.client, self.runtime_info = initialize_gemini_client_with_runtime()
56
+ self.async_client = self.client.aio
57
+ self._renderer = GeminiPromptRenderer()
51
58
  except Exception as e:
52
- logger.error(f"Failed to initialize Gemini client: {str(e)}")
53
- raise ValueError(f"Failed to initialize Gemini client: {str(e)}")
59
+ # Re-raise or handle initialization errors specifically for the LLM context if needed
60
+ logger.error(f"Failed to initialize Gemini LLM: {str(e)}")
61
+ raise
54
62
 
55
- def _get_generation_config(self) -> genai_types.GenerateContentConfig:
63
+ def _get_generation_config(self, system_prompt: Optional[str] = None) -> genai_types.GenerateContentConfig:
56
64
  """Builds the generation config, handling special cases like 'thinking'."""
57
65
  config = self.generation_config_dict.copy()
58
66
 
59
- thinking_config = None
60
- if "flash" in self.model.value:
61
- thinking_config = genai_types.ThinkingConfig(thinking_budget=0)
67
+ # Map thinking_level to token budget
68
+ # Values based on Gemini 3 API recommendations
69
+ THINKING_LEVEL_BUDGETS = {
70
+ "minimal": 0,
71
+ "low": 1024,
72
+ "medium": 4096,
73
+ "high": 16384,
74
+ }
62
75
 
63
- # System instruction is now part of the config
64
- system_instruction = self.system_message if self.system_message else None
76
+ # Read thinking_level from extra_params (set by user config)
77
+ # Default to "minimal" (0 tokens) for backward compatibility
78
+ thinking_level = self.config.extra_params.get("thinking_level", "minimal")
79
+ thinking_budget = THINKING_LEVEL_BUDGETS.get(thinking_level, 0)
65
80
 
81
+ include_thoughts = self.config.extra_params.get("include_thoughts", False)
82
+ if not isinstance(include_thoughts, bool):
83
+ include_thoughts = False
84
+ thinking_config = genai_types.ThinkingConfig(
85
+ thinking_budget=thinking_budget,
86
+ include_thoughts=include_thoughts
87
+ )
88
+
89
+ # System instruction is now part of the config
90
+ system_instruction = system_prompt if system_prompt is not None else self.system_message
91
+
66
92
  return genai_types.GenerateContentConfig(
67
93
  **config,
68
94
  thinking_config=thinking_config,
69
95
  system_instruction=system_instruction
70
96
  )
71
97
 
72
- async def _send_user_message_to_llm(self, user_message: LLMUserMessage, **kwargs) -> CompleteResponse:
73
- self.add_user_message(user_message)
74
-
98
+ async def _send_messages_to_llm(self, messages: List[Message], **kwargs) -> CompleteResponse:
75
99
  try:
76
- history = _format_gemini_history(self.messages)
77
- generation_config = self._get_generation_config()
100
+ system_prompt, non_system = _split_system_message(messages)
101
+ history = await self._renderer.render(non_system)
102
+ generation_config = self._get_generation_config(system_prompt=system_prompt)
78
103
 
104
+ # FIX: Removed 'models/' prefix to support Vertex AI
105
+ runtime_adjusted_model = resolve_model_for_runtime(
106
+ self.model.value,
107
+ modality="llm",
108
+ runtime=getattr(self, "runtime_info", None) and self.runtime_info.runtime,
109
+ )
79
110
  response = await self.async_client.models.generate_content(
80
- model=f"models/{self.model.value}",
111
+ model=runtime_adjusted_model,
81
112
  contents=history,
82
113
  config=generation_config,
83
114
  )
84
115
 
85
- assistant_message = response.text
86
- self.add_assistant_message(assistant_message)
87
-
116
+ assistant_message = response.text or ""
117
+ reasoning_summary = None
118
+ if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
119
+ parsed_text, parsed_thoughts = _split_gemini_parts(response.candidates[0].content.parts)
120
+ if parsed_text:
121
+ assistant_message = parsed_text
122
+ if parsed_thoughts:
123
+ reasoning_summary = parsed_thoughts
124
+
88
125
  token_usage = TokenUsage(
89
126
  prompt_tokens=0,
90
127
  completion_tokens=0,
@@ -93,35 +130,102 @@ class GeminiLLM(BaseLLM):
93
130
 
94
131
  return CompleteResponse(
95
132
  content=assistant_message,
133
+ reasoning=reasoning_summary,
96
134
  usage=token_usage
97
135
  )
98
136
  except Exception as e:
99
137
  logger.error(f"Error in Gemini API call: {str(e)}")
100
138
  raise ValueError(f"Error in Gemini API call: {str(e)}")
101
139
 
102
- async def _stream_user_message_to_llm(self, user_message: LLMUserMessage, **kwargs) -> AsyncGenerator[ChunkResponse, None]:
103
- self.add_user_message(user_message)
140
+ async def _stream_messages_to_llm(self, messages: List[Message], **kwargs) -> AsyncGenerator[ChunkResponse, None]:
104
141
  complete_response = ""
142
+ complete_reasoning = ""
143
+
144
+ # Extract tools if provided
145
+ tools = kwargs.get("tools")
105
146
 
106
147
  try:
107
- history = _format_gemini_history(self.messages)
108
- generation_config = self._get_generation_config()
148
+ system_prompt, non_system = _split_system_message(messages)
149
+ history = await self._renderer.render(non_system)
150
+ generation_config = self._get_generation_config(system_prompt=system_prompt)
151
+
152
+ # Add tools to config if present
153
+ # Note: In google.genai, tools can be passed in config
154
+ if tools:
155
+ # Auto-wrap tools if they appear to be raw function declarations
156
+ if isinstance(tools, list) and len(tools) > 0:
157
+ first_tool = tools[0]
158
+ # Check if it's a raw declaration (dict with name/description) but NOT a wrapper (dict with function_declarations)
159
+ if isinstance(first_tool, dict):
160
+ is_declaration = "name" in first_tool and "description" in first_tool
161
+ is_wrapper = "function_declarations" in first_tool
162
+
163
+ if is_declaration and not is_wrapper:
164
+ # Wrap the list of declarations into a single Tool structure
165
+ tools = [{"function_declarations": tools}]
109
166
 
110
- response_stream = await self.async_client.models.generate_content_stream(
111
- model=f"models/{self.model.value}",
112
- contents=history,
113
- config=generation_config,
167
+ try:
168
+ generation_config.tools = tools
169
+ except Exception:
170
+ # Fallback or strict strict typing issues
171
+ pass
172
+
173
+ # FIX: Removed 'models/' prefix to support Vertex AI
174
+ runtime_adjusted_model = resolve_model_for_runtime(
175
+ self.model.value,
176
+ modality="llm",
177
+ runtime=getattr(self, "runtime_info", None) and self.runtime_info.runtime,
114
178
  )
179
+
180
+ # Prepare call args
181
+ call_kwargs = {
182
+ "model": runtime_adjusted_model,
183
+ "contents": history,
184
+ "config": generation_config,
185
+ }
186
+ # If explicit tools argument is needed and not supported in config for this SDK version:
187
+ # call_kwargs['tools'] = tools
188
+ # But usually config holds it.
189
+
190
+ response_stream = await self.async_client.models.generate_content_stream(**call_kwargs)
115
191
 
116
192
  async for chunk in response_stream:
117
- chunk_text = chunk.text
118
- complete_response += chunk_text
119
- yield ChunkResponse(
120
- content=chunk_text,
121
- is_complete=False
122
- )
123
-
124
- self.add_assistant_message(complete_response)
193
+ handled_parts = False
194
+ if chunk.candidates and chunk.candidates[0].content and chunk.candidates[0].content.parts:
195
+ handled_parts = True
196
+ for part in chunk.candidates[0].content.parts:
197
+ part_text = getattr(part, "text", None)
198
+ if part_text:
199
+ if getattr(part, "thought", False):
200
+ complete_reasoning += part_text
201
+ yield ChunkResponse(
202
+ content="",
203
+ reasoning=part_text,
204
+ is_complete=False
205
+ )
206
+ else:
207
+ complete_response += part_text
208
+ yield ChunkResponse(
209
+ content=part_text,
210
+ is_complete=False
211
+ )
212
+
213
+ tool_calls = convert_gemini_tool_calls(part)
214
+ if tool_calls:
215
+ yield ChunkResponse(
216
+ content="",
217
+ tool_calls=tool_calls,
218
+ is_complete=False
219
+ )
220
+
221
+ if not handled_parts:
222
+ chunk_text = chunk.text
223
+ if chunk_text:
224
+ complete_response += chunk_text
225
+ yield ChunkResponse(
226
+ content=chunk_text,
227
+ is_complete=False
228
+ )
125
229
 
126
230
  token_usage = TokenUsage(
127
231
  prompt_tokens=0,
@@ -10,7 +10,7 @@ class GrokLLM(OpenAICompatibleLLM):
10
10
  def __init__(self, model: LLMModel = None, llm_config: LLMConfig = None):
11
11
  # Provide defaults if not specified
12
12
  if model is None:
13
- model = LLMModel['grok-2-1212']
13
+ model = LLMModel['grok-4-1-fast-reasoning']
14
14
  if llm_config is None:
15
15
  llm_config = LLMConfig()
16
16