autobyteus 1.1.3__py3-none-any.whl → 1.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. autobyteus/agent/agent.py +1 -1
  2. autobyteus/agent/bootstrap_steps/system_prompt_processing_step.py +4 -2
  3. autobyteus/agent/context/__init__.py +4 -2
  4. autobyteus/agent/context/agent_config.py +35 -8
  5. autobyteus/agent/context/agent_context_registry.py +73 -0
  6. autobyteus/agent/events/notifiers.py +4 -0
  7. autobyteus/agent/events/worker_event_dispatcher.py +1 -2
  8. autobyteus/agent/handlers/inter_agent_message_event_handler.py +8 -3
  9. autobyteus/agent/handlers/llm_complete_response_received_event_handler.py +19 -19
  10. autobyteus/agent/handlers/llm_user_message_ready_event_handler.py +2 -2
  11. autobyteus/agent/handlers/tool_result_event_handler.py +48 -20
  12. autobyteus/agent/handlers/user_input_message_event_handler.py +16 -1
  13. autobyteus/agent/input_processor/__init__.py +1 -7
  14. autobyteus/agent/message/context_file_type.py +6 -0
  15. autobyteus/agent/message/send_message_to.py +74 -99
  16. autobyteus/agent/phases/discover.py +2 -1
  17. autobyteus/agent/runtime/agent_runtime.py +10 -2
  18. autobyteus/agent/runtime/agent_worker.py +1 -0
  19. autobyteus/agent/sender_type.py +15 -0
  20. autobyteus/agent/streaming/agent_event_stream.py +6 -0
  21. autobyteus/agent/streaming/stream_event_payloads.py +12 -0
  22. autobyteus/agent/streaming/stream_events.py +3 -0
  23. autobyteus/agent/system_prompt_processor/tool_manifest_injector_processor.py +7 -4
  24. autobyteus/agent/tool_execution_result_processor/__init__.py +9 -0
  25. autobyteus/agent/tool_execution_result_processor/base_processor.py +46 -0
  26. autobyteus/agent/tool_execution_result_processor/processor_definition.py +36 -0
  27. autobyteus/agent/tool_execution_result_processor/processor_meta.py +36 -0
  28. autobyteus/agent/tool_execution_result_processor/processor_registry.py +70 -0
  29. autobyteus/agent/workspace/base_workspace.py +17 -2
  30. autobyteus/agent_team/__init__.py +1 -0
  31. autobyteus/agent_team/agent_team.py +93 -0
  32. autobyteus/agent_team/agent_team_builder.py +184 -0
  33. autobyteus/agent_team/base_agent_team.py +86 -0
  34. autobyteus/agent_team/bootstrap_steps/__init__.py +24 -0
  35. autobyteus/agent_team/bootstrap_steps/agent_configuration_preparation_step.py +73 -0
  36. autobyteus/agent_team/bootstrap_steps/agent_team_bootstrapper.py +54 -0
  37. autobyteus/agent_team/bootstrap_steps/agent_team_runtime_queue_initialization_step.py +25 -0
  38. autobyteus/agent_team/bootstrap_steps/base_agent_team_bootstrap_step.py +23 -0
  39. autobyteus/agent_team/bootstrap_steps/coordinator_initialization_step.py +41 -0
  40. autobyteus/agent_team/bootstrap_steps/coordinator_prompt_preparation_step.py +85 -0
  41. autobyteus/agent_team/bootstrap_steps/task_notifier_initialization_step.py +51 -0
  42. autobyteus/agent_team/bootstrap_steps/team_context_initialization_step.py +45 -0
  43. autobyteus/agent_team/context/__init__.py +17 -0
  44. autobyteus/agent_team/context/agent_team_config.py +33 -0
  45. autobyteus/agent_team/context/agent_team_context.py +61 -0
  46. autobyteus/agent_team/context/agent_team_runtime_state.py +56 -0
  47. autobyteus/agent_team/context/team_manager.py +147 -0
  48. autobyteus/agent_team/context/team_node_config.py +76 -0
  49. autobyteus/agent_team/events/__init__.py +29 -0
  50. autobyteus/agent_team/events/agent_team_event_dispatcher.py +39 -0
  51. autobyteus/agent_team/events/agent_team_events.py +53 -0
  52. autobyteus/agent_team/events/agent_team_input_event_queue_manager.py +21 -0
  53. autobyteus/agent_team/exceptions.py +8 -0
  54. autobyteus/agent_team/factory/__init__.py +9 -0
  55. autobyteus/agent_team/factory/agent_team_factory.py +99 -0
  56. autobyteus/agent_team/handlers/__init__.py +19 -0
  57. autobyteus/agent_team/handlers/agent_team_event_handler_registry.py +23 -0
  58. autobyteus/agent_team/handlers/base_agent_team_event_handler.py +16 -0
  59. autobyteus/agent_team/handlers/inter_agent_message_request_event_handler.py +61 -0
  60. autobyteus/agent_team/handlers/lifecycle_agent_team_event_handler.py +27 -0
  61. autobyteus/agent_team/handlers/process_user_message_event_handler.py +46 -0
  62. autobyteus/agent_team/handlers/tool_approval_team_event_handler.py +48 -0
  63. autobyteus/agent_team/phases/__init__.py +11 -0
  64. autobyteus/agent_team/phases/agent_team_operational_phase.py +19 -0
  65. autobyteus/agent_team/phases/agent_team_phase_manager.py +48 -0
  66. autobyteus/agent_team/runtime/__init__.py +13 -0
  67. autobyteus/agent_team/runtime/agent_team_runtime.py +82 -0
  68. autobyteus/agent_team/runtime/agent_team_worker.py +117 -0
  69. autobyteus/agent_team/shutdown_steps/__init__.py +17 -0
  70. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_orchestrator.py +35 -0
  71. autobyteus/agent_team/shutdown_steps/agent_team_shutdown_step.py +42 -0
  72. autobyteus/agent_team/shutdown_steps/base_agent_team_shutdown_step.py +16 -0
  73. autobyteus/agent_team/shutdown_steps/bridge_cleanup_step.py +28 -0
  74. autobyteus/agent_team/shutdown_steps/sub_team_shutdown_step.py +41 -0
  75. autobyteus/agent_team/streaming/__init__.py +26 -0
  76. autobyteus/agent_team/streaming/agent_event_bridge.py +48 -0
  77. autobyteus/agent_team/streaming/agent_event_multiplexer.py +70 -0
  78. autobyteus/agent_team/streaming/agent_team_event_notifier.py +64 -0
  79. autobyteus/agent_team/streaming/agent_team_event_stream.py +33 -0
  80. autobyteus/agent_team/streaming/agent_team_stream_event_payloads.py +32 -0
  81. autobyteus/agent_team/streaming/agent_team_stream_events.py +56 -0
  82. autobyteus/agent_team/streaming/team_event_bridge.py +50 -0
  83. autobyteus/agent_team/task_notification/__init__.py +11 -0
  84. autobyteus/agent_team/task_notification/system_event_driven_agent_task_notifier.py +164 -0
  85. autobyteus/agent_team/task_notification/task_notification_mode.py +24 -0
  86. autobyteus/agent_team/utils/__init__.py +9 -0
  87. autobyteus/agent_team/utils/wait_for_idle.py +46 -0
  88. autobyteus/cli/__init__.py +1 -1
  89. autobyteus/cli/agent_team_tui/__init__.py +4 -0
  90. autobyteus/cli/agent_team_tui/app.py +210 -0
  91. autobyteus/cli/agent_team_tui/state.py +180 -0
  92. autobyteus/cli/agent_team_tui/widgets/__init__.py +6 -0
  93. autobyteus/cli/agent_team_tui/widgets/agent_list_sidebar.py +149 -0
  94. autobyteus/cli/agent_team_tui/widgets/focus_pane.py +320 -0
  95. autobyteus/cli/agent_team_tui/widgets/logo.py +20 -0
  96. autobyteus/cli/agent_team_tui/widgets/renderables.py +77 -0
  97. autobyteus/cli/agent_team_tui/widgets/shared.py +60 -0
  98. autobyteus/cli/agent_team_tui/widgets/status_bar.py +14 -0
  99. autobyteus/cli/agent_team_tui/widgets/task_board_panel.py +82 -0
  100. autobyteus/cli/cli_display.py +1 -1
  101. autobyteus/cli/workflow_tui/__init__.py +4 -0
  102. autobyteus/cli/workflow_tui/app.py +210 -0
  103. autobyteus/cli/workflow_tui/state.py +189 -0
  104. autobyteus/cli/workflow_tui/widgets/__init__.py +6 -0
  105. autobyteus/cli/workflow_tui/widgets/agent_list_sidebar.py +149 -0
  106. autobyteus/cli/workflow_tui/widgets/focus_pane.py +335 -0
  107. autobyteus/cli/workflow_tui/widgets/logo.py +27 -0
  108. autobyteus/cli/workflow_tui/widgets/renderables.py +70 -0
  109. autobyteus/cli/workflow_tui/widgets/shared.py +51 -0
  110. autobyteus/cli/workflow_tui/widgets/status_bar.py +14 -0
  111. autobyteus/events/event_types.py +8 -0
  112. autobyteus/llm/api/autobyteus_llm.py +11 -12
  113. autobyteus/llm/api/lmstudio_llm.py +34 -0
  114. autobyteus/llm/api/ollama_llm.py +8 -13
  115. autobyteus/llm/api/openai_compatible_llm.py +20 -3
  116. autobyteus/llm/autobyteus_provider.py +73 -46
  117. autobyteus/llm/llm_factory.py +103 -139
  118. autobyteus/llm/lmstudio_provider.py +104 -0
  119. autobyteus/llm/models.py +83 -53
  120. autobyteus/llm/ollama_provider.py +69 -61
  121. autobyteus/llm/ollama_provider_resolver.py +1 -0
  122. autobyteus/llm/providers.py +13 -12
  123. autobyteus/llm/runtimes.py +11 -0
  124. autobyteus/llm/token_counter/token_counter_factory.py +2 -0
  125. autobyteus/task_management/__init__.py +43 -0
  126. autobyteus/task_management/base_task_board.py +68 -0
  127. autobyteus/task_management/converters/__init__.py +11 -0
  128. autobyteus/task_management/converters/task_board_converter.py +64 -0
  129. autobyteus/task_management/converters/task_plan_converter.py +48 -0
  130. autobyteus/task_management/deliverable.py +16 -0
  131. autobyteus/task_management/deliverables/__init__.py +8 -0
  132. autobyteus/task_management/deliverables/file_deliverable.py +15 -0
  133. autobyteus/task_management/events.py +27 -0
  134. autobyteus/task_management/in_memory_task_board.py +126 -0
  135. autobyteus/task_management/schemas/__init__.py +15 -0
  136. autobyteus/task_management/schemas/deliverable_schema.py +13 -0
  137. autobyteus/task_management/schemas/plan_definition.py +35 -0
  138. autobyteus/task_management/schemas/task_status_report.py +27 -0
  139. autobyteus/task_management/task_plan.py +110 -0
  140. autobyteus/task_management/tools/__init__.py +14 -0
  141. autobyteus/task_management/tools/get_task_board_status.py +68 -0
  142. autobyteus/task_management/tools/publish_task_plan.py +113 -0
  143. autobyteus/task_management/tools/update_task_status.py +135 -0
  144. autobyteus/tools/__init__.py +2 -0
  145. autobyteus/tools/ask_user_input.py +2 -1
  146. autobyteus/tools/bash/bash_executor.py +61 -15
  147. autobyteus/tools/browser/session_aware/browser_session_aware_navigate_to.py +2 -0
  148. autobyteus/tools/browser/session_aware/browser_session_aware_web_element_trigger.py +3 -0
  149. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_reader.py +3 -0
  150. autobyteus/tools/browser/session_aware/browser_session_aware_webpage_screenshot_taker.py +3 -0
  151. autobyteus/tools/browser/standalone/google_search_ui.py +2 -0
  152. autobyteus/tools/browser/standalone/navigate_to.py +2 -0
  153. autobyteus/tools/browser/standalone/web_page_pdf_generator.py +3 -0
  154. autobyteus/tools/browser/standalone/webpage_image_downloader.py +3 -0
  155. autobyteus/tools/browser/standalone/webpage_reader.py +2 -0
  156. autobyteus/tools/browser/standalone/webpage_screenshot_taker.py +3 -0
  157. autobyteus/tools/file/file_reader.py +36 -9
  158. autobyteus/tools/file/file_writer.py +37 -9
  159. autobyteus/tools/functional_tool.py +5 -4
  160. autobyteus/tools/image_downloader.py +2 -0
  161. autobyteus/tools/mcp/config_service.py +63 -58
  162. autobyteus/tools/mcp/server/http_managed_mcp_server.py +14 -2
  163. autobyteus/tools/mcp/server/stdio_managed_mcp_server.py +14 -2
  164. autobyteus/tools/mcp/server_instance_manager.py +30 -4
  165. autobyteus/tools/mcp/tool_registrar.py +106 -51
  166. autobyteus/tools/parameter_schema.py +17 -11
  167. autobyteus/tools/pdf_downloader.py +2 -1
  168. autobyteus/tools/registry/tool_definition.py +36 -37
  169. autobyteus/tools/registry/tool_registry.py +50 -2
  170. autobyteus/tools/timer.py +2 -0
  171. autobyteus/tools/tool_category.py +15 -4
  172. autobyteus/tools/tool_meta.py +6 -1
  173. autobyteus/tools/tool_origin.py +10 -0
  174. autobyteus/tools/usage/formatters/default_json_example_formatter.py +78 -3
  175. autobyteus/tools/usage/formatters/default_xml_example_formatter.py +23 -3
  176. autobyteus/tools/usage/formatters/gemini_json_example_formatter.py +6 -0
  177. autobyteus/tools/usage/formatters/google_json_example_formatter.py +7 -0
  178. autobyteus/tools/usage/formatters/openai_json_example_formatter.py +6 -4
  179. autobyteus/tools/usage/parsers/gemini_json_tool_usage_parser.py +23 -7
  180. autobyteus/tools/usage/parsers/provider_aware_tool_usage_parser.py +14 -25
  181. autobyteus/tools/usage/providers/__init__.py +2 -12
  182. autobyteus/tools/usage/providers/tool_manifest_provider.py +36 -29
  183. autobyteus/tools/usage/registries/__init__.py +7 -12
  184. autobyteus/tools/usage/registries/tool_formatter_pair.py +15 -0
  185. autobyteus/tools/usage/registries/tool_formatting_registry.py +58 -0
  186. autobyteus/tools/usage/registries/tool_usage_parser_registry.py +55 -0
  187. autobyteus/workflow/agentic_workflow.py +93 -0
  188. autobyteus/{agent/workflow → workflow}/base_agentic_workflow.py +19 -27
  189. autobyteus/workflow/bootstrap_steps/__init__.py +20 -0
  190. autobyteus/workflow/bootstrap_steps/agent_tool_injection_step.py +34 -0
  191. autobyteus/workflow/bootstrap_steps/base_workflow_bootstrap_step.py +23 -0
  192. autobyteus/workflow/bootstrap_steps/coordinator_initialization_step.py +41 -0
  193. autobyteus/workflow/bootstrap_steps/coordinator_prompt_preparation_step.py +108 -0
  194. autobyteus/workflow/bootstrap_steps/workflow_bootstrapper.py +50 -0
  195. autobyteus/workflow/bootstrap_steps/workflow_runtime_queue_initialization_step.py +25 -0
  196. autobyteus/workflow/context/__init__.py +17 -0
  197. autobyteus/workflow/context/team_manager.py +147 -0
  198. autobyteus/workflow/context/workflow_config.py +30 -0
  199. autobyteus/workflow/context/workflow_context.py +61 -0
  200. autobyteus/workflow/context/workflow_node_config.py +76 -0
  201. autobyteus/workflow/context/workflow_runtime_state.py +53 -0
  202. autobyteus/workflow/events/__init__.py +29 -0
  203. autobyteus/workflow/events/workflow_event_dispatcher.py +39 -0
  204. autobyteus/workflow/events/workflow_events.py +53 -0
  205. autobyteus/workflow/events/workflow_input_event_queue_manager.py +21 -0
  206. autobyteus/workflow/exceptions.py +8 -0
  207. autobyteus/workflow/factory/__init__.py +9 -0
  208. autobyteus/workflow/factory/workflow_factory.py +99 -0
  209. autobyteus/workflow/handlers/__init__.py +19 -0
  210. autobyteus/workflow/handlers/base_workflow_event_handler.py +16 -0
  211. autobyteus/workflow/handlers/inter_agent_message_request_event_handler.py +61 -0
  212. autobyteus/workflow/handlers/lifecycle_workflow_event_handler.py +27 -0
  213. autobyteus/workflow/handlers/process_user_message_event_handler.py +46 -0
  214. autobyteus/workflow/handlers/tool_approval_workflow_event_handler.py +39 -0
  215. autobyteus/workflow/handlers/workflow_event_handler_registry.py +23 -0
  216. autobyteus/workflow/phases/__init__.py +11 -0
  217. autobyteus/workflow/phases/workflow_operational_phase.py +19 -0
  218. autobyteus/workflow/phases/workflow_phase_manager.py +48 -0
  219. autobyteus/workflow/runtime/__init__.py +13 -0
  220. autobyteus/workflow/runtime/workflow_runtime.py +82 -0
  221. autobyteus/workflow/runtime/workflow_worker.py +117 -0
  222. autobyteus/workflow/shutdown_steps/__init__.py +17 -0
  223. autobyteus/workflow/shutdown_steps/agent_team_shutdown_step.py +42 -0
  224. autobyteus/workflow/shutdown_steps/base_workflow_shutdown_step.py +16 -0
  225. autobyteus/workflow/shutdown_steps/bridge_cleanup_step.py +28 -0
  226. autobyteus/workflow/shutdown_steps/sub_workflow_shutdown_step.py +41 -0
  227. autobyteus/workflow/shutdown_steps/workflow_shutdown_orchestrator.py +35 -0
  228. autobyteus/workflow/streaming/__init__.py +26 -0
  229. autobyteus/workflow/streaming/agent_event_bridge.py +48 -0
  230. autobyteus/workflow/streaming/agent_event_multiplexer.py +70 -0
  231. autobyteus/workflow/streaming/workflow_event_bridge.py +50 -0
  232. autobyteus/workflow/streaming/workflow_event_notifier.py +83 -0
  233. autobyteus/workflow/streaming/workflow_event_stream.py +33 -0
  234. autobyteus/workflow/streaming/workflow_stream_event_payloads.py +28 -0
  235. autobyteus/workflow/streaming/workflow_stream_events.py +45 -0
  236. autobyteus/workflow/utils/__init__.py +9 -0
  237. autobyteus/workflow/utils/wait_for_idle.py +46 -0
  238. autobyteus/workflow/workflow_builder.py +151 -0
  239. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/METADATA +16 -14
  240. autobyteus-1.1.5.dist-info/RECORD +455 -0
  241. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/top_level.txt +1 -0
  242. examples/__init__.py +1 -0
  243. examples/agent_team/__init__.py +1 -0
  244. examples/discover_phase_transitions.py +104 -0
  245. examples/run_browser_agent.py +262 -0
  246. examples/run_google_slides_agent.py +287 -0
  247. examples/run_mcp_browser_client.py +174 -0
  248. examples/run_mcp_google_slides_client.py +270 -0
  249. examples/run_mcp_list_tools.py +189 -0
  250. examples/run_poem_writer.py +284 -0
  251. examples/run_sqlite_agent.py +295 -0
  252. autobyteus/agent/context/agent_phase_manager.py +0 -264
  253. autobyteus/agent/context/phases.py +0 -49
  254. autobyteus/agent/group/__init__.py +0 -0
  255. autobyteus/agent/group/agent_group.py +0 -164
  256. autobyteus/agent/group/agent_group_context.py +0 -81
  257. autobyteus/agent/input_processor/content_prefixing_input_processor.py +0 -41
  258. autobyteus/agent/input_processor/metadata_appending_input_processor.py +0 -34
  259. autobyteus/agent/input_processor/passthrough_input_processor.py +0 -33
  260. autobyteus/agent/workflow/__init__.py +0 -11
  261. autobyteus/agent/workflow/agentic_workflow.py +0 -89
  262. autobyteus/tools/mcp/call_handlers/__init__.py +0 -16
  263. autobyteus/tools/mcp/call_handlers/base_handler.py +0 -40
  264. autobyteus/tools/mcp/call_handlers/stdio_handler.py +0 -76
  265. autobyteus/tools/mcp/call_handlers/streamable_http_handler.py +0 -55
  266. autobyteus/tools/mcp/registrar.py +0 -202
  267. autobyteus/tools/usage/providers/json_example_provider.py +0 -32
  268. autobyteus/tools/usage/providers/json_schema_provider.py +0 -35
  269. autobyteus/tools/usage/providers/json_tool_usage_parser_provider.py +0 -28
  270. autobyteus/tools/usage/providers/xml_example_provider.py +0 -28
  271. autobyteus/tools/usage/providers/xml_schema_provider.py +0 -29
  272. autobyteus/tools/usage/providers/xml_tool_usage_parser_provider.py +0 -26
  273. autobyteus/tools/usage/registries/json_example_formatter_registry.py +0 -51
  274. autobyteus/tools/usage/registries/json_schema_formatter_registry.py +0 -51
  275. autobyteus/tools/usage/registries/json_tool_usage_parser_registry.py +0 -42
  276. autobyteus/tools/usage/registries/xml_example_formatter_registry.py +0 -30
  277. autobyteus/tools/usage/registries/xml_schema_formatter_registry.py +0 -33
  278. autobyteus/tools/usage/registries/xml_tool_usage_parser_registry.py +0 -30
  279. autobyteus/workflow/simple_task.py +0 -98
  280. autobyteus/workflow/task.py +0 -147
  281. autobyteus/workflow/workflow.py +0 -49
  282. autobyteus-1.1.3.dist-info/RECORD +0 -312
  283. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/WHEEL +0 -0
  284. {autobyteus-1.1.3.dist-info → autobyteus-1.1.5.dist-info}/licenses/LICENSE +0 -0
