massgen 0.0.3__py3-none-any.whl → 0.1.0a1__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.

Potentially problematic release.


This version of massgen might be problematic. Click here for more details.

Files changed (268) hide show
  1. massgen/__init__.py +142 -8
  2. massgen/adapters/__init__.py +29 -0
  3. massgen/adapters/ag2_adapter.py +483 -0
  4. massgen/adapters/base.py +183 -0
  5. massgen/adapters/tests/__init__.py +0 -0
  6. massgen/adapters/tests/test_ag2_adapter.py +439 -0
  7. massgen/adapters/tests/test_agent_adapter.py +128 -0
  8. massgen/adapters/utils/__init__.py +2 -0
  9. massgen/adapters/utils/ag2_utils.py +236 -0
  10. massgen/adapters/utils/tests/__init__.py +0 -0
  11. massgen/adapters/utils/tests/test_ag2_utils.py +138 -0
  12. massgen/agent_config.py +329 -55
  13. massgen/api_params_handler/__init__.py +10 -0
  14. massgen/api_params_handler/_api_params_handler_base.py +99 -0
  15. massgen/api_params_handler/_chat_completions_api_params_handler.py +176 -0
  16. massgen/api_params_handler/_claude_api_params_handler.py +113 -0
  17. massgen/api_params_handler/_response_api_params_handler.py +130 -0
  18. massgen/backend/__init__.py +39 -4
  19. massgen/backend/azure_openai.py +385 -0
  20. massgen/backend/base.py +341 -69
  21. massgen/backend/base_with_mcp.py +1102 -0
  22. massgen/backend/capabilities.py +386 -0
  23. massgen/backend/chat_completions.py +577 -130
  24. massgen/backend/claude.py +1033 -537
  25. massgen/backend/claude_code.py +1203 -0
  26. massgen/backend/cli_base.py +209 -0
  27. massgen/backend/docs/BACKEND_ARCHITECTURE.md +126 -0
  28. massgen/backend/{CLAUDE_API_RESEARCH.md → docs/CLAUDE_API_RESEARCH.md} +18 -18
  29. massgen/backend/{GEMINI_API_DOCUMENTATION.md → docs/GEMINI_API_DOCUMENTATION.md} +9 -9
  30. massgen/backend/docs/Gemini MCP Integration Analysis.md +1050 -0
  31. massgen/backend/docs/MCP_IMPLEMENTATION_CLAUDE_BACKEND.md +177 -0
  32. massgen/backend/docs/MCP_INTEGRATION_RESPONSE_BACKEND.md +352 -0
  33. massgen/backend/docs/OPENAI_GPT5_MODELS.md +211 -0
  34. massgen/backend/{OPENAI_RESPONSES_API_FORMAT.md → docs/OPENAI_RESPONSE_API_TOOL_CALLS.md} +3 -3
  35. massgen/backend/docs/OPENAI_response_streaming.md +20654 -0
  36. massgen/backend/docs/inference_backend.md +257 -0
  37. massgen/backend/docs/permissions_and_context_files.md +1085 -0
  38. massgen/backend/external.py +126 -0
  39. massgen/backend/gemini.py +1850 -241
  40. massgen/backend/grok.py +40 -156
  41. massgen/backend/inference.py +156 -0
  42. massgen/backend/lmstudio.py +171 -0
  43. massgen/backend/response.py +1095 -322
  44. massgen/chat_agent.py +131 -113
  45. massgen/cli.py +1504 -287
  46. massgen/config_builder.py +2165 -0
  47. massgen/configs/BACKEND_CONFIGURATION.md +458 -0
  48. massgen/configs/README.md +559 -216
  49. massgen/configs/ag2/ag2_case_study.yaml +27 -0
  50. massgen/configs/ag2/ag2_coder.yaml +34 -0
  51. massgen/configs/ag2/ag2_coder_case_study.yaml +36 -0
  52. massgen/configs/ag2/ag2_gemini.yaml +27 -0
  53. massgen/configs/ag2/ag2_groupchat.yaml +108 -0
  54. massgen/configs/ag2/ag2_groupchat_gpt.yaml +118 -0
  55. massgen/configs/ag2/ag2_single_agent.yaml +21 -0
  56. massgen/configs/basic/multi/fast_timeout_example.yaml +37 -0
  57. massgen/configs/basic/multi/gemini_4o_claude.yaml +31 -0
  58. massgen/configs/basic/multi/gemini_gpt5nano_claude.yaml +36 -0
  59. massgen/configs/{gemini_4o_claude.yaml → basic/multi/geminicode_4o_claude.yaml} +3 -3
  60. massgen/configs/basic/multi/geminicode_gpt5nano_claude.yaml +36 -0
  61. massgen/configs/basic/multi/glm_gemini_claude.yaml +25 -0
  62. massgen/configs/basic/multi/gpt4o_audio_generation.yaml +30 -0
  63. massgen/configs/basic/multi/gpt4o_image_generation.yaml +31 -0
  64. massgen/configs/basic/multi/gpt5nano_glm_qwen.yaml +26 -0
  65. massgen/configs/basic/multi/gpt5nano_image_understanding.yaml +26 -0
  66. massgen/configs/{three_agents_default.yaml → basic/multi/three_agents_default.yaml} +8 -4
  67. massgen/configs/basic/multi/three_agents_opensource.yaml +27 -0
  68. massgen/configs/basic/multi/three_agents_vllm.yaml +20 -0
  69. massgen/configs/basic/multi/two_agents_gemini.yaml +19 -0
  70. massgen/configs/{two_agents.yaml → basic/multi/two_agents_gpt5.yaml} +14 -6
  71. massgen/configs/basic/multi/two_agents_opensource_lmstudio.yaml +31 -0
  72. massgen/configs/basic/multi/two_qwen_vllm_sglang.yaml +28 -0
  73. massgen/configs/{single_agent.yaml → basic/single/single_agent.yaml} +1 -1
  74. massgen/configs/{single_flash2.5.yaml → basic/single/single_flash2.5.yaml} +1 -2
  75. massgen/configs/basic/single/single_gemini2.5pro.yaml +16 -0
  76. massgen/configs/basic/single/single_gpt4o_audio_generation.yaml +22 -0
  77. massgen/configs/basic/single/single_gpt4o_image_generation.yaml +22 -0
  78. massgen/configs/basic/single/single_gpt4o_video_generation.yaml +24 -0
  79. massgen/configs/basic/single/single_gpt5nano.yaml +20 -0
  80. massgen/configs/basic/single/single_gpt5nano_file_search.yaml +18 -0
  81. massgen/configs/basic/single/single_gpt5nano_image_understanding.yaml +17 -0
  82. massgen/configs/basic/single/single_gptoss120b.yaml +15 -0
  83. massgen/configs/basic/single/single_openrouter_audio_understanding.yaml +15 -0
  84. massgen/configs/basic/single/single_qwen_video_understanding.yaml +15 -0
  85. massgen/configs/debug/code_execution/command_filtering_blacklist.yaml +29 -0
  86. massgen/configs/debug/code_execution/command_filtering_whitelist.yaml +28 -0
  87. massgen/configs/debug/code_execution/docker_verification.yaml +29 -0
  88. massgen/configs/debug/skip_coordination_test.yaml +27 -0
  89. massgen/configs/debug/test_sdk_migration.yaml +17 -0
  90. massgen/configs/docs/DISCORD_MCP_SETUP.md +208 -0
  91. massgen/configs/docs/TWITTER_MCP_ENESCINAR_SETUP.md +82 -0
  92. massgen/configs/providers/azure/azure_openai_multi.yaml +21 -0
  93. massgen/configs/providers/azure/azure_openai_single.yaml +19 -0
  94. massgen/configs/providers/claude/claude.yaml +14 -0
  95. massgen/configs/providers/gemini/gemini_gpt5nano.yaml +28 -0
  96. massgen/configs/providers/local/lmstudio.yaml +11 -0
  97. massgen/configs/providers/openai/gpt5.yaml +46 -0
  98. massgen/configs/providers/openai/gpt5_nano.yaml +46 -0
  99. massgen/configs/providers/others/grok_single_agent.yaml +19 -0
  100. massgen/configs/providers/others/zai_coding_team.yaml +108 -0
  101. massgen/configs/providers/others/zai_glm45.yaml +12 -0
  102. massgen/configs/{creative_team.yaml → teams/creative/creative_team.yaml} +16 -6
  103. massgen/configs/{travel_planning.yaml → teams/creative/travel_planning.yaml} +16 -6
  104. massgen/configs/{news_analysis.yaml → teams/research/news_analysis.yaml} +16 -6
  105. massgen/configs/{research_team.yaml → teams/research/research_team.yaml} +15 -7
  106. massgen/configs/{technical_analysis.yaml → teams/research/technical_analysis.yaml} +16 -6
  107. massgen/configs/tools/code-execution/basic_command_execution.yaml +25 -0
  108. massgen/configs/tools/code-execution/code_execution_use_case_simple.yaml +41 -0
  109. massgen/configs/tools/code-execution/docker_claude_code.yaml +32 -0
  110. massgen/configs/tools/code-execution/docker_multi_agent.yaml +32 -0
  111. massgen/configs/tools/code-execution/docker_simple.yaml +29 -0
  112. massgen/configs/tools/code-execution/docker_with_resource_limits.yaml +32 -0
  113. massgen/configs/tools/code-execution/multi_agent_playwright_automation.yaml +57 -0
  114. massgen/configs/tools/filesystem/cc_gpt5_gemini_filesystem.yaml +34 -0
  115. massgen/configs/tools/filesystem/claude_code_context_sharing.yaml +68 -0
  116. massgen/configs/tools/filesystem/claude_code_flash2.5.yaml +43 -0
  117. massgen/configs/tools/filesystem/claude_code_flash2.5_gptoss.yaml +49 -0
  118. massgen/configs/tools/filesystem/claude_code_gpt5nano.yaml +31 -0
  119. massgen/configs/tools/filesystem/claude_code_single.yaml +40 -0
  120. massgen/configs/tools/filesystem/fs_permissions_test.yaml +87 -0
  121. massgen/configs/tools/filesystem/gemini_gemini_workspace_cleanup.yaml +54 -0
  122. massgen/configs/tools/filesystem/gemini_gpt5_filesystem_casestudy.yaml +30 -0
  123. massgen/configs/tools/filesystem/gemini_gpt5nano_file_context_path.yaml +43 -0
  124. massgen/configs/tools/filesystem/gemini_gpt5nano_protected_paths.yaml +45 -0
  125. massgen/configs/tools/filesystem/gpt5mini_cc_fs_context_path.yaml +31 -0
  126. massgen/configs/tools/filesystem/grok4_gpt5_gemini_filesystem.yaml +32 -0
  127. massgen/configs/tools/filesystem/multiturn/grok4_gpt5_claude_code_filesystem_multiturn.yaml +58 -0
  128. massgen/configs/tools/filesystem/multiturn/grok4_gpt5_gemini_filesystem_multiturn.yaml +58 -0
  129. massgen/configs/tools/filesystem/multiturn/two_claude_code_filesystem_multiturn.yaml +47 -0
  130. massgen/configs/tools/filesystem/multiturn/two_gemini_flash_filesystem_multiturn.yaml +48 -0
  131. massgen/configs/tools/mcp/claude_code_discord_mcp_example.yaml +27 -0
  132. massgen/configs/tools/mcp/claude_code_simple_mcp.yaml +35 -0
  133. massgen/configs/tools/mcp/claude_code_twitter_mcp_example.yaml +32 -0
  134. massgen/configs/tools/mcp/claude_mcp_example.yaml +24 -0
  135. massgen/configs/tools/mcp/claude_mcp_test.yaml +27 -0
  136. massgen/configs/tools/mcp/five_agents_travel_mcp_test.yaml +157 -0
  137. massgen/configs/tools/mcp/five_agents_weather_mcp_test.yaml +103 -0
  138. massgen/configs/tools/mcp/gemini_mcp_example.yaml +24 -0
  139. massgen/configs/tools/mcp/gemini_mcp_filesystem_test.yaml +23 -0
  140. massgen/configs/tools/mcp/gemini_mcp_filesystem_test_sharing.yaml +23 -0
  141. massgen/configs/tools/mcp/gemini_mcp_filesystem_test_single_agent.yaml +17 -0
  142. massgen/configs/tools/mcp/gemini_mcp_filesystem_test_with_claude_code.yaml +24 -0
  143. massgen/configs/tools/mcp/gemini_mcp_test.yaml +27 -0
  144. massgen/configs/tools/mcp/gemini_notion_mcp.yaml +52 -0
  145. massgen/configs/tools/mcp/gpt5_nano_mcp_example.yaml +24 -0
  146. massgen/configs/tools/mcp/gpt5_nano_mcp_test.yaml +27 -0
  147. massgen/configs/tools/mcp/gpt5mini_claude_code_discord_mcp_example.yaml +38 -0
  148. massgen/configs/tools/mcp/gpt_oss_mcp_example.yaml +25 -0
  149. massgen/configs/tools/mcp/gpt_oss_mcp_test.yaml +28 -0
  150. massgen/configs/tools/mcp/grok3_mini_mcp_example.yaml +24 -0
  151. massgen/configs/tools/mcp/grok3_mini_mcp_test.yaml +27 -0
  152. massgen/configs/tools/mcp/multimcp_gemini.yaml +111 -0
  153. massgen/configs/tools/mcp/qwen_api_mcp_example.yaml +25 -0
  154. massgen/configs/tools/mcp/qwen_api_mcp_test.yaml +28 -0
  155. massgen/configs/tools/mcp/qwen_local_mcp_example.yaml +24 -0
  156. massgen/configs/tools/mcp/qwen_local_mcp_test.yaml +27 -0
  157. massgen/configs/tools/planning/five_agents_discord_mcp_planning_mode.yaml +140 -0
  158. massgen/configs/tools/planning/five_agents_filesystem_mcp_planning_mode.yaml +151 -0
  159. massgen/configs/tools/planning/five_agents_notion_mcp_planning_mode.yaml +151 -0
  160. massgen/configs/tools/planning/five_agents_twitter_mcp_planning_mode.yaml +155 -0
  161. massgen/configs/tools/planning/gpt5_mini_case_study_mcp_planning_mode.yaml +73 -0
  162. massgen/configs/tools/web-search/claude_streamable_http_test.yaml +43 -0
  163. massgen/configs/tools/web-search/gemini_streamable_http_test.yaml +43 -0
  164. massgen/configs/tools/web-search/gpt5_mini_streamable_http_test.yaml +43 -0
  165. massgen/configs/tools/web-search/gpt_oss_streamable_http_test.yaml +44 -0
  166. massgen/configs/tools/web-search/grok3_mini_streamable_http_test.yaml +43 -0
  167. massgen/configs/tools/web-search/qwen_api_streamable_http_test.yaml +44 -0
  168. massgen/configs/tools/web-search/qwen_local_streamable_http_test.yaml +43 -0
  169. massgen/coordination_tracker.py +708 -0
  170. massgen/docker/README.md +462 -0
  171. massgen/filesystem_manager/__init__.py +21 -0
  172. massgen/filesystem_manager/_base.py +9 -0
  173. massgen/filesystem_manager/_code_execution_server.py +545 -0
  174. massgen/filesystem_manager/_docker_manager.py +477 -0
  175. massgen/filesystem_manager/_file_operation_tracker.py +248 -0
  176. massgen/filesystem_manager/_filesystem_manager.py +813 -0
  177. massgen/filesystem_manager/_path_permission_manager.py +1261 -0
  178. massgen/filesystem_manager/_workspace_tools_server.py +1815 -0
  179. massgen/formatter/__init__.py +10 -0
  180. massgen/formatter/_chat_completions_formatter.py +284 -0
  181. massgen/formatter/_claude_formatter.py +235 -0
  182. massgen/formatter/_formatter_base.py +156 -0
  183. massgen/formatter/_response_formatter.py +263 -0
  184. massgen/frontend/__init__.py +1 -2
  185. massgen/frontend/coordination_ui.py +471 -286
  186. massgen/frontend/displays/base_display.py +56 -11
  187. massgen/frontend/displays/create_coordination_table.py +1956 -0
  188. massgen/frontend/displays/rich_terminal_display.py +1259 -619
  189. massgen/frontend/displays/simple_display.py +9 -4
  190. massgen/frontend/displays/terminal_display.py +27 -68
  191. massgen/logger_config.py +681 -0
  192. massgen/mcp_tools/README.md +232 -0
  193. massgen/mcp_tools/__init__.py +105 -0
  194. massgen/mcp_tools/backend_utils.py +1035 -0
  195. massgen/mcp_tools/circuit_breaker.py +195 -0
  196. massgen/mcp_tools/client.py +894 -0
  197. massgen/mcp_tools/config_validator.py +138 -0
  198. massgen/mcp_tools/docs/circuit_breaker.md +646 -0
  199. massgen/mcp_tools/docs/client.md +950 -0
  200. massgen/mcp_tools/docs/config_validator.md +478 -0
  201. massgen/mcp_tools/docs/exceptions.md +1165 -0
  202. massgen/mcp_tools/docs/security.md +854 -0
  203. massgen/mcp_tools/exceptions.py +338 -0
  204. massgen/mcp_tools/hooks.py +212 -0
  205. massgen/mcp_tools/security.py +780 -0
  206. massgen/message_templates.py +342 -64
  207. massgen/orchestrator.py +1515 -241
  208. massgen/stream_chunk/__init__.py +35 -0
  209. massgen/stream_chunk/base.py +92 -0
  210. massgen/stream_chunk/multimodal.py +237 -0
  211. massgen/stream_chunk/text.py +162 -0
  212. massgen/tests/mcp_test_server.py +150 -0
  213. massgen/tests/multi_turn_conversation_design.md +0 -8
  214. massgen/tests/test_azure_openai_backend.py +156 -0
  215. massgen/tests/test_backend_capabilities.py +262 -0
  216. massgen/tests/test_backend_event_loop_all.py +179 -0
  217. massgen/tests/test_chat_completions_refactor.py +142 -0
  218. massgen/tests/test_claude_backend.py +15 -28
  219. massgen/tests/test_claude_code.py +268 -0
  220. massgen/tests/test_claude_code_context_sharing.py +233 -0
  221. massgen/tests/test_claude_code_orchestrator.py +175 -0
  222. massgen/tests/test_cli_backends.py +180 -0
  223. massgen/tests/test_code_execution.py +679 -0
  224. massgen/tests/test_external_agent_backend.py +134 -0
  225. massgen/tests/test_final_presentation_fallback.py +237 -0
  226. massgen/tests/test_gemini_planning_mode.py +351 -0
  227. massgen/tests/test_grok_backend.py +7 -10
  228. massgen/tests/test_http_mcp_server.py +42 -0
  229. massgen/tests/test_integration_simple.py +198 -0
  230. massgen/tests/test_mcp_blocking.py +125 -0
  231. massgen/tests/test_message_context_building.py +29 -47
  232. massgen/tests/test_orchestrator_final_presentation.py +48 -0
  233. massgen/tests/test_path_permission_manager.py +2087 -0
  234. massgen/tests/test_rich_terminal_display.py +14 -13
  235. massgen/tests/test_timeout.py +133 -0
  236. massgen/tests/test_v3_3agents.py +11 -12
  237. massgen/tests/test_v3_simple.py +8 -13
  238. massgen/tests/test_v3_three_agents.py +11 -18
  239. massgen/tests/test_v3_two_agents.py +8 -13
  240. massgen/token_manager/__init__.py +7 -0
  241. massgen/token_manager/token_manager.py +400 -0
  242. massgen/utils.py +52 -16
  243. massgen/v1/agent.py +45 -91
  244. massgen/v1/agents.py +18 -53
  245. massgen/v1/backends/gemini.py +50 -153
  246. massgen/v1/backends/grok.py +21 -54
  247. massgen/v1/backends/oai.py +39 -111
  248. massgen/v1/cli.py +36 -93
  249. massgen/v1/config.py +8 -12
  250. massgen/v1/logging.py +43 -127
  251. massgen/v1/main.py +18 -32
  252. massgen/v1/orchestrator.py +68 -209
  253. massgen/v1/streaming_display.py +62 -163
  254. massgen/v1/tools.py +8 -12
  255. massgen/v1/types.py +9 -23
  256. massgen/v1/utils.py +5 -23
  257. massgen-0.1.0a1.dist-info/METADATA +1287 -0
  258. massgen-0.1.0a1.dist-info/RECORD +273 -0
  259. massgen-0.1.0a1.dist-info/entry_points.txt +2 -0
  260. massgen/frontend/logging/__init__.py +0 -9
  261. massgen/frontend/logging/realtime_logger.py +0 -197
  262. massgen-0.0.3.dist-info/METADATA +0 -568
  263. massgen-0.0.3.dist-info/RECORD +0 -76
  264. massgen-0.0.3.dist-info/entry_points.txt +0 -2
  265. /massgen/backend/{Function calling openai responses.md → docs/Function calling openai responses.md} +0 -0
  266. {massgen-0.0.3.dist-info → massgen-0.1.0a1.dist-info}/WHEEL +0 -0
  267. {massgen-0.0.3.dist-info → massgen-0.1.0a1.dist-info}/licenses/LICENSE +0 -0
  268. {massgen-0.0.3.dist-info → massgen-0.1.0a1.dist-info}/top_level.txt +0 -0
