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.
- massgen/__init__.py +142 -8
- massgen/adapters/__init__.py +29 -0
- massgen/adapters/ag2_adapter.py +483 -0
- massgen/adapters/base.py +183 -0
- massgen/adapters/tests/__init__.py +0 -0
- massgen/adapters/tests/test_ag2_adapter.py +439 -0
- massgen/adapters/tests/test_agent_adapter.py +128 -0
- massgen/adapters/utils/__init__.py +2 -0
- massgen/adapters/utils/ag2_utils.py +236 -0
- massgen/adapters/utils/tests/__init__.py +0 -0
- massgen/adapters/utils/tests/test_ag2_utils.py +138 -0
- massgen/agent_config.py +329 -55
- massgen/api_params_handler/__init__.py +10 -0
- massgen/api_params_handler/_api_params_handler_base.py +99 -0
- massgen/api_params_handler/_chat_completions_api_params_handler.py +176 -0
- massgen/api_params_handler/_claude_api_params_handler.py +113 -0
- massgen/api_params_handler/_response_api_params_handler.py +130 -0
- massgen/backend/__init__.py +39 -4
- massgen/backend/azure_openai.py +385 -0
- massgen/backend/base.py +341 -69
- massgen/backend/base_with_mcp.py +1102 -0
- massgen/backend/capabilities.py +386 -0
- massgen/backend/chat_completions.py +577 -130
- massgen/backend/claude.py +1033 -537
- massgen/backend/claude_code.py +1203 -0
- massgen/backend/cli_base.py +209 -0
- massgen/backend/docs/BACKEND_ARCHITECTURE.md +126 -0
- massgen/backend/{CLAUDE_API_RESEARCH.md → docs/CLAUDE_API_RESEARCH.md} +18 -18
- massgen/backend/{GEMINI_API_DOCUMENTATION.md → docs/GEMINI_API_DOCUMENTATION.md} +9 -9
- massgen/backend/docs/Gemini MCP Integration Analysis.md +1050 -0
- massgen/backend/docs/MCP_IMPLEMENTATION_CLAUDE_BACKEND.md +177 -0
- massgen/backend/docs/MCP_INTEGRATION_RESPONSE_BACKEND.md +352 -0
- massgen/backend/docs/OPENAI_GPT5_MODELS.md +211 -0
- massgen/backend/{OPENAI_RESPONSES_API_FORMAT.md → docs/OPENAI_RESPONSE_API_TOOL_CALLS.md} +3 -3
- massgen/backend/docs/OPENAI_response_streaming.md +20654 -0
- massgen/backend/docs/inference_backend.md +257 -0
- massgen/backend/docs/permissions_and_context_files.md +1085 -0
- massgen/backend/external.py +126 -0
- massgen/backend/gemini.py +1850 -241
- massgen/backend/grok.py +40 -156
- massgen/backend/inference.py +156 -0
- massgen/backend/lmstudio.py +171 -0
- massgen/backend/response.py +1095 -322
- massgen/chat_agent.py +131 -113
- massgen/cli.py +1560 -275
- massgen/config_builder.py +2396 -0
- massgen/configs/BACKEND_CONFIGURATION.md +458 -0
- massgen/configs/README.md +559 -216
- massgen/configs/ag2/ag2_case_study.yaml +27 -0
- massgen/configs/ag2/ag2_coder.yaml +34 -0
- massgen/configs/ag2/ag2_coder_case_study.yaml +36 -0
- massgen/configs/ag2/ag2_gemini.yaml +27 -0
- massgen/configs/ag2/ag2_groupchat.yaml +108 -0
- massgen/configs/ag2/ag2_groupchat_gpt.yaml +118 -0
- massgen/configs/ag2/ag2_single_agent.yaml +21 -0
- massgen/configs/basic/multi/fast_timeout_example.yaml +37 -0
- massgen/configs/basic/multi/gemini_4o_claude.yaml +31 -0
- massgen/configs/basic/multi/gemini_gpt5nano_claude.yaml +36 -0
- massgen/configs/{gemini_4o_claude.yaml → basic/multi/geminicode_4o_claude.yaml} +3 -3
- massgen/configs/basic/multi/geminicode_gpt5nano_claude.yaml +36 -0
- massgen/configs/basic/multi/glm_gemini_claude.yaml +25 -0
- massgen/configs/basic/multi/gpt4o_audio_generation.yaml +30 -0
- massgen/configs/basic/multi/gpt4o_image_generation.yaml +31 -0
- massgen/configs/basic/multi/gpt5nano_glm_qwen.yaml +26 -0
- massgen/configs/basic/multi/gpt5nano_image_understanding.yaml +26 -0
- massgen/configs/{three_agents_default.yaml → basic/multi/three_agents_default.yaml} +8 -4
- massgen/configs/basic/multi/three_agents_opensource.yaml +27 -0
- massgen/configs/basic/multi/three_agents_vllm.yaml +20 -0
- massgen/configs/basic/multi/two_agents_gemini.yaml +19 -0
- massgen/configs/{two_agents.yaml → basic/multi/two_agents_gpt5.yaml} +14 -6
- massgen/configs/basic/multi/two_agents_opensource_lmstudio.yaml +31 -0
- massgen/configs/basic/multi/two_qwen_vllm_sglang.yaml +28 -0
- massgen/configs/{single_agent.yaml → basic/single/single_agent.yaml} +1 -1
- massgen/configs/{single_flash2.5.yaml → basic/single/single_flash2.5.yaml} +1 -2
- massgen/configs/basic/single/single_gemini2.5pro.yaml +16 -0
- massgen/configs/basic/single/single_gpt4o_audio_generation.yaml +22 -0
- massgen/configs/basic/single/single_gpt4o_image_generation.yaml +22 -0
- massgen/configs/basic/single/single_gpt4o_video_generation.yaml +24 -0
- massgen/configs/basic/single/single_gpt5nano.yaml +20 -0
- massgen/configs/basic/single/single_gpt5nano_file_search.yaml +18 -0
- massgen/configs/basic/single/single_gpt5nano_image_understanding.yaml +17 -0
- massgen/configs/basic/single/single_gptoss120b.yaml +15 -0
- massgen/configs/basic/single/single_openrouter_audio_understanding.yaml +15 -0
- massgen/configs/basic/single/single_qwen_video_understanding.yaml +15 -0
- massgen/configs/debug/code_execution/command_filtering_blacklist.yaml +29 -0
- massgen/configs/debug/code_execution/command_filtering_whitelist.yaml +28 -0
- massgen/configs/debug/code_execution/docker_verification.yaml +29 -0
- massgen/configs/debug/skip_coordination_test.yaml +27 -0
- massgen/configs/debug/test_sdk_migration.yaml +17 -0
- massgen/configs/docs/DISCORD_MCP_SETUP.md +208 -0
- massgen/configs/docs/TWITTER_MCP_ENESCINAR_SETUP.md +82 -0
- massgen/configs/providers/azure/azure_openai_multi.yaml +21 -0
- massgen/configs/providers/azure/azure_openai_single.yaml +19 -0
- massgen/configs/providers/claude/claude.yaml +14 -0
- massgen/configs/providers/gemini/gemini_gpt5nano.yaml +28 -0
- massgen/configs/providers/local/lmstudio.yaml +11 -0
- massgen/configs/providers/openai/gpt5.yaml +46 -0
- massgen/configs/providers/openai/gpt5_nano.yaml +46 -0
- massgen/configs/providers/others/grok_single_agent.yaml +19 -0
- massgen/configs/providers/others/zai_coding_team.yaml +108 -0
- massgen/configs/providers/others/zai_glm45.yaml +12 -0
- massgen/configs/{creative_team.yaml → teams/creative/creative_team.yaml} +16 -6
- massgen/configs/{travel_planning.yaml → teams/creative/travel_planning.yaml} +16 -6
- massgen/configs/{news_analysis.yaml → teams/research/news_analysis.yaml} +16 -6
- massgen/configs/{research_team.yaml → teams/research/research_team.yaml} +15 -7
- massgen/configs/{technical_analysis.yaml → teams/research/technical_analysis.yaml} +16 -6
- massgen/configs/tools/code-execution/basic_command_execution.yaml +25 -0
- massgen/configs/tools/code-execution/code_execution_use_case_simple.yaml +41 -0
- massgen/configs/tools/code-execution/docker_claude_code.yaml +32 -0
- massgen/configs/tools/code-execution/docker_multi_agent.yaml +32 -0
- massgen/configs/tools/code-execution/docker_simple.yaml +29 -0
- massgen/configs/tools/code-execution/docker_with_resource_limits.yaml +32 -0
- massgen/configs/tools/code-execution/multi_agent_playwright_automation.yaml +57 -0
- massgen/configs/tools/filesystem/cc_gpt5_gemini_filesystem.yaml +34 -0
- massgen/configs/tools/filesystem/claude_code_context_sharing.yaml +68 -0
- massgen/configs/tools/filesystem/claude_code_flash2.5.yaml +43 -0
- massgen/configs/tools/filesystem/claude_code_flash2.5_gptoss.yaml +49 -0
- massgen/configs/tools/filesystem/claude_code_gpt5nano.yaml +31 -0
- massgen/configs/tools/filesystem/claude_code_single.yaml +40 -0
- massgen/configs/tools/filesystem/fs_permissions_test.yaml +87 -0
- massgen/configs/tools/filesystem/gemini_gemini_workspace_cleanup.yaml +54 -0
- massgen/configs/tools/filesystem/gemini_gpt5_filesystem_casestudy.yaml +30 -0
- massgen/configs/tools/filesystem/gemini_gpt5nano_file_context_path.yaml +43 -0
- massgen/configs/tools/filesystem/gemini_gpt5nano_protected_paths.yaml +45 -0
- massgen/configs/tools/filesystem/gpt5mini_cc_fs_context_path.yaml +31 -0
- massgen/configs/tools/filesystem/grok4_gpt5_gemini_filesystem.yaml +32 -0
- massgen/configs/tools/filesystem/multiturn/grok4_gpt5_claude_code_filesystem_multiturn.yaml +58 -0
- massgen/configs/tools/filesystem/multiturn/grok4_gpt5_gemini_filesystem_multiturn.yaml +58 -0
- massgen/configs/tools/filesystem/multiturn/two_claude_code_filesystem_multiturn.yaml +47 -0
- massgen/configs/tools/filesystem/multiturn/two_gemini_flash_filesystem_multiturn.yaml +48 -0
- massgen/configs/tools/mcp/claude_code_discord_mcp_example.yaml +27 -0
- massgen/configs/tools/mcp/claude_code_simple_mcp.yaml +35 -0
- massgen/configs/tools/mcp/claude_code_twitter_mcp_example.yaml +32 -0
- massgen/configs/tools/mcp/claude_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/claude_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/five_agents_travel_mcp_test.yaml +157 -0
- massgen/configs/tools/mcp/five_agents_weather_mcp_test.yaml +103 -0
- massgen/configs/tools/mcp/gemini_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test.yaml +23 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_sharing.yaml +23 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_single_agent.yaml +17 -0
- massgen/configs/tools/mcp/gemini_mcp_filesystem_test_with_claude_code.yaml +24 -0
- massgen/configs/tools/mcp/gemini_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/gemini_notion_mcp.yaml +52 -0
- massgen/configs/tools/mcp/gpt5_nano_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/gpt5_nano_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/gpt5mini_claude_code_discord_mcp_example.yaml +38 -0
- massgen/configs/tools/mcp/gpt_oss_mcp_example.yaml +25 -0
- massgen/configs/tools/mcp/gpt_oss_mcp_test.yaml +28 -0
- massgen/configs/tools/mcp/grok3_mini_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/grok3_mini_mcp_test.yaml +27 -0
- massgen/configs/tools/mcp/multimcp_gemini.yaml +111 -0
- massgen/configs/tools/mcp/qwen_api_mcp_example.yaml +25 -0
- massgen/configs/tools/mcp/qwen_api_mcp_test.yaml +28 -0
- massgen/configs/tools/mcp/qwen_local_mcp_example.yaml +24 -0
- massgen/configs/tools/mcp/qwen_local_mcp_test.yaml +27 -0
- massgen/configs/tools/planning/five_agents_discord_mcp_planning_mode.yaml +140 -0
- massgen/configs/tools/planning/five_agents_filesystem_mcp_planning_mode.yaml +151 -0
- massgen/configs/tools/planning/five_agents_notion_mcp_planning_mode.yaml +151 -0
- massgen/configs/tools/planning/five_agents_twitter_mcp_planning_mode.yaml +155 -0
- massgen/configs/tools/planning/gpt5_mini_case_study_mcp_planning_mode.yaml +73 -0
- massgen/configs/tools/web-search/claude_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gemini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gpt5_mini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/gpt_oss_streamable_http_test.yaml +44 -0
- massgen/configs/tools/web-search/grok3_mini_streamable_http_test.yaml +43 -0
- massgen/configs/tools/web-search/qwen_api_streamable_http_test.yaml +44 -0
- massgen/configs/tools/web-search/qwen_local_streamable_http_test.yaml +43 -0
- massgen/coordination_tracker.py +708 -0
- massgen/docker/README.md +462 -0
- massgen/filesystem_manager/__init__.py +21 -0
- massgen/filesystem_manager/_base.py +9 -0
- massgen/filesystem_manager/_code_execution_server.py +545 -0
- massgen/filesystem_manager/_docker_manager.py +477 -0
- massgen/filesystem_manager/_file_operation_tracker.py +248 -0
- massgen/filesystem_manager/_filesystem_manager.py +813 -0
- massgen/filesystem_manager/_path_permission_manager.py +1261 -0
- massgen/filesystem_manager/_workspace_tools_server.py +1815 -0
- massgen/formatter/__init__.py +10 -0
- massgen/formatter/_chat_completions_formatter.py +284 -0
- massgen/formatter/_claude_formatter.py +235 -0
- massgen/formatter/_formatter_base.py +156 -0
- massgen/formatter/_response_formatter.py +263 -0
- massgen/frontend/__init__.py +1 -2
- massgen/frontend/coordination_ui.py +471 -286
- massgen/frontend/displays/base_display.py +56 -11
- massgen/frontend/displays/create_coordination_table.py +1956 -0
- massgen/frontend/displays/rich_terminal_display.py +1259 -619
- massgen/frontend/displays/simple_display.py +9 -4
- massgen/frontend/displays/terminal_display.py +27 -68
- massgen/logger_config.py +681 -0
- massgen/mcp_tools/README.md +232 -0
- massgen/mcp_tools/__init__.py +105 -0
- massgen/mcp_tools/backend_utils.py +1035 -0
- massgen/mcp_tools/circuit_breaker.py +195 -0
- massgen/mcp_tools/client.py +894 -0
- massgen/mcp_tools/config_validator.py +138 -0
- massgen/mcp_tools/docs/circuit_breaker.md +646 -0
- massgen/mcp_tools/docs/client.md +950 -0
- massgen/mcp_tools/docs/config_validator.md +478 -0
- massgen/mcp_tools/docs/exceptions.md +1165 -0
- massgen/mcp_tools/docs/security.md +854 -0
- massgen/mcp_tools/exceptions.py +338 -0
- massgen/mcp_tools/hooks.py +212 -0
- massgen/mcp_tools/security.py +780 -0
- massgen/message_templates.py +342 -64
- massgen/orchestrator.py +1515 -241
- massgen/stream_chunk/__init__.py +35 -0
- massgen/stream_chunk/base.py +92 -0
- massgen/stream_chunk/multimodal.py +237 -0
- massgen/stream_chunk/text.py +162 -0
- massgen/tests/mcp_test_server.py +150 -0
- massgen/tests/multi_turn_conversation_design.md +0 -8
- massgen/tests/test_azure_openai_backend.py +156 -0
- massgen/tests/test_backend_capabilities.py +262 -0
- massgen/tests/test_backend_event_loop_all.py +179 -0
- massgen/tests/test_chat_completions_refactor.py +142 -0
- massgen/tests/test_claude_backend.py +15 -28
- massgen/tests/test_claude_code.py +268 -0
- massgen/tests/test_claude_code_context_sharing.py +233 -0
- massgen/tests/test_claude_code_orchestrator.py +175 -0
- massgen/tests/test_cli_backends.py +180 -0
- massgen/tests/test_code_execution.py +679 -0
- massgen/tests/test_external_agent_backend.py +134 -0
- massgen/tests/test_final_presentation_fallback.py +237 -0
- massgen/tests/test_gemini_planning_mode.py +351 -0
- massgen/tests/test_grok_backend.py +7 -10
- massgen/tests/test_http_mcp_server.py +42 -0
- massgen/tests/test_integration_simple.py +198 -0
- massgen/tests/test_mcp_blocking.py +125 -0
- massgen/tests/test_message_context_building.py +29 -47
- massgen/tests/test_orchestrator_final_presentation.py +48 -0
- massgen/tests/test_path_permission_manager.py +2087 -0
- massgen/tests/test_rich_terminal_display.py +14 -13
- massgen/tests/test_timeout.py +133 -0
- massgen/tests/test_v3_3agents.py +11 -12
- massgen/tests/test_v3_simple.py +8 -13
- massgen/tests/test_v3_three_agents.py +11 -18
- massgen/tests/test_v3_two_agents.py +8 -13
- massgen/token_manager/__init__.py +7 -0
- massgen/token_manager/token_manager.py +400 -0
- massgen/utils.py +52 -16
- massgen/v1/agent.py +45 -91
- massgen/v1/agents.py +18 -53
- massgen/v1/backends/gemini.py +50 -153
- massgen/v1/backends/grok.py +21 -54
- massgen/v1/backends/oai.py +39 -111
- massgen/v1/cli.py +36 -93
- massgen/v1/config.py +8 -12
- massgen/v1/logging.py +43 -127
- massgen/v1/main.py +18 -32
- massgen/v1/orchestrator.py +68 -209
- massgen/v1/streaming_display.py +62 -163
- massgen/v1/tools.py +8 -12
- massgen/v1/types.py +9 -23
- massgen/v1/utils.py +5 -23
- massgen-0.1.0.dist-info/METADATA +1245 -0
- massgen-0.1.0.dist-info/RECORD +273 -0
- massgen-0.1.0.dist-info/entry_points.txt +2 -0
- massgen/frontend/logging/__init__.py +0 -9
- massgen/frontend/logging/realtime_logger.py +0 -197
- massgen-0.0.3.dist-info/METADATA +0 -568
- massgen-0.0.3.dist-info/RECORD +0 -76
- massgen-0.0.3.dist-info/entry_points.txt +0 -2
- /massgen/backend/{Function calling openai responses.md → docs/Function calling openai responses.md} +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/WHEEL +0 -0
- {massgen-0.0.3.dist-info → massgen-0.1.0.dist-info}/licenses/LICENSE +0 -0
- {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())
|