@@ -34,6 +34,7 @@ class EventType(Enum):
34
34
  AGENT_DATA_ASSISTANT_COMPLETE_RESPONSE = "agent_data_assistant_complete_response"
35
35
  AGENT_DATA_TOOL_LOG = "agent_data_tool_log"
36
36
  AGENT_DATA_TOOL_LOG_STREAM_END = "agent_data_tool_log_stream_end"
37
+ AGENT_DATA_SYSTEM_TASK_NOTIFICATION_RECEIVED = "agent_data_system_task_notification_received" # NEW
37
38
 
38
39
  # --- Agent Requests for External Interaction ---
39
40
  AGENT_REQUEST_TOOL_INVOCATION_APPROVAL = "agent_request_tool_invocation_approval"
@@ -42,5 +43,12 @@ class EventType(Enum):
42
43
  # --- Agent Errors (not necessarily phase changes, e.g., error during output generation) ---
43
44
  AGENT_ERROR_OUTPUT_GENERATION = "agent_error_output_generation"
44
45
 
46
+ # --- Agent Team Events ---
47
+ TEAM_STREAM_EVENT = "team_stream_event" # For unified agent team event stream
48
+
49
+ # --- Task Board Events ---
50
+ TASK_BOARD_PLAN_PUBLISHED = "task_board_plan_published"
51
+ TASK_BOARD_STATUS_UPDATED = "task_board_status_updated"
52
+
45
53
  def __str__(self):