massgen/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  MassGen - Multi-Agent System Generator (Foundation Release)
3
4
 
@@ -46,33 +47,166 @@ Usage:
46
47
  print(chunk.content, end="")
47
48
  """
48
49
 
49
- # Import main classes for convenience
50
- from .backend.response import ResponseBackend
50
+ from .agent_config import AgentConfig
51
51
  from .backend.claude import ClaudeBackend
52
52
  from .backend.gemini import GeminiBackend
53
53
  from .backend.grok import GrokBackend
54
+ from .backend.inference import InferenceBackend
55
+ from .backend.lmstudio import LMStudioBackend
56
+
57
+ # Import main classes for convenience
58
+ from .backend.response import ResponseBackend
54
59
  from .chat_agent import (
55
60
  ChatAgent,
56
- SingleAgent,
57
61
  ConfigurableAgent,
58
- create_simple_agent,
62
+ SingleAgent,
63
+ create_computational_agent,
59
64
  create_expert_agent,
60
65
  create_research_agent,
61
- create_computational_agent,
66
+ create_simple_agent,
62
67
  )
63
- from .orchestrator import Orchestrator, create_orchestrator
64
68
  from .message_templates import MessageTemplates, get_templates
65
- from .agent_config import AgentConfig
69
+ from .orchestrator import Orchestrator, create_orchestrator
66
70
 
67
- __version__ = "0.0.3"
71
+ __version__ = "0.1.0a1"
68
72
  __author__ = "MassGen Contributors"
69
73
 
74
+
75
+ # Python API for programmatic usage
76
+ async def run(
77
+ query: str,
78
+ config: str = None,
79
+ model: str = None,
80
+ **kwargs,
81
+ ) -> dict:
82
+ """Run MassGen query programmatically.
83
+
84
+ This is an async wrapper around MassGen's CLI logic, providing a simple
85
+ Python API for programmatic usage.
86
+
87
+ Args:
88
+ query: Question or task for the agent(s)
89
+ config: Config file path or @examples/NAME (optional)
90
+ model: Quick single-agent mode with model name (optional)
91
+ **kwargs: Additional configuration options
92
+
93
+ Returns:
94
+ dict: Result with 'final_answer' and metadata:
95
+ {
96
+ 'final_answer': str, # The generated answer
97
+ 'winning_agent_id': str, # ID of winning agent (multi-agent)
98
+ 'config_used': str, # Path to config used
99
+ }
100
+
101
+ Examples:
102
+ # Single agent with model
103
+ >>> result = await massgen.run(
104
+ ... query="What is machine learning?",
105
+ ... model="gpt-5-mini"
106
+ ... )
107
+ >>> print(result['final_answer'])
108
+
109
+ # Multi-agent with config
110
+ >>> result = await massgen.run(
111
+ ... query="Compare renewable energy sources",
112
+ ... config="@examples/basic_multi"
113
+ ... )
114
+
115
+ # Use default config (from first-run setup)
116
+ >>> result = await massgen.run("Your question")
117
+
118
+ Note:
119
+ MassGen is async by nature. Use `asyncio.run()` if calling from sync code:
120
+ >>> import asyncio
121
+ >>> result = asyncio.run(massgen.run("Question", model="gpt-5"))
122
+ """
123
+ from pathlib import Path
124
+
125
+ from .cli import (
126
+ create_agents_from_config,
127
+ create_simple_config,
128
+ load_config_file,
129
+ resolve_config_path,
130
+ run_single_question,
131
+ )
132
+ from .utils import get_backend_type_from_model
133
+
134
+ # Determine config to use
135
+ if config:
136
+ resolved_path = resolve_config_path(config)
137
+ if resolved_path is None:
138
+ raise ValueError("Could not resolve config path. Use --init to create default config.")
139
+ config_dict = load_config_file(str(resolved_path))
140
+ config_path_used = str(resolved_path)
141
+ elif model:
142
+ # Quick single-agent mode
143
+ backend_type = get_backend_type_from_model(model)
144
+ # Create headless UI config for programmatic API usage
145
+ headless_ui_config = {
146
+ "display_type": "simple",
147
+ "logging_enabled": False,
148
+ }
149
+ config_dict = create_simple_config(
150
+ backend_type=backend_type,
151
+ model=model,
152
+ system_message=kwargs.get("system_message"),
153
+ base_url=kwargs.get("base_url"),
154
+ ui_config=headless_ui_config,
155
+ )
156
+ config_path_used = f"single-agent:{model}"
157
+ else:
158
+ # Try default config
159
+ default_config = Path.home() / ".config/massgen/config.yaml"
160
+ if default_config.exists():
161
+ config_dict = load_config_file(str(default_config))
162
+ config_path_used = str(default_config)
163
+ else:
164
+ raise ValueError(
165
+ "No config specified and no default config found.\n" "Run `massgen --init` to create a default configuration.",
166
+ )
167
+
168
+ # Extract orchestrator config
169
+ orchestrator_cfg = config_dict.get("orchestrator", {})
170
+
171
+ # Create agents
172
+ agents = create_agents_from_config(config_dict, orchestrator_cfg)
173
+ if not agents:
174
+ raise ValueError("No agents configured")
175
+
176
+ # Force headless UI config for programmatic API usage
177
+ # Override any UI settings from the config file to ensure non-interactive operation
178
+ ui_config = {
179
+ "display_type": "simple", # Headless mode for API usage
180
+ "logging_enabled": False, # Quiet for API usage
181
+ }
182
+
183
+ # Run the query
184
+ answer = await run_single_question(
185
+ query,
186
+ agents,
187
+ ui_config,
188
+ orchestrator=orchestrator_cfg,
189
+ )
190
+
191
+ # Build result dict
192
+ result = {
193
+ "final_answer": answer,
194
+ "config_used": config_path_used,
195
+ }
196
+
197
+ return result
198
+
199
+
70
200
  __all__ = [
201
+ # Python API
202
+ "run",
71
203
  # Backends
72
204
  "ResponseBackend",
73
205
  "ClaudeBackend",
74
206
  "GeminiBackend",
75
207
  "GrokBackend",
208
+ "LMStudioBackend",
209
+ "InferenceBackend",
76
210
  # Agents
77
211
  "ChatAgent",
78
212
  "SingleAgent",
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ External agent adapters for MassGen.
4
+
5
+ This package provides adapters for integrating various external agent
6
+ frameworks and systems into MassGen's orchestration system.
7
+ """
8
+ from typing import Dict, Type
9
+
10
+ from .base import AgentAdapter
11
+
12
+ # Adapter registry maps framework names to adapter classes
13
+ adapter_registry: Dict[str, Type[AgentAdapter]] = {}
14
+
15
+ # Try to import AG2 adapter (optional dependency)
16
+ try:
17
+ from .ag2_adapter import AG2Adapter
18
+
19
+ adapter_registry["ag2"] = AG2Adapter
20
+ adapter_registry["autogen"] = AG2Adapter # Alias for backward compatibility
21
+ except ImportError:
22
+ # AG2 not installed, skip registration
23
+ pass
24
+
25
+
26
+ __all__ = [
27
+ "AgentAdapter",
28
+ "adapter_registry",
29
+ ]
@@ -0,0 +1,483 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AG2 (AutoGen) adapter for MassGen.
4
+
5
+ Supports both single agents and GroupChat configurations.
6
+ """
7
+ # Suppress autogen deprecation warnings
8
+ import warnings
9
+ from typing import Any, AsyncGenerator, Dict, List
10
+
11
+ from massgen.logger_config import log_backend_activity, logger
12
+
13
+ from ..utils import CoordinationStage
14
+ from .base import AgentAdapter, StreamChunk
15
+ from .utils.ag2_utils import (
16
+ create_llm_config,
17
+ get_group_initial_message,
18
+ get_user_agent_default_description,
19
+ get_user_agent_default_system_message,
20
+ get_user_agent_tool_call_message,
21
+ postprocess_group_chat_results,
22
+ register_tools_for_agent,
23
+ setup_agent_from_config,
24
+ setup_api_keys,
25
+ unregister_tools_for_agent,
26
+ )
27
+
28
+ warnings.filterwarnings("ignore", category=DeprecationWarning, module="autogen")
29
+ warnings.filterwarnings("ignore", message=".*jsonschema.*")
30
+ warnings.filterwarnings("ignore", message=".*Pydantic.*")
31
+
32
+ from autogen import ConversableAgent # noqa: E402
33
+ from autogen.agentchat import a_run_group_chat # noqa: E402
34
+ from autogen.agentchat.group.patterns import AutoPattern # noqa: E402
35
+
36
+ DEFAULT_MAX_ROUNDS = 20
37
+ SUPPORTED_GROUPCHAT_PATTERNS = ["auto"]
38
+
39
+
40
+ class AG2Adapter(AgentAdapter):
41
+ """
42
+ Adapter for AG2 (AutoGen) framework.
43
+
44
+ Supports:
45
+ - Single AG2 agents (ConversableAgent, AssistantAgent)
46
+ - Function/tool calling
47
+ - Code execution with multiple executor types:
48
+ * LocalCommandLineCodeExecutor (local shell)
49
+ * DockerCommandLineCodeExecutor (Docker containers)
50
+ * JupyterCodeExecutor (Jupyter kernels)
51
+ * YepCodeCodeExecutor (YepCode serverless)
52
+ - Async execution with a_generate_reply
53
+ - No human-in-the-loop (autonomous operation)
54
+
55
+ Todos:
56
+ - Group chat support with patterns (e.g., AutoPattern, DefaultPattern, etc.)
57
+ - More tool support including MCP
58
+ """
59
+
60
+ def __init__(self, **kwargs):
61
+ """
62
+ Initialize AG2 adapter.
63
+
64
+ The adapter receives the entire backend configuration from MassGen.
65
+ It should contain EITHER 'agent_config' OR 'group_config' (not both).
66
+
67
+ Args:
68
+ **kwargs: Backend configuration containing either:
69
+ - agent_config: Configuration for single AG2 agent
70
+ - group_config: Configuration for AG2 GroupChat
71
+ """
72
+ super().__init__(**kwargs)
73
+
74
+ # Set up API keys for AG2 compatibility
75
+ setup_api_keys()
76
+
77
+ # Extract agent_config or group_config from kwargs
78
+ self.agent_config = kwargs.get("agent_config")
79
+ self.group_config = kwargs.get("group_config")
80
+
81
+ # Validate that we have exactly one of them
82
+ if self.agent_config and self.group_config:
83
+ raise ValueError(
84
+ "Backend configuration should contain EITHER 'agent_config' OR 'group_config', not both.",
85
+ )
86
+ if not self.agent_config and not self.group_config:
87
+ raise ValueError(
88
+ "Backend configuration must contain either 'agent_config' for single agent " "or 'group_config' for GroupChat.",
89
+ )
90
+ self.agent_id = None
91
+ # Initialize AG2 components
92
+ self._setup_agents()
93
+
94
+ def _setup_agents(self):
95
+ """Set up AG2 agents based on configuration."""
96
+ if self.group_config:
97
+ # GroupChat setup
98
+ self._setup_group_chat()
99
+ else:
100
+ # Single agent setup
101
+ self._setup_single_agent()
102
+
103
+ def _setup_single_agent(self):
104
+ """Set up a single AG2 agent."""
105
+ self.agent = setup_agent_from_config(self.agent_config)
106
+
107
+ self.is_group_chat = False
108
+
109
+ def _setup_group_chat(self):
110
+ """Set up AG2 GroupChat with multiple agents and pattern."""
111
+ # Validate group_config has required fields
112
+ if "pattern" not in self.group_config:
113
+ raise ValueError("group_config must include 'pattern' configuration")
114
+
115
+ # Get default llm_config from group_config (required)
116
+ self.default_llm_config = self.group_config.get("llm_config")
117
+ if not self.default_llm_config:
118
+ raise ValueError("group_config must include 'llm_config' as default for all agents")
119
+
120
+ # Create sub-agents from configuration
121
+ agents = []
122
+ agent_name_map = {}
123
+
124
+ for agent_cfg in self.group_config.get("agents", []):
125
+ agent = setup_agent_from_config(agent_cfg, default_llm_config=self.default_llm_config)
126
+ agents.append(agent)
127
+ agent_name_map[agent.name] = agent
128
+
129
+ if not agents:
130
+ raise ValueError("No valid agents configured for group chat")
131
+
132
+ # Get pattern configuration
133
+ pattern_config = self.group_config["pattern"]
134
+ pattern_type = pattern_config.get("type")
135
+
136
+ if not pattern_type:
137
+ raise ValueError("pattern configuration must include 'type' field")
138
+
139
+ if pattern_type not in SUPPORTED_GROUPCHAT_PATTERNS:
140
+ raise NotImplementedError(
141
+ f"Pattern type '{pattern_type}' not supported. Supported types: {', '.join(SUPPORTED_GROUPCHAT_PATTERNS)}",
142
+ )
143
+
144
+ # Set up user_agent
145
+ self.user_agent = self._setup_user_agent(
146
+ user_agent_config=self.group_config.get("user_agent"),
147
+ default_llm_config=self.default_llm_config,
148
+ )
149
+
150
+ # Set up group_manager_args
151
+ group_manager_args = self._setup_group_manager_args(pattern_config, self.default_llm_config)
152
+
153
+ # Create pattern based on type
154
+ self.pattern = self._create_pattern(pattern_type, pattern_config, agents, agent_name_map, group_manager_args)
155
+
156
+ # Store agents and pattern info
157
+ self.agents = agents
158
+ self.group_max_rounds = self.group_config.get("max_rounds", DEFAULT_MAX_ROUNDS)
159
+ self.is_group_chat = True
160
+
161
+ logger.info(f"[AG2Adapter] GroupChat setup complete with {len(agents)} agents and {pattern_type} pattern")
162
+
163
+ def _setup_user_agent(self, user_agent_config: Any, default_llm_config: Any) -> ConversableAgent:
164
+ """
165
+ Set up user_agent for group chat.
166
+
167
+ User agent makes final decisions and calls workflow tools.
168
+ Its name MUST be "User" for termination condition to work.
169
+
170
+ Args:
171
+ user_agent_config: Optional user agent configuration from YAML
172
+ default_llm_config: Default llm_config to use if not specified
173
+
174
+ Returns:
175
+ ConversableAgent with name "User"
176
+ """
177
+ if user_agent_config:
178
+ # User provided custom user_agent configuration
179
+ user_agent = setup_agent_from_config(user_agent_config, default_llm_config=default_llm_config)
180
+
181
+ # Validate name is "User"
182
+ if user_agent.name != "User":
183
+ raise ValueError(
184
+ f"user_agent name must be 'User', got '{user_agent.name}' for termination condition to work",
185
+ )
186
+ return user_agent
187
+ else:
188
+ # Create default user_agent
189
+ return ConversableAgent(
190
+ name="User",
191
+ system_message=get_user_agent_default_system_message(),
192
+ description=get_user_agent_default_description(),
193
+ human_input_mode="NEVER",
194
+ code_execution_config=False,
195
+ llm_config=create_llm_config(default_llm_config),
196
+ )
197
+
198
+ def _setup_group_manager_args(self, pattern_config: Dict[str, Any], default_llm_config: Any) -> Dict[str, Any]:
199
+ """
200
+ Set up group_manager_args for pattern.
201
+
202
+ Args:
203
+ pattern_config: Pattern configuration from YAML
204
+ default_llm_config: Default llm_config to use if not specified
205
+
206
+ Returns:
207
+ Dict with llm_config and termination condition
208
+ """
209
+ group_manager_args = pattern_config.get("group_manager_args", {})
210
+
211
+ # Ensure group_manager_args has llm_config (use default if not provided)
212
+ if "llm_config" not in group_manager_args:
213
+ group_manager_args["llm_config"] = create_llm_config(default_llm_config)
214
+ else:
215
+ group_manager_args["llm_config"] = create_llm_config(group_manager_args["llm_config"])
216
+
217
+ # Add termination condition: terminate when User says "TERMINATE"
218
+ group_manager_args["is_termination_msg"] = lambda msg: (msg.get("name") == self.user_agent.name and "TERMINATE" in msg.get("content", ""))
219
+
220
+ return group_manager_args
221
+
222
+ def _create_pattern(
223
+ self,
224
+ pattern_type: str,
225
+ pattern_config: Dict[str, Any],
226
+ agents: List[ConversableAgent],
227
+ agent_name_map: Dict[str, ConversableAgent],
228
+ group_manager_args: Dict[str, Any],
229
+ *args,
230
+ ) -> Any:
231
+ """
232
+ Create AG2 pattern based on type.
233
+
234
+ Args:
235
+ pattern_type: Type of pattern (currently only "auto")
236
+ pattern_config: Pattern configuration from YAML
237
+ agents: List of expert agents
238
+ agent_name_map: Mapping from agent names to agent objects
239
+ group_manager_args: Group manager configuration
240
+
241
+ Returns:
242
+ Pattern instance (AutoPattern)
243
+ """
244
+ # Get initial agent
245
+ initial_agent_name = pattern_config.get("initial_agent")
246
+ if not initial_agent_name:
247
+ raise ValueError("initial_agent must be specified in pattern configuration")
248
+
249
+ if initial_agent_name not in agent_name_map:
250
+ raise ValueError(f"initial_agent '{initial_agent_name}' not found in agents list")
251
+
252
+ initial_agent = agent_name_map[initial_agent_name]
253
+
254
+ # Extract extra pattern-specific arguments
255
+ extra_args = {k: v for k, v in pattern_config.items() if k not in ["type", "initial_agent", "group_manager_args"]}
256
+
257
+ # Create pattern
258
+ if pattern_type == "auto":
259
+ return AutoPattern(
260
+ initial_agent=initial_agent,
261
+ agents=agents,
262
+ user_agent=self.user_agent,
263
+ group_manager_args=group_manager_args,
264
+ **extra_args,
265
+ )
266
+ else:
267
+ raise NotImplementedError(f"Pattern type '{pattern_type}' not supported")
268
+
269
+ async def _execute_single_agent(self, messages: List[Dict[str, Any]], agent: ConversableAgent) -> AsyncGenerator[StreamChunk, None]:
270
+ """
271
+ Execute single AG2 agent.
272
+
273
+ Args:
274
+ messages: Conversation messages
275
+ agent_id: Agent ID for logging
276
+
277
+ Returns:
278
+ Tuple of (content, tool_calls)
279
+ """
280
+ result = await agent.a_generate_reply(messages)
281
+
282
+ # Extract content and tool_calls from AG2 response
283
+ # MassGen and AG2 use same format for tool calls
284
+ content = result.get("content", "") if isinstance(result, dict) else str(result)
285
+ tool_calls = result.get("tool_calls") if isinstance(result, dict) else None
286
+
287
+ # Log extracted data
288
+ log_backend_activity(
289
+ "ag2",
290
+ "Received response data from AG2",
291
+ {
292
+ "has_content": bool(content),
293
+ "content_length": len(content) if content else 0,
294
+ "has_tool_calls": bool(tool_calls),
295
+ "tool_count": len(tool_calls) if tool_calls else 0,
296
+ },
297
+ agent_id=self.agent_id,
298
+ )
299
+
300
+ # Use base class simulate_streaming method
301
+ async for chunk in self.simulate_streaming(content, tool_calls):
302
+ yield chunk
303
+
304
+ async def _execute_group_chat(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[str, None]:
305
+ """
306
+ Execute AG2 group chat with pattern.
307
+
308
+ Args:
309
+ messages: Conversation messages
310
+ agent_id: Agent ID for logging
311
+
312
+ Returns:
313
+ Tuple of (content, tool_calls)
314
+ """
315
+ # Add name field to all messages so the conversation will start correctly from user agent
316
+ for message in messages:
317
+ message["name"] = "User"
318
+
319
+ # The pattern will coordinate agents until user_agent generates tool_calls
320
+ response = await a_run_group_chat(
321
+ pattern=self.pattern,
322
+ messages=messages,
323
+ max_rounds=self.group_max_rounds,
324
+ )
325
+
326
+ last_group_chat_event_msgs = [] # Store messages from the last event
327
+
328
+ def process_and_log_event(*args, **kwargs) -> None:
329
+ """Process and log AG2 event, returning string representation."""
330
+ line = " ".join(str(arg) for arg in args)
331
+ last_group_chat_event_msgs.append(line)
332
+
333
+ async for event in response.events:
334
+ last_group_chat_event_msgs.clear()
335
+ event.print(f=process_and_log_event)
336
+ formatted_message = "\n".join(last_group_chat_event_msgs)
337
+
338
+ log_backend_activity(
339
+ "ag2",
340
+ "Received response from AG2",
341
+ {"message": formatted_message},
342
+ agent_id=self.agent_id,
343
+ )
344
+ yield formatted_message
345
+
346
+ async def _execute_group_chat_with_user_agent(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[StreamChunk, None]:
347
+ messages_to_execute = []
348
+ if self.coordination_stage == CoordinationStage.INITIAL_ANSWER:
349
+ # Todo: should make ag2 integration stateful and put this in reset_state
350
+ self.user_agent.update_system_message(get_user_agent_default_system_message())
351
+
352
+ messages[0] = get_group_initial_message()
353
+ async for event_msg in self._execute_group_chat(messages):
354
+ yield StreamChunk(type="content", content=event_msg)
355
+ results = list(self.user_agent._oai_messages.values())[0]
356
+
357
+ self.user_agent.update_system_message(get_user_agent_tool_call_message())
358
+ register_tools_for_agent(self.workflow_tools, self.user_agent)
359
+
360
+ messages_to_execute = postprocess_group_chat_results(results)
361
+
362
+ elif self.coordination_stage == CoordinationStage.ENFORCEMENT:
363
+ register_tools_for_agent(self.workflow_tools, self.user_agent)
364
+ messages_to_execute = messages
365
+
366
+ elif self.coordination_stage == CoordinationStage.PRESENTATION:
367
+ self.user_agent.update_system_message(messages[0]["content"])
368
+ messages_to_execute = [messages[1]]
369
+
370
+ async for chunk in self._execute_single_agent(messages=messages_to_execute, agent=self.user_agent):
371
+ yield chunk
372
+
373
+ async def execute_streaming(
374
+ self,
375
+ messages: List[Dict[str, Any]],
376
+ tools: List[Dict[str, Any]],
377
+ **kwargs,
378
+ ) -> AsyncGenerator[StreamChunk, None]:
379
+ """
380
+ Stream response from AG2 agent(s).
381
+
382
+ Since AG2 doesn't support streaming, we simulate it.
383
+ """
384
+ try:
385
+ self._register_tools(tools)
386
+ agent_id = kwargs.get("agent_id")
387
+ if agent_id:
388
+ self.agent_id = agent_id
389
+
390
+ # Log start
391
+ log_backend_activity(
392
+ "ag2",
393
+ "Starting execute_streaming",
394
+ {"num_messages": len(messages), "num_tools": len(tools) if tools else 0},
395
+ agent_id=agent_id,
396
+ )
397
+
398
+ # Execute agent or group chat
399
+ if self.is_group_chat:
400
+ async for chunk in self._execute_group_chat_with_user_agent(messages):
401
+ yield chunk
402
+ else:
403
+ async for chunk in self._execute_single_agent(messages, self.agent):
404
+ yield chunk
405
+
406
+ # unregister workflow tools after each chat to make sure they're not used in wrong time
407
+ unregister_tools_for_agent(self.workflow_tools, self.user_agent)
408
+
409
+ except Exception as e:
410
+ logger.error(f"[AG2Adapter] Error in execute_streaming: {e}", exc_info=True)
411
+
412
+ agent_id = kwargs.get("agent_id", "ag2_agent")
413
+ log_backend_activity(
414
+ "ag2",
415
+ "Error during execution",
416
+ {"error": str(e), "error_type": type(e).__name__},
417
+ agent_id=agent_id,
418
+ )
419
+
420
+ # Yield error chunk
421
+ yield StreamChunk(type="error", error=f"AG2 execution error: {str(e)}")
422
+
423
+ # =============================================================================
424
+ # TOOL REGISTRATION METHODS
425
+ # =============================================================================
426
+
427
+ def _register_tools(self, tools: List[Dict[str, Any]]) -> None:
428
+ """
429
+ Register tools with the agent(s).
430
+
431
+ For single agent: Register all tools to the agent.
432
+
433
+ For group chat:
434
+ - Workflow tools (new_answer, vote) → register ONLY to user_agent
435
+ - Other tools (MCP, etc.) → register to ALL expert agents (not user_agent)
436
+
437
+ MassGen and AG2 both use OpenAI function format for tools.
438
+ """
439
+ if not tools:
440
+ return
441
+
442
+ if self.is_group_chat:
443
+ self._register_tools_for_group_chat(tools)
444
+ else:
445
+ register_tools_for_agent(tools, self.agent)
446
+
447
+ def _register_tools_for_group_chat(self, tools: List[Dict[str, Any]]) -> None:
448
+ """Register tools to group chat agents based on type."""
449
+ workflow_tools, other_tools = self._separate_workflow_and_other_tools(tools)
450
+
451
+ # Register other tools to ALL expert agents (not user_agent)
452
+ for agent in self.agents:
453
+ for tool in other_tools:
454
+ register_tools_for_agent([tool], agent)
455
+ if other_tools:
456
+ logger.info(f"[AG2Adapter] Registered {len(other_tools)} non-workflow tools to agent '{agent.name}'")
457
+
458
+ self.workflow_tools = workflow_tools
459
+
460
+ def _separate_workflow_and_other_tools(self, tools: List[Dict[str, Any]]) -> tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
461
+ """
462
+ Separate workflow tools from other tools.
463
+
464
+ Args:
465
+ tools: List of all tools
466
+
467
+ Returns:
468
+ Tuple of (workflow_tools, other_tools)
469
+ """
470
+ workflow_tools = []
471
+ other_tools = []
472
+
473
+ for tool in tools:
474
+ tool_name = self._get_tool_name(tool)
475
+ if tool_name in ["new_answer", "vote"]:
476
+ workflow_tools.append(tool)
477
+ else:
478
+ other_tools.append(tool)
479
+
480
+ if "new_answer" in workflow_tools and "vote" not in workflow_tools:
481
+ raise ValueError("Both 'new_answer' and 'vote' workflow tools must be provided.")
482
+
483
+ return workflow_tools, other_tools