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,134 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Unit tests for ExternalAgentBackend.
|
|
4
|
+
"""
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from massgen.adapters import adapter_registry
|
|
8
|
+
from massgen.adapters.base import AgentAdapter
|
|
9
|
+
from massgen.backend.external import ExternalAgentBackend
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SimpleTestAdapter(AgentAdapter):
|
|
13
|
+
"""Simple test adapter."""
|
|
14
|
+
|
|
15
|
+
async def execute_streaming(self, messages, tools, **kwargs):
|
|
16
|
+
"""Return simple response."""
|
|
17
|
+
content = "Test response"
|
|
18
|
+
async for chunk in self.simulate_streaming(content):
|
|
19
|
+
yield chunk
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture(autouse=True)
|
|
23
|
+
def register_test_adapter():
|
|
24
|
+
"""Register test adapter before tests."""
|
|
25
|
+
adapter_registry["test"] = SimpleTestAdapter
|
|
26
|
+
yield
|
|
27
|
+
# Cleanup
|
|
28
|
+
if "test" in adapter_registry:
|
|
29
|
+
del adapter_registry["test"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_initialization_with_valid_adapter():
|
|
33
|
+
"""Test backend initialization with valid adapter type."""
|
|
34
|
+
backend = ExternalAgentBackend(adapter_type="test")
|
|
35
|
+
|
|
36
|
+
assert backend.adapter_type == "test"
|
|
37
|
+
assert isinstance(backend.adapter, SimpleTestAdapter)
|
|
38
|
+
assert backend.get_provider_name() == "test"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_initialization_with_invalid_adapter():
|
|
42
|
+
"""Test backend initialization with invalid adapter type."""
|
|
43
|
+
with pytest.raises(ValueError) as exc_info:
|
|
44
|
+
ExternalAgentBackend(adapter_type="nonexistent")
|
|
45
|
+
|
|
46
|
+
assert "Unsupported framework" in str(exc_info.value)
|
|
47
|
+
assert "nonexistent" in str(exc_info.value)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_adapter_type_case_insensitive():
|
|
51
|
+
"""Test that adapter type is case-insensitive."""
|
|
52
|
+
backend1 = ExternalAgentBackend(adapter_type="TEST")
|
|
53
|
+
backend2 = ExternalAgentBackend(adapter_type="Test")
|
|
54
|
+
backend3 = ExternalAgentBackend(adapter_type="test")
|
|
55
|
+
|
|
56
|
+
assert backend1.adapter_type == "test"
|
|
57
|
+
assert backend2.adapter_type == "test"
|
|
58
|
+
assert backend3.adapter_type == "test"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.mark.asyncio
|
|
62
|
+
async def test_stream_with_tools():
|
|
63
|
+
"""Test streaming with tools."""
|
|
64
|
+
backend = ExternalAgentBackend(adapter_type="test")
|
|
65
|
+
|
|
66
|
+
messages = [{"role": "user", "content": "Hello"}]
|
|
67
|
+
tools = []
|
|
68
|
+
|
|
69
|
+
chunks = []
|
|
70
|
+
async for chunk in backend.stream_with_tools(messages, tools):
|
|
71
|
+
chunks.append(chunk)
|
|
72
|
+
|
|
73
|
+
# Should receive chunks from adapter
|
|
74
|
+
assert len(chunks) > 0
|
|
75
|
+
assert any(c.type == "content" for c in chunks)
|
|
76
|
+
assert any(c.type == "done" for c in chunks)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_extract_adapter_config():
|
|
80
|
+
"""Test extraction of adapter-specific config."""
|
|
81
|
+
backend = ExternalAgentBackend(
|
|
82
|
+
adapter_type="test",
|
|
83
|
+
# Base params (should be excluded)
|
|
84
|
+
type="test",
|
|
85
|
+
agent_id="test_agent",
|
|
86
|
+
session_id="session_1",
|
|
87
|
+
# Custom params (should be included)
|
|
88
|
+
custom_param="value",
|
|
89
|
+
temperature=0.7,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Check that adapter received config
|
|
93
|
+
assert "custom_param" in backend.adapter.config
|
|
94
|
+
assert "temperature" in backend.adapter.config
|
|
95
|
+
|
|
96
|
+
# Base params should not be in adapter config
|
|
97
|
+
assert "type" not in backend.adapter.config
|
|
98
|
+
assert "agent_id" not in backend.adapter.config
|
|
99
|
+
assert "session_id" not in backend.adapter.config
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_is_stateful_default():
|
|
103
|
+
"""Test stateful check with default adapter."""
|
|
104
|
+
backend = ExternalAgentBackend(adapter_type="test")
|
|
105
|
+
|
|
106
|
+
# Default is False
|
|
107
|
+
assert backend.is_stateful() is False
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_clear_history():
|
|
111
|
+
"""Test clearing history."""
|
|
112
|
+
backend = ExternalAgentBackend(adapter_type="test")
|
|
113
|
+
|
|
114
|
+
# Add some history
|
|
115
|
+
backend.adapter._conversation_history = [{"role": "user", "content": "test"}]
|
|
116
|
+
|
|
117
|
+
# Clear
|
|
118
|
+
backend.clear_history()
|
|
119
|
+
|
|
120
|
+
assert len(backend.adapter._conversation_history) == 0
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_reset_state():
|
|
124
|
+
"""Test resetting state."""
|
|
125
|
+
backend = ExternalAgentBackend(adapter_type="test")
|
|
126
|
+
|
|
127
|
+
# Add some history
|
|
128
|
+
backend.adapter._conversation_history = [{"role": "user", "content": "test"}]
|
|
129
|
+
|
|
130
|
+
# Reset
|
|
131
|
+
backend.reset_state()
|
|
132
|
+
|
|
133
|
+
# Should clear history
|
|
134
|
+
assert len(backend.adapter._conversation_history) == 0
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Test script for testing the final presentation fallback functionality.
|
|
5
|
+
This tests the specific changes we made to handle empty final presentations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
from unittest.mock import Mock
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
# Ensure project root is on sys.path
|
|
16
|
+
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
17
|
+
if PROJECT_ROOT not in sys.path:
|
|
18
|
+
sys.path.insert(0, PROJECT_ROOT)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.asyncio
|
|
22
|
+
async def test_final_presentation_fallback():
|
|
23
|
+
"""Test that the final presentation fallback works when content is empty."""
|
|
24
|
+
try:
|
|
25
|
+
try:
|
|
26
|
+
from massgen.orchestrator import Orchestrator
|
|
27
|
+
except ModuleNotFoundError as e:
|
|
28
|
+
# Skip if optional backend deps are missing during package import
|
|
29
|
+
if "claude_code_sdk" in str(e):
|
|
30
|
+
pytest.skip("Skipping: optional dependency 'claude_code_sdk' not installed")
|
|
31
|
+
raise
|
|
32
|
+
|
|
33
|
+
# Create a mock orchestrator with minimal setup
|
|
34
|
+
orchestrator = Orchestrator(agents={})
|
|
35
|
+
|
|
36
|
+
# Mock the agent states to simulate a stored answer
|
|
37
|
+
orchestrator.agent_states = {"test_agent": Mock(answer="This is a stored answer for testing purposes.")}
|
|
38
|
+
|
|
39
|
+
# Mock the message templates
|
|
40
|
+
orchestrator.message_templates = Mock()
|
|
41
|
+
orchestrator.message_templates.build_final_presentation_message.return_value = "Test message"
|
|
42
|
+
orchestrator.message_templates.final_presentation_system_message.return_value = "Test system message"
|
|
43
|
+
|
|
44
|
+
# Mock the current task
|
|
45
|
+
orchestrator.current_task = "Test task"
|
|
46
|
+
|
|
47
|
+
# Create a mock agent that returns no content
|
|
48
|
+
mock_agent = Mock()
|
|
49
|
+
|
|
50
|
+
# Simulate empty response from agent
|
|
51
|
+
async def empty_response(*args, **kwargs):
|
|
52
|
+
# Yield a done chunk with no content
|
|
53
|
+
yield Mock(type="done", content="")
|
|
54
|
+
|
|
55
|
+
# Set the chat method to return the async generator
|
|
56
|
+
mock_agent.chat = empty_response
|
|
57
|
+
|
|
58
|
+
# Add the mock agent to orchestrator
|
|
59
|
+
orchestrator.agents = {"test_agent": mock_agent}
|
|
60
|
+
|
|
61
|
+
# Test the get_final_presentation method
|
|
62
|
+
vote_results = {
|
|
63
|
+
"vote_counts": {"test_agent": 1},
|
|
64
|
+
"voter_details": {"test_agent": [{"voter": "other_agent", "reason": "Test reason"}]},
|
|
65
|
+
"is_tie": False,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Collect all chunks from the method
|
|
69
|
+
chunks = []
|
|
70
|
+
async for chunk in orchestrator.get_final_presentation("test_agent", vote_results):
|
|
71
|
+
chunks.append(chunk)
|
|
72
|
+
|
|
73
|
+
# Check if we got the fallback content
|
|
74
|
+
fallback_found = any(getattr(c, "type", None) == "content" and (getattr(c, "content", "") or "").find("stored answer") != -1 for c in chunks)
|
|
75
|
+
|
|
76
|
+
assert fallback_found, "Fallback content not found"
|
|
77
|
+
except Exception as e:
|
|
78
|
+
assert False, f"Error during fallback test: {e}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@pytest.mark.asyncio
|
|
82
|
+
async def test_final_presentation_with_content():
|
|
83
|
+
"""Test that the final presentation works normally when content is provided."""
|
|
84
|
+
try:
|
|
85
|
+
from massgen.orchestrator import Orchestrator
|
|
86
|
+
|
|
87
|
+
# Create a mock orchestrator with minimal setup
|
|
88
|
+
orchestrator = Orchestrator(agents={})
|
|
89
|
+
|
|
90
|
+
# Mock the agent states
|
|
91
|
+
orchestrator.agent_states = {"test_agent": Mock(answer="This is a stored answer for testing purposes.")}
|
|
92
|
+
|
|
93
|
+
# Mock the message templates
|
|
94
|
+
orchestrator.message_templates = Mock()
|
|
95
|
+
orchestrator.message_templates.build_final_presentation_message.return_value = "Test message"
|
|
96
|
+
orchestrator.message_templates.final_presentation_system_message.return_value = "Test system message"
|
|
97
|
+
|
|
98
|
+
# Mock the current task
|
|
99
|
+
orchestrator.current_task = "Test task"
|
|
100
|
+
|
|
101
|
+
# Create a mock agent that returns content
|
|
102
|
+
mock_agent = Mock()
|
|
103
|
+
|
|
104
|
+
# Simulate normal response from agent
|
|
105
|
+
async def normal_response(*args, **kwargs):
|
|
106
|
+
# Yield content chunks
|
|
107
|
+
yield Mock(type="content", content="This is the final presentation content.")
|
|
108
|
+
yield Mock(type="done", content="")
|
|
109
|
+
|
|
110
|
+
# Set the chat method to return the async generator
|
|
111
|
+
mock_agent.chat = normal_response
|
|
112
|
+
|
|
113
|
+
# Add the mock agent to orchestrator
|
|
114
|
+
orchestrator.agents = {"test_agent": mock_agent}
|
|
115
|
+
|
|
116
|
+
# Test the get_final_presentation method
|
|
117
|
+
vote_results = {
|
|
118
|
+
"vote_counts": {"test_agent": 1},
|
|
119
|
+
"voter_details": {"test_agent": [{"voter": "other_agent", "reason": "Test reason"}]},
|
|
120
|
+
"is_tie": False,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Collect all chunks from the method
|
|
124
|
+
chunks = []
|
|
125
|
+
async for chunk in orchestrator.get_final_presentation("test_agent", vote_results):
|
|
126
|
+
chunks.append(chunk)
|
|
127
|
+
|
|
128
|
+
# Check if we got the normal content (no fallback needed)
|
|
129
|
+
content_found = any(getattr(c, "type", None) == "content" and (getattr(c, "content", "") or "").find("final presentation content") != -1 for c in chunks)
|
|
130
|
+
|
|
131
|
+
assert content_found, "Normal content not found"
|
|
132
|
+
except Exception as e:
|
|
133
|
+
assert False, f"Error during normal content test: {e}"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@pytest.mark.asyncio
|
|
137
|
+
async def test_no_stored_answer_fallback():
|
|
138
|
+
"""Test that the fallback handles the case when there's no stored answer."""
|
|
139
|
+
try:
|
|
140
|
+
from massgen.orchestrator import Orchestrator
|
|
141
|
+
|
|
142
|
+
# Create a mock orchestrator with minimal setup
|
|
143
|
+
orchestrator = Orchestrator(agents={})
|
|
144
|
+
|
|
145
|
+
# Mock the agent states with no stored answer
|
|
146
|
+
orchestrator.agent_states = {"test_agent": Mock(answer="")} # No stored answer
|
|
147
|
+
|
|
148
|
+
# Mock the message templates
|
|
149
|
+
orchestrator.message_templates = Mock()
|
|
150
|
+
orchestrator.message_templates.build_final_presentation_message.return_value = "Test message"
|
|
151
|
+
orchestrator.message_templates.final_presentation_system_message.return_value = "Test system message"
|
|
152
|
+
|
|
153
|
+
# Mock the current task
|
|
154
|
+
orchestrator.current_task = "Test task"
|
|
155
|
+
|
|
156
|
+
# Create a mock agent that returns no content
|
|
157
|
+
mock_agent = Mock()
|
|
158
|
+
|
|
159
|
+
# Simulate empty response from agent
|
|
160
|
+
async def empty_response(*args, **kwargs):
|
|
161
|
+
# Yield a done chunk with no content
|
|
162
|
+
yield Mock(type="done", content="")
|
|
163
|
+
|
|
164
|
+
# Set the chat method to return the async generator
|
|
165
|
+
mock_agent.chat = empty_response
|
|
166
|
+
|
|
167
|
+
# Add the mock agent to orchestrator
|
|
168
|
+
orchestrator.agents = {"test_agent": mock_agent}
|
|
169
|
+
|
|
170
|
+
# Test the get_final_presentation method
|
|
171
|
+
vote_results = {
|
|
172
|
+
"vote_counts": {"test_agent": 1},
|
|
173
|
+
"voter_details": {"test_agent": [{"voter": "other_agent", "reason": "Test reason"}]},
|
|
174
|
+
"is_tie": False,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# Collect all chunks from the method
|
|
178
|
+
chunks = []
|
|
179
|
+
async for chunk in orchestrator.get_final_presentation("test_agent", vote_results):
|
|
180
|
+
chunks.append(chunk)
|
|
181
|
+
|
|
182
|
+
# Check if we got the no-content fallback message
|
|
183
|
+
fallback_found = any(getattr(c, "type", None) == "content" and (getattr(c, "content", "") or "").find("No content generated") != -1 for c in chunks)
|
|
184
|
+
|
|
185
|
+
assert fallback_found, "No-content fallback message not found"
|
|
186
|
+
except Exception as e:
|
|
187
|
+
assert False, f"Error during no stored answer test: {e}"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
async def main():
|
|
191
|
+
"""Main test runner."""
|
|
192
|
+
print("🚀 MassGen Final Presentation Fallback Test Suite")
|
|
193
|
+
print("Testing the specific changes we made to handle empty final presentations...")
|
|
194
|
+
print("=" * 80)
|
|
195
|
+
|
|
196
|
+
tests = [
|
|
197
|
+
("Final Presentation Fallback", test_final_presentation_fallback),
|
|
198
|
+
("Normal Final Presentation", test_final_presentation_with_content),
|
|
199
|
+
("No Stored Answer Fallback", test_no_stored_answer_fallback),
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
passed = 0
|
|
203
|
+
total = len(tests)
|
|
204
|
+
|
|
205
|
+
for test_name, test_func in tests:
|
|
206
|
+
print(f"\n{'='*20} {test_name} {'='*20}")
|
|
207
|
+
if await test_func():
|
|
208
|
+
passed += 1
|
|
209
|
+
print()
|
|
210
|
+
|
|
211
|
+
print("=" * 80)
|
|
212
|
+
print("🏁 Final Test Summary")
|
|
213
|
+
print("=" * 80)
|
|
214
|
+
|
|
215
|
+
if passed == total:
|
|
216
|
+
print("🎉 All tests passed! The final presentation fallback is working correctly.")
|
|
217
|
+
print("\n✅ What we've verified:")
|
|
218
|
+
print(" • Fallback to stored answer works when final presentation is empty")
|
|
219
|
+
print(" • Normal final presentation still works when content is provided")
|
|
220
|
+
print(" • Proper error message when no stored answer is available")
|
|
221
|
+
print("\n✅ The orchestrator changes are robust and won't break the program!")
|
|
222
|
+
return 0
|
|
223
|
+
else:
|
|
224
|
+
print(f"❌ {total - passed} tests failed. Please check the errors above.")
|
|
225
|
+
return 1
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
try:
|
|
230
|
+
exit_code = asyncio.run(main())
|
|
231
|
+
sys.exit(exit_code)
|
|
232
|
+
except KeyboardInterrupt:
|
|
233
|
+
print("\n\n⏹️ Tests interrupted by user")
|
|
234
|
+
sys.exit(1)
|
|
235
|
+
except Exception as e:
|
|
236
|
+
print(f"\n\n💥 Unexpected error during testing: {e}")
|
|
237
|
+
sys.exit(1)
|