46
54
  return self.value
@@ -11,18 +11,17 @@ import uuid
11
11
  logger = logging.getLogger(__name__)
12
12
 
13
13
  class AutobyteusLLM(BaseLLM):
14
- def __init__(self, model: LLMModel = None, llm_config: LLMConfig = None):
15
- # Provide defaults if not specified
16
- if model is None:
17
- # This should be set by the factory/caller, but providing a fallback
18
- raise ValueError("AutobyteusLLM requires a model to be specified")
19
- if llm_config is None:
20
- llm_config = LLMConfig()
21
-
14
+ def __init__(self, model: LLMModel, llm_config: LLMConfig):
15
+ # The host URL is now passed via the model object.
16
+ if not model.host_url:
17
+ raise ValueError("AutobyteusLLM requires a host_url to be set in its LLMModel object.")
18
+
22
19
  super().__init__(model=model, llm_config=llm_config)
23
- self.client = AutobyteusClient()
20
+
21
+ # Instantiate the client with the specific host for this model.
22
+ self.client = AutobyteusClient(server_url=self.model.host_url)
24
23
  self.conversation_id = str(uuid.uuid4())
25
- logger.info(f"AutobyteusLLM initialized with model: {self.model} and conversation ID: {self.conversation_id}")
24
+ logger.info(f"AutobyteusLLM initialized for model '{self.model.model_identifier}' with conversation ID: {self.conversation_id}")
26
25
 
