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