massgen 0.0.3__py3-none-any.whl → 0.1.0__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 +1560 -275
  46. massgen/config_builder.py +2396 -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.0.dist-info/METADATA +1245 -0
  258. massgen-0.1.0.dist-info/RECORD +273 -0
  259. massgen-0.1.0.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.0.dist-info}/WHEEL +0 -0
  267. {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/licenses/LICENSE +0 -0
  268. {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,233 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Test script for Claude Code Context Sharing functionality.
4
+
5
+ This script tests the context sharing capabilities between multiple Claude Code agents,
6
+ ensuring that:
7
+ 1. Workspace snapshots are saved correctly
8
+ 2. Snapshots are restored with anonymization
9
+ 3. Agents can access each other's work through shared context
10
+ """
11
+
12
+ import shutil
13
+ from pathlib import Path
14
+ from unittest.mock import MagicMock
15
+
16
+ import pytest
17
+
18
+ from massgen.chat_agent import ChatAgent
19
+ from massgen.orchestrator import Orchestrator
20
+
21
+
22
+ class MockClaudeCodeBackend:
23
+ """Mock Claude Code backend for testing."""
24
+
25
+ def __init__(self, cwd: str = None):
26
+ self._cwd = cwd or "test_workspace"
27
+
28
+ def get_provider_name(self) -> str:
29
+ return "claude_code"
30
+
31
+
32
+ class MockClaudeCodeAgent(ChatAgent):
33
+ """Mock Claude Code agent for testing."""
34
+
35
+ def __init__(self, agent_id: str, cwd: str = None):
36
+ super().__init__(session_id=f"session_{agent_id}")
37
+ self.agent_id = agent_id
38
+ self.backend = MockClaudeCodeBackend(cwd)
39
+
40
+ async def chat(self, messages, tools=None, reset_chat=False, clear_history=False):
41
+ """Mock chat implementation."""
42
+ for _ in range(3):
43
+ yield {
44
+ "type": "content",
45
+ "content": f"Working on task from {self.agent_id}",
46
+ }
47
+ yield {"type": "result", "data": ("answer", f"Solution from {self.agent_id}")}
48
+
49
+
50
+ @pytest.fixture
51
+ def test_workspace(tmp_path):
52
+ """Create temporary test workspace."""
53
+ workspace = tmp_path / "test_context_sharing"
54
+ workspace.mkdir(exist_ok=True)
55
+
56
+ # Create test directories
57
+ snapshot_storage = workspace / "snapshots"
58
+ temp_workspace = workspace / "temp_workspaces"
59
+
60
+ snapshot_storage.mkdir(exist_ok=True)
61
+ temp_workspace.mkdir(exist_ok=True)
62
+
63
+ yield {
64
+ "workspace": workspace,
65
+ "snapshot_storage": str(snapshot_storage),
66
+ "temp_workspace": str(temp_workspace),
67
+ }
68
+
69
+ # Cleanup
70
+ if workspace.exists():
71
+ shutil.rmtree(workspace)
72
+
73
+
74
+ @pytest.fixture
75
+ def mock_agents():
76
+ """Create mock Claude Code agents."""
77
+ agents = {}
78
+ for i in range(1, 4):
79
+ agent_id = f"claude_code_{i}"
80
+ cwd = f"test_workspace/agent_{i}"
81
+ agents[agent_id] = MockClaudeCodeAgent(agent_id, cwd)
82
+ return agents
83
+
84
+
85
+ def test_orchestrator_initialization_with_context_sharing(test_workspace, mock_agents):
86
+ """Test orchestrator initializes with context sharing parameters."""
87
+
88
+ orchestrator = Orchestrator(
89
+ agents=mock_agents,
90
+ snapshot_storage=test_workspace["snapshot_storage"],
91
+ agent_temporary_workspace=test_workspace["temp_workspace"],
92
+ )
93
+
94
+ # Check that parameters are set
95
+ assert orchestrator._snapshot_storage == test_workspace["snapshot_storage"]
96
+ assert orchestrator._agent_temporary_workspace == test_workspace["temp_workspace"]
97
+
98
+ # Check that agent ID mappings are created
99
+ assert len(orchestrator._agent_id_mapping) == 3
100
+ assert "claude_code_1" in orchestrator._agent_id_mapping
101
+ assert "claude_code_2" in orchestrator._agent_id_mapping
102
+ assert "claude_code_3" in orchestrator._agent_id_mapping
103
+
104
+ # Check anonymized IDs
105
+ assert orchestrator._agent_id_mapping["claude_code_1"] == "agent_1"
106
+ assert orchestrator._agent_id_mapping["claude_code_2"] == "agent_2"
107
+ assert orchestrator._agent_id_mapping["claude_code_3"] == "agent_3"
108
+
109
+ # Check directories are created
110
+ assert Path(test_workspace["snapshot_storage"]).exists()
111
+ assert Path(test_workspace["temp_workspace"]).exists()
112
+
113
+ for agent_id in mock_agents.keys():
114
+ snapshot_dir = Path(test_workspace["snapshot_storage"]) / agent_id
115
+ temp_dir = Path(test_workspace["temp_workspace"]) / agent_id
116
+ assert snapshot_dir.exists()
117
+ assert temp_dir.exists()
118
+
119
+
120
+ @pytest.mark.asyncio
121
+ async def test_snapshot_saving(test_workspace, mock_agents):
122
+ """Test that snapshots are saved correctly when agents complete tasks."""
123
+
124
+ orchestrator = Orchestrator(
125
+ agents=mock_agents,
126
+ snapshot_storage=test_workspace["snapshot_storage"],
127
+ agent_temporary_workspace=test_workspace["temp_workspace"],
128
+ )
129
+
130
+ # Create some test files in agent workspaces
131
+ for agent_id, agent in mock_agents.items():
132
+ workspace = Path(agent.backend._cwd)
133
+ workspace.mkdir(parents=True, exist_ok=True)
134
+
135
+ # Create test files
136
+ (workspace / f"code_{agent_id}.py").write_text(f"# Code from {agent_id}")
137
+ (workspace / f"test_{agent_id}.txt").write_text(f"Test data from {agent_id}")
138
+
139
+ # Save snapshot for one agent
140
+ await orchestrator._save_claude_code_snapshot("claude_code_1")
141
+
142
+ # Check snapshot was saved
143
+ snapshot_dir = Path(test_workspace["snapshot_storage"]) / "claude_code_1"
144
+ assert (snapshot_dir / "code_claude_code_1.py").exists()
145
+ assert (snapshot_dir / "test_claude_code_1.txt").exists()
146
+
147
+
148
+ @pytest.mark.asyncio
149
+ async def test_workspace_restoration_with_anonymization(test_workspace, mock_agents):
150
+ """Test that workspaces are restored with anonymized agent IDs."""
151
+
152
+ orchestrator = Orchestrator(
153
+ agents=mock_agents,
154
+ snapshot_storage=test_workspace["snapshot_storage"],
155
+ agent_temporary_workspace=test_workspace["temp_workspace"],
156
+ )
157
+
158
+ # Create snapshots for all agents
159
+ for agent_id, agent in mock_agents.items():
160
+ workspace = Path(agent.backend._cwd)
161
+ workspace.mkdir(parents=True, exist_ok=True)
162
+ (workspace / f"work_{agent_id}.txt").write_text(f"Work from {agent_id}")
163
+
164
+ # Save to snapshot
165
+ await orchestrator._save_claude_code_snapshot(agent_id)
166
+
167
+ # Restore workspace for agent 2
168
+ workspace_path = await orchestrator._restore_snapshots_to_workspace("claude_code_2")
169
+
170
+ assert workspace_path is not None
171
+ workspace_dir = Path(workspace_path)
172
+
173
+ # Check that all snapshots are restored with anonymized names
174
+ assert (workspace_dir / "agent_1" / "work_claude_code_1.txt").exists()
175
+ assert (workspace_dir / "agent_2" / "work_claude_code_2.txt").exists()
176
+ assert (workspace_dir / "agent_3" / "work_claude_code_3.txt").exists()
177
+
178
+ # Verify content
179
+ content1 = (workspace_dir / "agent_1" / "work_claude_code_1.txt").read_text()
180
+ assert content1 == "Work from claude_code_1"
181
+
182
+
183
+ @pytest.mark.asyncio
184
+ async def test_save_all_snapshots(test_workspace, mock_agents):
185
+ """Test saving all Claude Code agent snapshots at once."""
186
+
187
+ orchestrator = Orchestrator(
188
+ agents=mock_agents,
189
+ snapshot_storage=test_workspace["snapshot_storage"],
190
+ agent_temporary_workspace=test_workspace["temp_workspace"],
191
+ )
192
+
193
+ # Create test files in all agent workspaces
194
+ for agent_id, agent in mock_agents.items():
195
+ workspace = Path(agent.backend._cwd)
196
+ workspace.mkdir(parents=True, exist_ok=True)
197
+ (workspace / "shared.py").write_text(f"# Shared code from {agent_id}")
198
+
199
+ # Save all snapshots
200
+ await orchestrator._save_all_claude_code_snapshots()
201
+
202
+ # Verify all snapshots were saved
203
+ for agent_id in mock_agents.keys():
204
+ snapshot_dir = Path(test_workspace["snapshot_storage"]) / agent_id
205
+ assert (snapshot_dir / "shared.py").exists()
206
+ content = (snapshot_dir / "shared.py").read_text()
207
+ assert agent_id in content
208
+
209
+
210
+ def test_non_claude_code_agents_ignored(test_workspace):
211
+ """Test that non-Claude Code agents are ignored for context sharing."""
212
+
213
+ # Create mixed agents (some Claude Code, some not)
214
+ agents = {
215
+ "claude_code_1": MockClaudeCodeAgent("claude_code_1"),
216
+ "regular_agent": MagicMock(backend=MagicMock(get_provider_name=lambda: "openai")),
217
+ }
218
+
219
+ orchestrator = Orchestrator(
220
+ agents=agents,
221
+ snapshot_storage=test_workspace["snapshot_storage"],
222
+ agent_temporary_workspace=test_workspace["temp_workspace"],
223
+ )
224
+
225
+ # Only Claude Code agents should have mappings
226
+ assert "claude_code_1" in orchestrator._agent_id_mapping
227
+ assert "regular_agent" not in orchestrator._agent_id_mapping
228
+ assert len(orchestrator._agent_id_mapping) == 1
229
+
230
+
231
+ if __name__ == "__main__":
232
+ # Run tests
233
+ pytest.main([__file__, "-v"])
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Test ClaudeCodeBackend with MassGen Orchestrator.
5
+ This test demonstrates a workflow with Claude Code backend.
6
+ """
7
+
8
+ import asyncio
9
+ import os
10
+ import sys
11
+
12
+ from massgen.agent_config import AgentConfig
13
+ from massgen.backend.claude_code import ClaudeCodeBackend
14
+ from massgen.chat_agent import ConfigurableAgent
15
+ from massgen.frontend.coordination_ui import CoordinationUI
16
+ from massgen.orchestrator import Orchestrator
17
+
18
+ sys.path.insert(0, "/workspaces/MassGen")
19
+
20
+
21
+ async def test_claude_code_with_orchestrator():
22
+ """Test Claude Code backend with MassGen Orchestrator."""
23
+
24
+ print("🚀 Testing Claude Code Backend with MassGen Orchestrator")
25
+ print("=" * 60)
26
+
27
+ # Create Claude Code backend
28
+ backend = ClaudeCodeBackend()
29
+
30
+ print(f"✅ Backend initialized: {backend.get_provider_name()}")
31
+ print(f"📊 Stateful backend: {backend.is_stateful()}")
32
+ print(f"🛠️ Supported tools: {len(backend.get_supported_builtin_tools())} tools")
33
+
34
+ # Create agent configuration
35
+ agent_config = AgentConfig(
36
+ agent_id="claude_code_agent",
37
+ # custom_system_instruction="You are a helpful AI assistant. Provide clear, accurate answers.",
38
+ backend_params={
39
+ "model": "claude-sonnet-4-20250514",
40
+ },
41
+ )
42
+
43
+ # Create configurable agent
44
+ agent = ConfigurableAgent(config=agent_config, backend=backend)
45
+
46
+ print(f"✅ Agent created: {agent.agent_id}")
47
+ print(f"📋 Agent status: {agent.get_status()}")
48
+
49
+ # Create orchestrator
50
+ orchestrator = Orchestrator(agents={agent.agent_id: agent})
51
+ print(f"✅ Orchestrator created with {len(orchestrator.agents)} agent(s)")
52
+
53
+ # Create coordination UI
54
+ ui = CoordinationUI(display_type="rich_terminal", logging_enabled=True)
55
+ print(f"✅ Coordination UI created: {ui.display_type}")
56
+
57
+ # Test question
58
+ question = "2+2=?"
59
+
60
+ print(f"\n📝 Test Question: {question}")
61
+ print("\n🔄 Starting orchestrator coordination...")
62
+ print("=" * 60)
63
+
64
+ try:
65
+ # Run orchestrator coordination
66
+ final_response = await ui.coordinate(orchestrator, question)
67
+
68
+ print("\n" + "=" * 60)
69
+ print("✅ Orchestrator coordination completed!")
70
+ print(f"📊 Final response length: {len(final_response)} characters")
71
+
72
+ # Display backend statistics
73
+ token_usage = backend.get_token_usage()
74
+ print("\n📈 Backend Statistics:")
75
+ print(f" Input tokens: {token_usage.input_tokens}")
76
+ print(f" Output tokens: {token_usage.output_tokens}")
77
+ print(f" Estimated cost: ${token_usage.estimated_cost:.4f}")
78
+ print(f" Session ID: {backend.get_current_session_id()}")
79
+
80
+ # Display final response
81
+ if final_response:
82
+ print("\n📄 Final Response:")
83
+ print("-" * 40)
84
+ print(final_response)
85
+ print("-" * 40)
86
+
87
+ except Exception as e:
88
+ print(f"❌ Error during orchestrator test: {e}")
89
+ import traceback
90
+
91
+ traceback.print_exc()
92
+ return
93
+
94
+ print("\n✅ Orchestrator test completed successfully!")
95
+
96
+
97
+ async def test_stateful_behavior():
98
+ """Test the stateful behavior of Claude Code backend."""
99
+
100
+ if not os.getenv("ANTHROPIC_API_KEY"):
101
+ print("❌ ANTHROPIC_API_KEY not found - skipping stateful test")
102
+ return
103
+
104
+ print("\n" + "=" * 60)
105
+ print("🚀 Testing Stateful Behavior")
106
+ print("=" * 60)
107
+
108
+ # Create backend and agent
109
+ backend = ClaudeCodeBackend(model="claude-sonnet-4-20250514")
110
+ config = AgentConfig(
111
+ agent_id="stateful_agent",
112
+ backend_params={
113
+ "model": "claude-sonnet-4-20250514",
114
+ "append_system_prompt": "You are a helpful assistant. Remember our conversation context.",
115
+ },
116
+ )
117
+ agent = ConfigurableAgent(config=config, backend=backend)
118
+
119
+ print(f"✅ Stateful agent created: {backend.is_stateful()}")
120
+ print(f"🔗 Initial session ID: {backend.get_current_session_id()}")
121
+
122
+ # Test conversation continuity
123
+ messages1 = [{"role": "user", "content": "My favorite color is blue. Please remember this."}]
124
+ messages2 = [{"role": "user", "content": "What did I just tell you about my favorite color?"}]
125
+
126
+ try:
127
+ print("\n📝 Turn 1: Setting context...")
128
+ response1 = ""
129
+ async for chunk in agent.chat(messages1):
130
+ if chunk.type == "content" and chunk.content:
131
+ response1 += chunk.content
132
+
133
+ print(f"✅ Turn 1 completed. Session: {backend.get_current_session_id()}")
134
+ print(f"📄 Response 1 preview: {response1[:100]}...")
135
+
136
+ print("\n📝 Turn 2: Testing memory...")
137
+ response2 = ""
138
+ async for chunk in agent.chat(messages2):
139
+ if chunk.type == "content" and chunk.content:
140
+ response2 += chunk.content
141
+
142
+ print(f"✅ Turn 2 completed. Session: {backend.get_current_session_id()}")
143
+ print(f"📄 Response 2 preview: {response2[:100]}...")
144
+
145
+ # Check if context was maintained
146
+ if "blue" in response2.lower():
147
+ print("✅ Stateful behavior confirmed - context maintained!")
148
+ else:
149
+ print("⚠️ Context may not have been maintained")
150
+
151
+ print(f"\n📊 Token usage after 2 turns: {backend.get_token_usage()}")
152
+
153
+ except Exception as e:
154
+ print(f"❌ Error during stateful test: {e}")
155
+ import traceback
156
+
157
+ traceback.print_exc()
158
+ return
159
+
160
+ print("\n✅ Stateful behavior test completed!")
161
+
162
+
163
+ async def main():
164
+ """Run all Claude Code tests."""
165
+ print("🧪 Claude Code Backend + Orchestrator Tests")
166
+ print("=" * 60)
167
+
168
+ await test_claude_code_with_orchestrator()
169
+ # await test_stateful_behavior()
170
+
171
+ print("\n🎉 All tests completed!")
172
+
173
+
174
+ if __name__ == "__main__":
175
+ asyncio.run(main())
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Test script for CLI backends - Claude Code CLI and Gemini CLI integration.
5
+
6
+ This script tests the basic functionality of CLI backends without requiring
7
+ the actual CLI tools to be installed (mocked for testing).
8
+
9
+ TODO: This file is outdated - ClaudeCodeCLIBackend was removed, only SDK-based ClaudeCodeBackend remains.
10
+ Update tests to reflect current backend architecture.
11
+ """
12
+
13
+ import asyncio
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ # Add project root to path
18
+ project_root = Path(__file__).parent
19
+ sys.path.insert(0, str(project_root))
20
+
21
+ try:
22
+ # from massgen.backend.claude_code_cli import ClaudeCodeCLIBackend # File removed
23
+ from massgen.backend.cli_base import CLIBackend
24
+ except ImportError as e:
25
+ print(f"❌ Import error: {e}")
26
+ print("Make sure you're running from the project root directory")
27
+ sys.exit(1)
28
+
29
+
30
+ class MockCLIBackend(CLIBackend):
31
+ """Mock CLI backend for testing purposes."""
32
+
33
+ def __init__(self, cli_command: str, mock_output: str = "Mock response", **kwargs):
34
+ self.mock_output = mock_output
35
+ # Skip the actual CLI tool check
36
+ self.cli_command = cli_command
37
+ self.working_dir = kwargs.get("working_dir", Path.cwd())
38
+ self.timeout = kwargs.get("timeout", 300)
39
+ self.config = kwargs
40
+ from massgen.backend.base import TokenUsage
41
+
42
+ self.token_usage = TokenUsage()
43
+
44
+ def _build_command(self, messages, tools, **kwargs):
45
+ return ["echo", "mock command"]
46
+
47
+ def _parse_output(self, output):
48
+ return {"content": self.mock_output, "tool_calls": [], "raw_response": output}
49
+
50
+ async def _execute_cli_command(self, command):
51
+ """Mock command execution."""
52
+ await asyncio.sleep(0.1) # Simulate some delay
53
+ return self.mock_output
54
+
55
+ def get_cost_per_token(self):
56
+ """Mock cost per token."""
57
+ return {"input": 0.001, "output": 0.002}
58
+
59
+
60
+ async def test_cli_base_functionality():
61
+ """Test the CLI base class functionality."""
62
+ print("🧪 Testing CLI base functionality...")
63
+
64
+ backend = MockCLIBackend("mock-cli", "Hello from mock CLI!")
65
+
66
+ messages = [{"role": "user", "content": "Test message"}]
67
+ tools = []
68
+
69
+ chunks = []
70
+ async for chunk in backend.stream_with_tools(messages, tools):
71
+ chunks.append(chunk)
72
+
73
+ assert len(chunks) > 0, "Should produce at least one chunk"
74
+ assert any(chunk.type == "content" for chunk in chunks), "Should have content chunk"
75
+ assert any(chunk.type == "done" for chunk in chunks), "Should have done chunk"
76
+
77
+ print("✅ CLI base functionality test passed")
78
+
79
+
80
+ def test_claude_code_cli_command_building():
81
+ """Test Claude Code CLI command building (without executing) - SKIPPED: File removed."""
82
+ print("🧪 Testing Claude Code CLI command building... SKIPPED (file removed)")
83
+ print("✅ Claude Code CLI command building test skipped")
84
+
85
+ # NOTE: ClaudeCodeCLIBackend was removed, only ClaudeCodeBackend (SDK-based) remains
86
+
87
+
88
+ def test_configuration_files():
89
+ """Test that configuration files are valid."""
90
+ print("🧪 Testing configuration files...")
91
+
92
+ import yaml
93
+
94
+ config_files = [
95
+ "massgen/configs/claude_code_cli.yaml",
96
+ "massgen/configs/cli_backends_mixed.yaml",
97
+ ]
98
+
99
+ for config_file in config_files:
100
+ if Path(config_file).exists():
101
+ try:
102
+ with open(config_file, "r") as f:
103
+ config = yaml.safe_load(f)
104
+ assert config is not None, f"Config {config_file} should not be empty"
105
+ print(f"✅ {config_file} is valid")
106
+ except Exception as e:
107
+ print(f"❌ {config_file} is invalid: {e}")
108
+ raise
109
+ else:
110
+ print(f"⚠️ {config_file} not found, skipping")
111
+
112
+
113
+ async def test_end_to_end_mock():
114
+ """Test end-to-end functionality with mocked CLI execution."""
115
+ print("🧪 Testing end-to-end with mock execution...")
116
+
117
+ # Test Claude Code CLI mock
118
+ claude_backend = MockCLIBackend("claude", '{"response": "4", "reasoning": "2+2 equals 4"}')
119
+
120
+ messages = [{"role": "user", "content": "What is 2+2?"}]
121
+ tools = []
122
+
123
+ chunks = []
124
+ async for chunk in claude_backend.stream_with_tools(messages, tools):
125
+ chunks.append(chunk)
126
+ print(f" 📝 Chunk: {chunk.type} - {chunk.content}")
127
+
128
+ assert len(chunks) >= 3, "Should have content, complete_message, and done chunks"
129
+
130
+ print("✅ End-to-end mock test passed")
131
+
132
+
133
+ async def main():
134
+ """Run all tests."""
135
+ print("🚀 Starting CLI backend tests...\n")
136
+
137
+ try:
138
+ # Test basic functionality
139
+ await test_cli_base_functionality()
140
+ print()
141
+
142
+ # Test command building
143
+ test_claude_code_cli_command_building()
144
+ print()
145
+
146
+ # Test configuration files
147
+ test_configuration_files()
148
+ print()
149
+
150
+ # Test end-to-end mock
151
+ await test_end_to_end_mock()
152
+ print()
153
+
154
+ print("🎉 All CLI backend tests passed!")
155
+
156
+ # Show usage information
157
+ print("\n📋 Usage Information:")
158
+ print("CLI backends are now available in MassGen!")
159
+ print()
160
+ print("Prerequisites:")
161
+ print(" • Claude Code CLI: npm install -g @anthropic-ai/claude-code")
162
+ print(" • Gemini CLI: npm install -g @google/gemini-cli")
163
+ print()
164
+ print("Usage examples:")
165
+ print(" # Claude Code (SDK-based)")
166
+ print(" uv run python -m massgen.cli --backend claude_code --model claude-sonnet-4-20250514 'What is 2+2?'")
167
+ print()
168
+ print(" # Mixed CLI backends")
169
+ print(" uv run python -m massgen.cli --config massgen/configs/cli_backends_mixed.yaml 'Complex question'")
170
+
171
+ except Exception as e:
172
+ print(f"❌ Test failed: {e}")
173
+ import traceback
174
+
175
+ traceback.print_exc()
176
+ sys.exit(1)
177
+
178
+
179
+ if __name__ == "__main__":
180
+ asyncio.run(main())