27
26
  async def _send_user_message_to_llm(
28
27
  self,
@@ -34,7 +33,7 @@ class AutobyteusLLM(BaseLLM):
34
33
  try:
35
34
  response = await self.client.send_message(
36
35
  conversation_id=self.conversation_id,
37
- model_name=self.model.name,
36
+ model_name=self.model.name, # Use `name` as it's the original model name for the API
38
37
  user_message=user_message,
39
38
  image_urls=image_urls
40
39
  )
@@ -70,7 +69,7 @@ class AutobyteusLLM(BaseLLM):
70
69
  try:
71
70
  async for chunk in self.client.stream_message(
72
71
  conversation_id=self.conversation_id,
73
- model_name=self.model.name,
72
+ model_name=self.model.name, # Use `name` for the API call
74
73
  user_message=user_message,
75
74
  image_urls=image_urls
76
75
  ):
@@ -0,0 +1,34 @@
1
+ import logging
2
+ from autobyteus.llm.models import LLMModel
3
+ from autobyteus.llm.utils.llm_config import LLMConfig
4
+ from autobyteus.llm.api.openai_compatible_llm import OpenAICompatibleLLM
5
+ import os
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class LMStudioLLM(OpenAICompatibleLLM):
10
+ """
11
+ LLM class for models served by a local LM Studio instance.
12
+ This class is now decoupled from environment variables and receives its connection
13
+ details from the LLMModel object.
14
+ """
15
+
16
+ def __init__(self, model: LLMModel, llm_config: LLMConfig):
17
+ if not model.host_url:
18
+ raise ValueError("LMStudioLLM requires a host_url to be set in its LLMModel object.")
19
+
20
+ base_url = f"{model.host_url}/v1"
21
+
22
+ # The API key is often not needed for LM Studio, but we allow it to be set via env var.
23
+ # It defaults to a dummy value if not set.
24
+ super().__init__(
25
+ model=model,
26
+ llm_config=llm_config,
27
+ api_key_env_var="LMSTUDIO_API_KEY",
28
+ base_url=base_url,
29
+ api_key_default="lm-studio" # Dummy key for LM Studio
30
+ )
31
+ logger.info(f"LMStudioLLM initialized for model '{model.model_identifier}' with base URL: {base_url}")
32
+
33
+ async def cleanup(self):
34
+ await super().cleanup()
@@ -14,22 +14,17 @@ import os
14
14
  logger = logging.getLogger(__name__)
15
15
 
16
16
  class OllamaLLM(BaseLLM):
17
- DEFAULT_OLLAMA_HOST = 'http://localhost:11434'
18
-
19
- def __init__(self, model: LLMModel = None, llm_config: LLMConfig = None):
20
- self.ollama_host = os.getenv('DEFAULT_OLLAMA_HOST', self.DEFAULT_OLLAMA_HOST)
21
- logger.info(f"Initializing Ollama with host: {self.ollama_host}")
17
+ def __init__(self, model: LLMModel, llm_config: LLMConfig):
18
+ # The host URL is now passed via the model object, decoupling from environment variables here.
19
+ if not model.host_url:
20
+ raise ValueError("OllamaLLM requires a host_url to be set in its LLMModel object.")
21
+
22
+ logger.info(f"Initializing OllamaLLM for model '{model.name}' with host: {model.host_url}")
22
23
 
23
- self.client = AsyncClient(host=self.ollama_host)
24
+ self.client = AsyncClient(host=model.host_url)
24
25
 
25
- # Provide defaults if not specified
26
- if model is None:
27
- model = LLMModel.OLLAMA_LLAMA_3_2
28
- if llm_config is None:
29
- llm_config = LLMConfig()
30
-
31
26
  super().__init__(model=model, llm_config=llm_config)
32
- logger.info(f"OllamaLLM initialized with model: {self.model}")
27
+ logger.info(f"OllamaLLM initialized with model: {self.model.model_identifier}")
33
28
 
34
29
  async def _send_user_message_to_llm(self, user_message: str, image_urls: Optional[List[str]] = None, **kwargs) -> CompleteResponse:
35
30
  self.add_user_message(user_message)
@@ -21,12 +21,29 @@ class OpenAICompatibleLLM(BaseLLM, ABC):
21
21
  model: LLMModel,
22
22
  llm_config: LLMConfig,
23
23
  api_key_env_var: str,
24
- base_url: str
24
+ base_url: str,
25
+ api_key_default: Optional[str] = None
25
26
  ):
