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
@@ -0,0 +1,183 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Base adapter class for external agent agents.
4
+ """
5
+ import asyncio
6
+ from abc import ABC, abstractmethod
7
+ from typing import Any, AsyncGenerator, Dict, List, Optional
8
+
9
+ from massgen.backend.base import StreamChunk
10
+ from massgen.utils import CoordinationStage
11
+
12
+
13
+ class AgentAdapter(ABC):
14
+ """
15
+ Abstract base class for external agent adapters.
16
+
17
+ Adapters handle:
18
+ - Message format conversion between MassGen and external agents
19
+ - Tool/function conversion and mapping
20
+ - Streaming simulation for non-streaming agents
21
+ - State management for stateful agents
22
+ """
23
+
24
+ def __init__(self, **kwargs):
25
+ """Initialize adapter with agent-specific configuration."""
26
+ self.config = kwargs
27
+ self._conversation_history = []
28
+ self.coordination_stage = None
29
+
30
+ @abstractmethod
31
+ async def execute_streaming(
32
+ self,
33
+ messages: List[Dict[str, Any]],
34
+ tools: List[Dict[str, Any]],
35
+ **kwargs,
36
+ ) -> AsyncGenerator[StreamChunk, None]:
37
+ """
38
+ Stream response with tool support.
39
+
40
+ This method must:
41
+ 1. Convert MassGen messages to agent format
42
+ 2. Convert MassGen tools to agent format
43
+ 3. Call the agent
44
+ 4. Convert response back to MassGen format
45
+ 5. Simulate streaming if agent doesn't support it
46
+
47
+ Args:
48
+ messages: MassGen format messages
49
+ tools: MassGen format tools
50
+ **kwargs: Additional parameters
51
+
52
+ Yields:
53
+ StreamChunk: Standardized response chunks
54
+
55
+ """
56
+
57
+ async def simulate_streaming(
58
+ self,
59
+ content: str,
60
+ tool_calls: Optional[List[Dict[str, Any]]] = None,
61
+ delay: float = 0.01,
62
+ ) -> AsyncGenerator[StreamChunk, None]:
63
+ """
64
+ Simulate streaming for agents that don't support it natively.
65
+
66
+ Args:
67
+ content: Complete response content
68
+ tool_calls: Tool calls to include
69
+ delay: Delay between chunks (seconds)
70
+
71
+ Yields:
72
+ StreamChunk: Simulated streaming chunks
73
+ """
74
+ # Stream content in chunks
75
+ if content:
76
+ words = content.split()
77
+ for i, word in enumerate(words):
78
+ chunk_text = word + (" " if i < len(words) - 1 else "")
79
+ yield StreamChunk(type="content", content=chunk_text)
80
+ await asyncio.sleep(delay)
81
+
82
+ # Send tool calls if any
83
+ if tool_calls:
84
+ yield StreamChunk(type="tool_calls", tool_calls=tool_calls)
85
+
86
+ # Send completion signals
87
+ complete_message = {
88
+ "role": "assistant",
89
+ "content": content or "",
90
+ }
91
+ if tool_calls:
92
+ complete_message["tool_calls"] = tool_calls
93
+
94
+ yield StreamChunk(type="complete_message", complete_message=complete_message)
95
+ yield StreamChunk(type="done")
96
+
97
+ @staticmethod
98
+ def _get_tool_name(tool: Dict[str, Any]) -> str:
99
+ """
100
+ Extract tool name from tool schema.
101
+
102
+ Supports both formats:
103
+ - {"type": "function", "function": {"name": "tool_name", ...}}
104
+ - {"name": "tool_name", ...}
105
+ """
106
+ if "function" in tool:
107
+ return tool["function"].get("name", "")
108
+ return tool.get("name", "")
109
+
110
+ def convert_messages_from_massgen(
111
+ self,
112
+ messages: List[Dict[str, Any]],
113
+ ) -> Any:
114
+ """
115
+ Convert MassGen messages to agent-specific format.
116
+
117
+ Override this method for agent-specific conversion.
118
+
119
+ Args:
120
+ messages: List of MassGen format messages
121
+
122
+ Returns:
123
+ agent-specific message format
124
+ """
125
+ return messages
126
+
127
+ def convert_response_to_massgen(
128
+ self,
129
+ response: Any,
130
+ ) -> Dict[str, Any]:
131
+ """
132
+ Convert agent response to MassGen format.
133
+
134
+ Override this method for agent-specific conversion.
135
+
136
+ Args:
137
+ response: agent-specific response
138
+
139
+ Returns:
140
+ MassGen format response with content and optional tool_calls
141
+ """
142
+ return {
143
+ "content": str(response),
144
+ "tool_calls": None,
145
+ }
146
+
147
+ def convert_tools_from_massgen(
148
+ self,
149
+ tools: List[Dict[str, Any]],
150
+ ) -> Any:
151
+ """
152
+ Convert MassGen tools to agent-specific format.
153
+
154
+ Override this method for agent-specific conversion.
155
+
156
+ Args:
157
+ tools: List of MassGen format tools
158
+
159
+ Returns:
160
+ agent-specific tool format
161
+ """
162
+ return tools
163
+
164
+ def is_stateful(self) -> bool:
165
+ """
166
+ Check if this adapter maintains conversation state.
167
+
168
+ Override if your agent is stateless.
169
+ """
170
+ return False
171
+
172
+ def clear_history(self) -> None:
173
+ """Clear conversation history."""
174
+ self._conversation_history.clear()
175
+
176
+ def reset_state(self) -> None:
177
+ """Reset adapter state."""
178
+ self.clear_history()
179
+ # Override to add agent-specific reset logic
180
+
181
+ def set_stage(self, stage: CoordinationStage) -> None:
182
+ """Set the coordination stage for the adapter, if applicable."""
183
+ self.coordination_stage = stage
File without changes
@@ -0,0 +1,439 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Unit tests for AG2Adapter (single agent case only).
4
+ """
5
+ import os
6
+ from unittest.mock import AsyncMock, MagicMock, patch
7
+
8
+ import pytest
9
+
10
+ from massgen.adapters.ag2_adapter import AG2Adapter
11
+ from massgen.adapters.utils.ag2_utils import setup_agent_from_config, setup_api_keys
12
+
13
+
14
+ def test_setup_api_keys_copies_gemini_key():
15
+ """Test that GEMINI_API_KEY is copied to GOOGLE_GEMINI_API_KEY."""
16
+ # Setup
17
+ os.environ["GEMINI_API_KEY"] = "test_key"
18
+ if "GOOGLE_GEMINI_API_KEY" in os.environ:
19
+ del os.environ["GOOGLE_GEMINI_API_KEY"]
20
+
21
+ # Execute
22
+ setup_api_keys()
23
+
24
+ # Verify
25
+ assert os.environ["GOOGLE_GEMINI_API_KEY"] == "test_key"
26
+
27
+ # Cleanup
28
+ del os.environ["GEMINI_API_KEY"]
29
+ del os.environ["GOOGLE_GEMINI_API_KEY"]
30
+
31
+
32
+ @patch("massgen.adapters.utils.ag2_utils.AssistantAgent")
33
+ def test_setup_agent_from_config_assistant(mock_assistant):
34
+ """Test setting up AssistantAgent from config."""
35
+ config = {
36
+ "type": "assistant",
37
+ "name": "test_agent",
38
+ "system_message": "You are helpful",
39
+ "llm_config": {
40
+ "api_type": "openai",
41
+ "model": "gpt-4o",
42
+ },
43
+ }
44
+
45
+ setup_agent_from_config(config)
46
+
47
+ # Verify AssistantAgent was called with correct params
48
+ mock_assistant.assert_called_once()
49
+ call_kwargs = mock_assistant.call_args[1]
50
+ assert call_kwargs["name"] == "test_agent"
51
+ assert call_kwargs["system_message"] == "You are helpful"
52
+ assert call_kwargs["human_input_mode"] == "NEVER"
53
+
54
+
55
+ @patch("massgen.adapters.utils.ag2_utils.ConversableAgent")
56
+ def test_setup_agent_from_config_conversable(mock_conversable):
57
+ """Test setting up ConversableAgent from config."""
58
+ config = {
59
+ "type": "conversable",
60
+ "name": "test_agent",
61
+ "llm_config": [{"api_type": "openai", "model": "gpt-4o"}],
62
+ }
63
+
64
+ setup_agent_from_config(config)
65
+
66
+ # Verify ConversableAgent was called
67
+ mock_conversable.assert_called_once()
68
+ call_kwargs = mock_conversable.call_args[1]
69
+ assert call_kwargs["name"] == "test_agent"
70
+ assert call_kwargs["human_input_mode"] == "NEVER"
71
+
72
+
73
+ def test_setup_agent_missing_llm_config():
74
+ """Test that missing llm_config raises error."""
75
+ config = {
76
+ "type": "assistant",
77
+ "name": "test_agent",
78
+ }
79
+
80
+ with pytest.raises(ValueError) as exc_info:
81
+ setup_agent_from_config(config)
82
+
83
+ assert "llm_config" in str(exc_info.value)
84
+
85
+
86
+ def test_setup_agent_missing_name():
87
+ """Test that missing name raises error."""
88
+ config = {
89
+ "type": "assistant",
90
+ "llm_config": [{"api_type": "openai", "model": "gpt-4o"}],
91
+ }
92
+
93
+ with pytest.raises(ValueError) as exc_info:
94
+ setup_agent_from_config(config)
95
+
96
+ assert "name" in str(exc_info.value)
97
+
98
+
99
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
100
+ def test_adapter_init_single_agent(mock_setup):
101
+ """Test adapter initialization with single agent config."""
102
+ mock_agent = MagicMock()
103
+ mock_setup.return_value = mock_agent
104
+
105
+ agent_config = {
106
+ "type": "assistant",
107
+ "name": "test",
108
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
109
+ }
110
+
111
+ adapter = AG2Adapter(agent_config=agent_config)
112
+
113
+ # Verify single agent setup
114
+ assert adapter.is_group_chat is False
115
+ assert adapter.agent == mock_agent
116
+ mock_setup.assert_called_once_with(agent_config)
117
+
118
+
119
+ def test_adapter_init_requires_config():
120
+ """Test that adapter requires either agent_config or group_config."""
121
+ with pytest.raises(ValueError) as exc_info:
122
+ AG2Adapter()
123
+
124
+ assert "agent_config" in str(exc_info.value) or "group_config" in str(exc_info.value)
125
+
126
+
127
+ def test_adapter_init_rejects_both_configs():
128
+ """Test that adapter rejects both agent_config and group_config."""
129
+ with pytest.raises(ValueError) as exc_info:
130
+ AG2Adapter(
131
+ agent_config={"name": "test", "llm_config": []},
132
+ group_config={"agents": []},
133
+ )
134
+
135
+ assert "not both" in str(exc_info.value).lower()
136
+
137
+
138
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
139
+ @pytest.mark.asyncio
140
+ async def test_execute_streaming_single_agent(mock_setup):
141
+ """Test streaming execution with single agent."""
142
+ # Setup mock agent
143
+ mock_agent = MagicMock()
144
+ mock_agent.a_generate_reply = AsyncMock(
145
+ return_value={"content": "Test response", "tool_calls": None},
146
+ )
147
+ mock_setup.return_value = mock_agent
148
+
149
+ # Create adapter
150
+ agent_config = {
151
+ "type": "assistant",
152
+ "name": "test",
153
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
154
+ }
155
+ adapter = AG2Adapter(agent_config=agent_config)
156
+
157
+ # Execute streaming
158
+ messages = [{"role": "user", "content": "Hello"}]
159
+ tools = []
160
+
161
+ chunks = []
162
+ async for chunk in adapter.execute_streaming(messages, tools):
163
+ chunks.append(chunk)
164
+
165
+ # Verify response
166
+ assert len(chunks) > 0
167
+ assert any(c.type == "content" for c in chunks)
168
+ assert any(c.type == "done" for c in chunks)
169
+
170
+ # Verify agent was called
171
+ mock_agent.a_generate_reply.assert_called_once_with(messages)
172
+
173
+
174
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
175
+ def test_register_tools_single_agent(mock_setup):
176
+ """Test tool registration with single agent."""
177
+ mock_agent = MagicMock()
178
+ mock_setup.return_value = mock_agent
179
+
180
+ agent_config = {
181
+ "type": "assistant",
182
+ "name": "test",
183
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
184
+ }
185
+ adapter = AG2Adapter(agent_config=agent_config)
186
+
187
+ # Register tools
188
+ tools = [
189
+ {
190
+ "type": "function",
191
+ "function": {"name": "search", "description": "Search tool"},
192
+ },
193
+ ]
194
+
195
+ adapter._register_tools(tools)
196
+
197
+ # Verify update_tool_signature was called for each tool
198
+ assert mock_agent.update_tool_signature.call_count == len(tools)
199
+
200
+
201
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
202
+ def test_register_tools_empty_list(mock_setup):
203
+ """Test that empty tool list doesn't call update_tool_signature."""
204
+ mock_agent = MagicMock()
205
+ mock_setup.return_value = mock_agent
206
+
207
+ agent_config = {
208
+ "type": "assistant",
209
+ "name": "test",
210
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
211
+ }
212
+ adapter = AG2Adapter(agent_config=agent_config)
213
+
214
+ # Register empty tools
215
+ adapter._register_tools([])
216
+
217
+ # Verify update_tool_signature was not called
218
+ mock_agent.update_tool_signature.assert_not_called()
219
+
220
+
221
+ @patch("autogen.coding.LocalCommandLineCodeExecutor")
222
+ @patch("massgen.adapters.utils.ag2_utils.AssistantAgent")
223
+ def test_setup_agent_with_local_code_executor(mock_assistant, mock_executor):
224
+ """Test setting up agent with LocalCommandLineCodeExecutor."""
225
+ config = {
226
+ "type": "assistant",
227
+ "name": "coder",
228
+ "llm_config": [{"api_type": "openai", "model": "gpt-4o"}],
229
+ "code_execution_config": {
230
+ "executor": {
231
+ "type": "LocalCommandLineCodeExecutor",
232
+ "timeout": 60,
233
+ "work_dir": "./workspace",
234
+ },
235
+ },
236
+ }
237
+
238
+ setup_agent_from_config(config)
239
+
240
+ # Verify executor was created without 'type' in params
241
+ mock_executor.assert_called_once_with(timeout=60, work_dir="./workspace")
242
+
243
+ # Verify AssistantAgent was called with code_execution_config
244
+ call_kwargs = mock_assistant.call_args[1]
245
+ assert "code_execution_config" in call_kwargs
246
+ assert "executor" in call_kwargs["code_execution_config"]
247
+
248
+
249
+ @patch("autogen.coding.DockerCommandLineCodeExecutor")
250
+ @patch("massgen.adapters.utils.ag2_utils.ConversableAgent")
251
+ def test_setup_agent_with_docker_executor(mock_conversable, mock_executor):
252
+ """Test setting up agent with DockerCommandLineCodeExecutor."""
253
+ config = {
254
+ "type": "conversable",
255
+ "name": "docker_coder",
256
+ "llm_config": [{"api_type": "openai", "model": "gpt-4o"}],
257
+ "code_execution_config": {
258
+ "executor": {
259
+ "type": "DockerCommandLineCodeExecutor",
260
+ "image": "python:3.10",
261
+ "timeout": 120,
262
+ },
263
+ },
264
+ }
265
+
266
+ setup_agent_from_config(config)
267
+
268
+ # Verify executor was created with correct params
269
+ mock_executor.assert_called_once_with(image="python:3.10", timeout=120)
270
+
271
+ # Verify ConversableAgent has code_execution_config
272
+ call_kwargs = mock_conversable.call_args[1]
273
+ assert "code_execution_config" in call_kwargs
274
+
275
+
276
+ def test_setup_agent_invalid_executor_type():
277
+ """Test that invalid executor type raises error."""
278
+ config = {
279
+ "type": "assistant",
280
+ "name": "coder",
281
+ "llm_config": [{"api_type": "openai", "model": "gpt-4o"}],
282
+ "code_execution_config": {
283
+ "executor": {
284
+ "type": "InvalidExecutor",
285
+ "timeout": 60,
286
+ },
287
+ },
288
+ }
289
+
290
+ with pytest.raises(ValueError) as exc_info:
291
+ setup_agent_from_config(config)
292
+
293
+ assert "Unsupported code executor type" in str(exc_info.value)
294
+ assert "InvalidExecutor" in str(exc_info.value)
295
+
296
+
297
+ def test_setup_agent_missing_executor_type():
298
+ """Test that missing executor type raises error."""
299
+ config = {
300
+ "type": "assistant",
301
+ "name": "coder",
302
+ "llm_config": [{"api_type": "openai", "model": "gpt-4o"}],
303
+ "code_execution_config": {
304
+ "executor": {
305
+ "timeout": 60,
306
+ },
307
+ },
308
+ }
309
+
310
+ with pytest.raises(ValueError) as exc_info:
311
+ setup_agent_from_config(config)
312
+
313
+ assert "must include 'type' field" in str(exc_info.value)
314
+
315
+
316
+ @patch("massgen.adapters.ag2_adapter.ConversableAgent")
317
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
318
+ @patch("massgen.adapters.ag2_adapter.AutoPattern")
319
+ def test_adapter_init_group_chat(mock_pattern, mock_setup, mock_conversable):
320
+ """Test adapter initialization with group chat config."""
321
+ mock_agent1 = MagicMock()
322
+ mock_agent1.name = "Agent1"
323
+ mock_agent2 = MagicMock()
324
+ mock_agent2.name = "Agent2"
325
+ mock_user_agent = MagicMock()
326
+ mock_user_agent.name = "User"
327
+
328
+ mock_setup.side_effect = [mock_agent1, mock_agent2]
329
+ mock_conversable.return_value = mock_user_agent
330
+
331
+ group_config = {
332
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
333
+ "agents": [
334
+ {"type": "assistant", "name": "Agent1", "llm_config": {"api_type": "openai", "model": "gpt-4o"}},
335
+ {"type": "assistant", "name": "Agent2", "llm_config": {"api_type": "openai", "model": "gpt-4o"}},
336
+ ],
337
+ "pattern": {
338
+ "type": "auto",
339
+ "initial_agent": "Agent1",
340
+ "group_manager_args": {"llm_config": {"api_type": "openai", "model": "gpt-4o"}},
341
+ },
342
+ }
343
+
344
+ adapter = AG2Adapter(group_config=group_config)
345
+
346
+ # Verify group chat setup
347
+ assert adapter.is_group_chat is True
348
+ assert len(adapter.agents) == 2
349
+ assert adapter.user_agent is not None
350
+ mock_pattern.assert_called_once()
351
+
352
+
353
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
354
+ def test_adapter_separate_workflow_and_other_tools(mock_setup):
355
+ """Test separation of workflow and other tools."""
356
+ mock_agent = MagicMock()
357
+ mock_setup.return_value = mock_agent
358
+
359
+ agent_config = {
360
+ "type": "assistant",
361
+ "name": "test",
362
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
363
+ }
364
+ adapter = AG2Adapter(agent_config=agent_config)
365
+
366
+ # Test tools with workflow and other tools
367
+ tools = [
368
+ {"type": "function", "function": {"name": "new_answer", "description": "Submit answer"}},
369
+ {"type": "function", "function": {"name": "vote", "description": "Vote for answer"}},
370
+ {"type": "function", "function": {"name": "search", "description": "Search tool"}},
371
+ ]
372
+
373
+ workflow_tools, other_tools = adapter._separate_workflow_and_other_tools(tools)
374
+
375
+ # Verify separation
376
+ assert len(workflow_tools) == 2
377
+ assert len(other_tools) == 1
378
+ assert any(t["function"]["name"] == "new_answer" for t in workflow_tools)
379
+ assert any(t["function"]["name"] == "vote" for t in workflow_tools)
380
+ assert other_tools[0]["function"]["name"] == "search"
381
+
382
+
383
+ @patch("massgen.adapters.ag2_adapter.ConversableAgent")
384
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
385
+ @patch("massgen.adapters.ag2_adapter.AutoPattern")
386
+ def test_adapter_setup_user_agent_custom(mock_pattern, mock_setup, mock_conversable):
387
+ """Test setting up custom user agent."""
388
+ mock_user_agent = MagicMock()
389
+ mock_user_agent.name = "User"
390
+ mock_agent = MagicMock()
391
+ mock_agent.name = "TestAgent"
392
+
393
+ # First call for expert agent, second for user agent
394
+ mock_setup.side_effect = [mock_agent, mock_user_agent]
395
+
396
+ group_config = {
397
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
398
+ "agents": [{"type": "assistant", "name": "TestAgent", "llm_config": {"api_type": "openai", "model": "gpt-4o"}}],
399
+ "pattern": {
400
+ "type": "auto",
401
+ "initial_agent": "TestAgent",
402
+ "group_manager_args": {"llm_config": {"api_type": "openai", "model": "gpt-4o"}},
403
+ },
404
+ "user_agent": {
405
+ "name": "User",
406
+ "system_message": "Custom user agent",
407
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
408
+ },
409
+ }
410
+
411
+ adapter = AG2Adapter(group_config=group_config)
412
+
413
+ # Verify custom user agent was set up
414
+ assert adapter.user_agent.name == "User"
415
+ # setup_agent_from_config should be called twice: once for expert agent, once for user agent
416
+ assert mock_setup.call_count == 2
417
+
418
+
419
+ @patch("massgen.adapters.ag2_adapter.setup_agent_from_config")
420
+ @patch("massgen.adapters.ag2_adapter.AutoPattern")
421
+ def test_adapter_invalid_pattern_type(mock_pattern, mock_setup):
422
+ """Test that invalid pattern type raises error."""
423
+ mock_agent = MagicMock()
424
+ mock_agent.name = "Agent1"
425
+ mock_setup.return_value = mock_agent
426
+
427
+ group_config = {
428
+ "llm_config": {"api_type": "openai", "model": "gpt-4o"},
429
+ "agents": [{"type": "assistant", "name": "Agent1", "llm_config": {"api_type": "openai", "model": "gpt-4o"}}],
430
+ "pattern": {
431
+ "type": "invalid_pattern",
432
+ "initial_agent": "Agent1",
433
+ },
434
+ }
435
+
436
+ with pytest.raises(NotImplementedError) as exc_info:
437
+ AG2Adapter(group_config=group_config)
438
+
439
+ assert "invalid_pattern" in str(exc_info.value)