27
+ """
28
+ Initializes an OpenAI-compatible LLM.
29
+
30
+ Args:
31
+ model (LLMModel): The model to use.
32
+ llm_config (LLMConfig): Configuration for the LLM.
33
+ api_key_env_var (str): The name of the environment variable for the API key.
34
+ base_url (str): The base URL for the API.
35
+ api_key_default (Optional[str], optional): A default API key to use if the
36
+ environment variable is not set.
37
+ Defaults to None.
38
+ """
26
39
  api_key = os.getenv(api_key_env_var)
27
40
  if not api_key:
28
- logger.error(f"{api_key_env_var} environment variable is not set.")
29
- raise ValueError(f"{api_key_env_var} environment variable is not set.")
41
+ if api_key_default:
42
+ api_key = api_key_default
43
+ logger.info(f"{api_key_env_var} not set, using default key.")
44
+ else:
45
+ logger.error(f"{api_key_env_var} environment variable is not set.")
46
+ raise ValueError(f"{api_key_env_var} environment variable is not set.")
30
47
 
31
48
  self.client = OpenAI(api_key=api_key, base_url=base_url)
32
49
  logger.info(f"Initialized OpenAI compatible client with base_url: {base_url}")
@@ -1,8 +1,9 @@
1
1
  from autobyteus.llm.api.autobyteus_llm import AutobyteusLLM
2
2
  from autobyteus.llm.models import LLMModel
3
3
  from autobyteus.llm.providers import LLMProvider
4
+ from autobyteus.llm.runtimes import LLMRuntime
4
5
  from autobyteus.llm.utils.llm_config import LLMConfig, TokenPricingConfig
5
- from typing import Dict, Any, TYPE_CHECKING
6
+ from typing import Dict, Any, TYPE_CHECKING, List, Optional
6
7
  import os
7
8
  import logging
8
9
  from urllib.parse import urlparse
@@ -15,63 +16,89 @@ logger = logging.getLogger(__name__)
15
16
 
16
17
  class AutobyteusModelProvider:
17
18
  DEFAULT_SERVER_URL = 'https://localhost:8000'
18
- CONNECTION_TIMEOUT = 5.0
19
+
20
+ @staticmethod
21
+ def _get_hosts() -> List[str]:
22
+ """Gets Autobyteus LLM server hosts from env vars, supporting a comma-separated list."""
23
+ hosts_str = os.getenv('AUTOBYTEUS_LLM_SERVER_HOSTS')
24
+ if hosts_str:
25
+ return [host.strip() for host in hosts_str.split(',')]
26
+
27
+ legacy_host = os.getenv('AUTOBYTEUS_LLM_SERVER_URL')
28
+ if legacy_host:
29
+ return [legacy_host]
30
+
31
+ return [AutobyteusModelProvider.DEFAULT_SERVER_URL]
19
32
 
20
33
  @staticmethod
21
34
  def discover_and_register():
22
- """Discover and register Autobyteus models synchronously"""
35
+ """Discover and register Autobyteus models from all configured hosts."""
23
36
  try:
24
- from autobyteus.llm.llm_factory import LLMFactory # Local import to avoid circular dependency
25
-
26
- client = None
27
- try:
28
- client = AutobyteusClient()
29
- response = client.get_available_models_sync()
30
- except Exception as e:
31
- logger.error(f"Client initialization failed: {str(e)}")
32
- return
33
- finally:
34
- if client:
35
- client.sync_client.close()
37
+ from autobyteus.llm.llm_factory import LLMFactory
36
38
 
37
- if not AutobyteusModelProvider._validate_server_response(response):
38
- return
39
+ hosts = AutobyteusModelProvider._get_hosts()
40
+ total_registered_count = 0
39
41
 
40
- models = response.get('models', [])
41
- registered_count = 0
42
-
43
- for model_info in models:
42
+ for host_url in hosts:
43
+ if not AutobyteusModelProvider.is_valid_url(host_url):
44
+ logger.error(f"Invalid Autobyteus host URL: {host_url}, skipping.")
45
+ continue
46
+
47
+ logger.info(f"Discovering Autobyteus models from host: {host_url}")
48
+ client = None
44
49
  try:
45
- validation_result = AutobyteusModelProvider._validate_model_info(model_info)
46
- if not validation_result["valid"]:
47
- logger.warning(validation_result["message"])
48
- continue
49
-
50
- llm_config = AutobyteusModelProvider._parse_llm_config(model_info["config"])
51
- if not llm_config:
52
- continue
53
-
54
- llm_model = LLMModel(
55
- name=model_info["name"],
56
- value=model_info["value"],
57
- provider=LLMProvider(model_info["provider"]), # Convert string to enum
58
- llm_class=AutobyteusLLM,
59
- canonical_name=model_info["canonical_name"], # Add canonical_name
60
- default_config=llm_config
61
- )
62
-
63
- LLMFactory.register_model(llm_model)
64
- registered_count += 1
65
- logger.debug(f"Registered model: {model_info['name']} with canonical name: {model_info['canonical_name']}")
66
-
50
+ # Instantiate client for this specific host
51
+ client = AutobyteusClient(server_url=host_url)
52
+ response = client.get_available_models_sync()
67
53
  except Exception as e:
68
- logger.error(f"Model registration failed: {str(e)}")
54
+ logger.warning(f"Could not connect or fetch models from Autobyteus server at {host_url}: {e}")
55
+ continue
56
+ finally:
57
+ if client:
58
+ client.sync_client.close()
59
+
60
+ if not AutobyteusModelProvider._validate_server_response(response):
69
61
  continue
62
+
63
+ models = response.get('models', [])
64
+ host_registered_count = 0
65
+ for model_info in models:
66
+ try:
67
+ validation_result = AutobyteusModelProvider._validate_model_info(model_info)
68
+ if not validation_result["valid"]:
69
+ logger.warning(validation_result["message"])
70
+ continue
71
+
72
+ llm_config = AutobyteusModelProvider._parse_llm_config(model_info["config"])
73
+ if not llm_config:
74
+ continue
70
75
 
71
- logger.info(f"Registered {registered_count} Autobyteus models")
76
+ llm_model = LLMModel(
77
+ name=model_info["name"],
78
+ value=model_info["value"],
79
+ provider=LLMProvider(model_info["provider"]),
80
+ llm_class=AutobyteusLLM,
81
+ canonical_name=model_info["canonical_name"],
82
+ runtime=LLMRuntime.AUTOBYTEUS,
83
+ host_url=host_url,
84
+ default_config=llm_config
85
+ )
86
+
87
+ LLMFactory.register_model(llm_model)
88
+ host_registered_count += 1
89
+
90
+ except Exception as e:
91
+ logger.error(f"Failed to register Autobyteus model '{model_info.get('name')}' from {host_url}: {e}")
92
+
93
+ if host_registered_count > 0:
94
+ logger.info(f"Registered {host_registered_count} models from Autobyteus host {host_url}")
95
+ total_registered_count += host_registered_count
96
+
97
+ if total_registered_count > 0:
98
+ logger.info(f"Finished Autobyteus discovery. Total models registered: {total_registered_count}")
72
99
 
73
100
  except Exception as e:
74
- logger.error(f"Discovery failed: {str(e)}", exc_info=True)
101
+ logger.error(f"An unexpected error occurred during Autobyteus model discovery: {e}", exc_info=True)
75
102
 
76
103
  @staticmethod
77
104
  def _validate_server_response(response: Dict[str, Any]) -> bool:
@@ -5,75 +5,48 @@ import inspect
5
5
  from autobyteus.llm.autobyteus_provider import AutobyteusModelProvider
6
6
  from autobyteus.llm.models import LLMModel, ModelInfo, ProviderModelGroup
7
7
  from autobyteus.llm.providers import LLMProvider
8
+ from autobyteus.llm.runtimes import LLMRuntime
8
9
  from autobyteus.llm.utils.llm_config import LLMConfig, TokenPricingConfig
9
10
  from autobyteus.llm.base_llm import BaseLLM
10
11
 
11
12
  from autobyteus.llm.api.claude_llm import ClaudeLLM
12
13
  from autobyteus.llm.api.mistral_llm import MistralLLM
13
14
  from autobyteus.llm.api.openai_llm import OpenAILLM
14
- from autobyteus.llm.api.ollama_llm import OllamaLLM
15
15
  from autobyteus.llm.api.deepseek_llm import DeepSeekLLM
16
16
  from autobyteus.llm.api.grok_llm import GrokLLM
17
17
  from autobyteus.llm.api.kimi_llm import KimiLLM
18
18
  from autobyteus.llm.ollama_provider import OllamaModelProvider
19
+ from autobyteus.llm.lmstudio_provider import LMStudioModelProvider
19
20
  from autobyteus.utils.singleton import SingletonMeta
20
21
 
21
22
  logger = logging.getLogger(__name__)
22
23
 
23
24
  class LLMFactory(metaclass=SingletonMeta):
24
25
  _models_by_provider: Dict[LLMProvider, List[LLMModel]] = {}
26
+ _models_by_identifier: Dict[str, LLMModel] = {}
25
27
  _initialized = False
26
28
 
27
- @staticmethod
28
- def register(model: LLMModel):
29
- LLMFactory.register_model(model)
30
-
31
29
  @staticmethod
32
30
  def ensure_initialized():
33
- """
34
- Ensures the factory is initialized before use.
35
- """
31
+ """Ensures the factory is initialized before use."""
36
32
  if not LLMFactory._initialized:
37
33
  LLMFactory._initialize_registry()
38
34
  LLMFactory._initialized = True
39
35
 
40
36
  @staticmethod
41
37
  def reinitialize():
42
- """
43
- Reinitializes the model registry by resetting the initialization state
44
- and reinitializing the registry.
45
-
46
- This is useful when new provider API keys are configured and
47
- we need to discover models that might be available with the new keys.
48
-
49
- Returns:
50
- bool: True if reinitialization was successful, False otherwise.
51
- """
52
- try:
53
- logger.info("Reinitializing LLM model registry...")
54
-
55
- # Reset the initialized flag
56
- LLMFactory._initialized = False
57
-
58
- # Clear existing models registry
59
- LLMFactory._models_by_provider = {}
60
-
61
- # Reinitialize the registry
62
- LLMFactory.ensure_initialized()
63
-
64
- logger.info("LLM model registry reinitialized successfully")
65
- return True
66
- except Exception as e:
67
- logger.error(f"Failed to reinitialize LLM model registry: {str(e)}")
68
- return False
38
+ """Reinitializes the model registry."""
39
+ logger.info("Reinitializing LLM model registry...")
40
+ LLMFactory._initialized = False
41
+ LLMFactory._models_by_provider.clear()
42
+ LLMFactory._models_by_identifier.clear()
43
+ LLMFactory.ensure_initialized()
44
+ logger.info("LLM model registry reinitialized successfully.")
69
45
 
70
46
  @staticmethod
71
47
  def _initialize_registry():
72
- """
73
- Initialize the registry with supported models, discover plugins,
74
- organize models by provider, and assign models as attributes on LLMModel.
75
- """
76
- # Organize supported models by provider sections
48
+ """Initializes the registry with built-in models and discovers runtime models."""
49
+ # Hardcoded direct-API models. Runtime defaults to API.
77
50
  supported_models = [
78
51
  # OPENAI Provider Models
79
52
  LLMModel(
@@ -305,133 +278,124 @@ class LLMFactory(metaclass=SingletonMeta):
305
278
  for model in supported_models:
306
279
  LLMFactory.register_model(model)
307
280
 
281
+ # Discover models from runtimes
308
282
  OllamaModelProvider.discover_and_register()
283
+ LMStudioModelProvider.discover_and_register()
309
284
  AutobyteusModelProvider.discover_and_register()
310
285
 
311
286
  @staticmethod
312
287
  def register_model(model: LLMModel):
313
- """
314
- Register a new LLM model, storing it under its provider category.
315
- If a model with the same name already exists, it will be replaced.
316
- """
317
- # Using a flat list of all models to check for existing model by name
318
- all_models = [m for models in LLMFactory._models_by_provider.values() for m in models]
319
-
320
- for existing_model in all_models:
321
- if existing_model.name == model.name:
322
- logger.warning(f"Model with name '{model.name}' is being redefined.")
323
- # Remove the old model from its provider list
324
- LLMFactory._models_by_provider[existing_model.provider].remove(existing_model)
325
- break
288
+ """Registers a new LLM model."""
289
+ identifier = model.model_identifier
290
+ if identifier in LLMFactory._models_by_identifier:
291
+ logger.debug(f"Redefining model with identifier '{identifier}'.")
292
+ # Remove old model from provider group to replace it
293
+ old_model = LLMFactory._models_by_identifier[identifier]
294
+ if old_model.provider in LLMFactory._models_by_provider:
295
+ # This check is needed because a model might be in _models_by_identifier but not yet in _models_by_provider if re-registering
296
+ if old_model in LLMFactory._models_by_provider[old_model.provider]:
297
+ LLMFactory._models_by_provider[old_model.provider].remove(old_model)
326
298
 
327
- models = LLMFactory._models_by_provider.setdefault(model.provider, [])
328
- models.append(model)
299
+ LLMFactory._models_by_identifier[identifier] = model
300
+ LLMFactory._models_by_provider.setdefault(model.provider, []).append(model)
329
301
 
330
302
  @staticmethod
331
303
  def create_llm(model_identifier: str, llm_config: Optional[LLMConfig] = None) -> BaseLLM:
332
304
  """
333
- Create an LLM instance for the specified model identifier.
334
-
335
- Args:
336
- model_identifier (str): The model name to create an instance for.
337
- llm_config (Optional[LLMConfig]): Configuration for the LLM. If None,
338
- the model's default configuration is used.
339
-
340
- Returns:
341
- BaseLLM: An instance of the LLM.
342
-
343
- Raises:
344
- ValueError: If the model is not supported.
305
+ Creates an LLM instance for the specified unique model identifier.
306
+ Raises an error if the identifier is not found or if a non-unique name is provided.
345
307
  """
346
308
  LLMFactory.ensure_initialized()
347
- for models in LLMFactory._models_by_provider.values():
348
- for model_instance in models:
349
- if model_instance.name == model_identifier:
350
- return model_instance.create_llm(llm_config)
351
- raise ValueError(f"Unsupported model: {model_identifier}")
309
+
310
+ # First, try a direct lookup by the unique model_identifier
311
+ model = LLMFactory._models_by_identifier.get(model_identifier)
312
+ if model:
313
+ return model.create_llm(llm_config)
352
314
 
353
- @staticmethod
354
- def get_all_models() -> List[str]:
355
- """
356
- Returns a list of all registered model values.
357
- """
358
- LLMFactory.ensure_initialized()
359
- all_models = []
360
- for models in LLMFactory._models_by_provider.values():
361
- all_models.extend(model.name for model in models)
362
- return all_models
315
+ # If not found, check if the user provided a non-unique name by mistake
316
+ found_by_name = [m for m in LLMFactory._models_by_identifier.values() if m.name == model_identifier]
317
+ if len(found_by_name) > 1:
318
+ identifiers = [m.model_identifier for m in found_by_name]
319
+ raise ValueError(
320
+ f"The model name '{model_identifier}' is ambiguous. Please use one of the unique "
321
+ f"model identifiers: {identifiers}"
322
+ )
323
+
324
+ raise ValueError(f"Model with identifier '{model_identifier}' not found.")
363
325
 
364
- @staticmethod
365
- def get_all_providers() -> Set[LLMProvider]:
366
- """
367
- Returns a set of all available LLM providers.
368
- """
369
- LLMFactory.ensure_initialized()
370
- return set(LLMProvider)
326
+ # --- New Public API ---
371
327
 
372
328
  @staticmethod
373
- def get_models_by_provider(provider: LLMProvider) -> List[str]:
374
- """
375
- Returns a list of all model values for a specific provider.
376
- """
329
+ def list_available_models() -> List[ModelInfo]:
330
+ """Returns a list of all available models with their detailed info."""
377
331
  LLMFactory.ensure_initialized()
378
- return [model.value for model in LLMFactory._models_by_provider.get(provider, [])]
332
+ models = sorted(LLMFactory._models_by_identifier.values(), key=lambda m: m.model_identifier)
333
+ return [
334
+ ModelInfo(
335
+ model_identifier=m.model_identifier,
336
+ display_name=m.name,
337
+ value=m.value,
338
+ canonical_name=m.canonical_name,
339
+ provider=m.provider.value,
340
+ runtime=m.runtime.value,
341
+ host_url=m.host_url
342
+ )
343
+ for m in models
344
+ ]
379
345
 
380
346
  @staticmethod
381
- def get_models_for_provider(provider: LLMProvider) -> List[LLMModel]:
382
- """
383
- Returns a list of LLMModel instances for a specific provider.
384
- """
347
+ def list_models_by_provider(provider: LLMProvider) -> List[ModelInfo]:
348
+ """Returns a list of available models for a specific provider."""
385
349
  LLMFactory.ensure_initialized()
386
- return LLMFactory._models_by_provider.get(provider, [])
350
+ provider_models = sorted(
351
+ [m for m in LLMFactory._models_by_identifier.values() if m.provider == provider],
352
+ key=lambda m: m.model_identifier
353
+ )
354
+ return [
355
+ ModelInfo(
356
+ model_identifier=m.model_identifier,
357
+ display_name=m.name,
358
+ value=m.value,
359
+ canonical_name=m.canonical_name,
360
+ provider=m.provider.value,
361
+ runtime=m.runtime.value,
362
+ host_url=m.host_url
363
+ )
364
+ for m in provider_models
365
+ ]
387
366
 
388
367
  @staticmethod
389
- def get_canonical_name(model_name: str) -> Optional[str]:
390
- """
391
- Get the canonical name for a model by its name.
392
-
393
- Args:
394
- model_name (str): The model name (e.g., "gpt_4o")
395
-
396
- Returns:
397
- Optional[str]: The canonical name if found, None otherwise
398
- """
368
+ def list_models_by_runtime(runtime: LLMRuntime) -> List[ModelInfo]:
369
+ """Returns a list of available models for a specific runtime."""
399
370
  LLMFactory.ensure_initialized()
400
- for models in LLMFactory._models_by_provider.values():
401
- for model_instance in models:
402
- if model_instance.name == model_name:
403
- return model_instance.canonical_name
404
- return None
371
+ runtime_models = sorted(
372
+ [m for m in LLMFactory._models_by_identifier.values() if m.runtime == runtime],
373
+ key=lambda m: m.model_identifier
374
+ )
375
+ return [
376
+ ModelInfo(
377
+ model_identifier=m.model_identifier,
378
+ display_name=m.name,
379
+ value=m.value,
380
+ canonical_name=m.canonical_name,
381
+ provider=m.provider.value,
382
+ runtime=m.runtime.value,
383
+ host_url=m.host_url
384
+ )
385
+ for m in runtime_models
386
+ ]
405
387
 
406
388
  @staticmethod
407
- def get_models_grouped_by_provider() -> List[ProviderModelGroup]:
389
+ def get_canonical_name(model_identifier: str) -> Optional[str]:
408
390
  """
409
- Returns a list of all providers, each with a list of its available models,
410
- sorted by provider name and model name. Providers with no models are included
411
- with an empty model list.
391
+ Retrieves the canonical name for a given model identifier.
412
392
  """
413
393
  LLMFactory.ensure_initialized()
414
- result: List[ProviderModelGroup] = []
415
- # Sort all providers from the enum by name for consistent order
416
- all_providers_sorted = sorted(list(LLMProvider), key=lambda p: p.name)
394
+ model = LLMFactory._models_by_identifier.get(model_identifier)
395
+ if model:
396
+ return model.canonical_name
417
397
 
418
- for provider in all_providers_sorted:
419
- # Get models for the current provider, defaults to [] if none are registered
420
- models = LLMFactory._models_by_provider.get(provider, [])
421
-
422
- # Sort the models for this provider by name
423
- sorted_models = sorted(models, key=lambda model: model.name)
424
-
425
- model_infos = [
426
- ModelInfo(name=model.name, canonical_name=model.canonical_name)
427
- for model in sorted_models
428
- ]
429
-
430
- result.append(ProviderModelGroup(
431
- provider=provider.name,
432
- models=model_infos
433
- ))
434
-
435
- return result
398
+ logger.warning(f"Could not find model with identifier '{model_identifier}' to get its canonical name.")
399
+ return None
436
400
 
437
401
  default_llm_factory = LLMFactory()