massgen 0.1.0a3__py3-none-any.whl → 0.1.2__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 +1 -1
- massgen/agent_config.py +17 -0
- massgen/api_params_handler/_api_params_handler_base.py +1 -0
- massgen/api_params_handler/_chat_completions_api_params_handler.py +15 -2
- massgen/api_params_handler/_claude_api_params_handler.py +8 -1
- massgen/api_params_handler/_gemini_api_params_handler.py +73 -0
- massgen/api_params_handler/_response_api_params_handler.py +8 -1
- massgen/backend/base.py +83 -0
- massgen/backend/{base_with_mcp.py → base_with_custom_tool_and_mcp.py} +286 -15
- massgen/backend/capabilities.py +6 -6
- massgen/backend/chat_completions.py +200 -103
- massgen/backend/claude.py +115 -18
- massgen/backend/claude_code.py +378 -14
- massgen/backend/docs/CLAUDE_API_RESEARCH.md +3 -3
- massgen/backend/gemini.py +1333 -1629
- massgen/backend/gemini_mcp_manager.py +545 -0
- massgen/backend/gemini_trackers.py +344 -0
- massgen/backend/gemini_utils.py +43 -0
- massgen/backend/grok.py +39 -6
- massgen/backend/response.py +147 -81
- massgen/cli.py +605 -110
- massgen/config_builder.py +376 -27
- massgen/configs/README.md +123 -80
- massgen/configs/basic/multi/three_agents_default.yaml +3 -3
- massgen/configs/basic/single/single_agent.yaml +1 -1
- massgen/configs/providers/openai/gpt5_nano.yaml +3 -3
- massgen/configs/tools/custom_tools/claude_code_custom_tool_example.yaml +32 -0
- massgen/configs/tools/custom_tools/claude_code_custom_tool_example_no_path.yaml +28 -0
- massgen/configs/tools/custom_tools/claude_code_custom_tool_with_mcp_example.yaml +40 -0
- massgen/configs/tools/custom_tools/claude_code_custom_tool_with_wrong_mcp_example.yaml +38 -0
- massgen/configs/tools/custom_tools/claude_code_wrong_custom_tool_with_mcp_example.yaml +38 -0
- massgen/configs/tools/custom_tools/claude_custom_tool_example.yaml +24 -0
- massgen/configs/tools/custom_tools/claude_custom_tool_example_no_path.yaml +22 -0
- massgen/configs/tools/custom_tools/claude_custom_tool_with_mcp_example.yaml +35 -0
- massgen/configs/tools/custom_tools/claude_custom_tool_with_wrong_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/claude_wrong_custom_tool_with_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/gemini_custom_tool_example.yaml +24 -0
- massgen/configs/tools/custom_tools/gemini_custom_tool_example_no_path.yaml +22 -0
- massgen/configs/tools/custom_tools/gemini_custom_tool_with_mcp_example.yaml +35 -0
- massgen/configs/tools/custom_tools/gemini_custom_tool_with_wrong_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/gemini_wrong_custom_tool_with_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/github_issue_market_analysis.yaml +94 -0
- massgen/configs/tools/custom_tools/gpt5_nano_custom_tool_example.yaml +24 -0
- massgen/configs/tools/custom_tools/gpt5_nano_custom_tool_example_no_path.yaml +22 -0
- massgen/configs/tools/custom_tools/gpt5_nano_custom_tool_with_mcp_example.yaml +35 -0
- massgen/configs/tools/custom_tools/gpt5_nano_custom_tool_with_wrong_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/gpt5_nano_wrong_custom_tool_with_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/gpt_oss_custom_tool_example.yaml +25 -0
- massgen/configs/tools/custom_tools/gpt_oss_custom_tool_example_no_path.yaml +23 -0
- massgen/configs/tools/custom_tools/gpt_oss_custom_tool_with_mcp_example.yaml +34 -0
- massgen/configs/tools/custom_tools/gpt_oss_custom_tool_with_wrong_mcp_example.yaml +34 -0
- massgen/configs/tools/custom_tools/gpt_oss_wrong_custom_tool_with_mcp_example.yaml +34 -0
- massgen/configs/tools/custom_tools/grok3_mini_custom_tool_example.yaml +24 -0
- massgen/configs/tools/custom_tools/grok3_mini_custom_tool_example_no_path.yaml +22 -0
- massgen/configs/tools/custom_tools/grok3_mini_custom_tool_with_mcp_example.yaml +35 -0
- massgen/configs/tools/custom_tools/grok3_mini_custom_tool_with_wrong_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/grok3_mini_wrong_custom_tool_with_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/qwen_api_custom_tool_example.yaml +25 -0
- massgen/configs/tools/custom_tools/qwen_api_custom_tool_example_no_path.yaml +23 -0
- massgen/configs/tools/custom_tools/qwen_api_custom_tool_with_mcp_example.yaml +36 -0
- massgen/configs/tools/custom_tools/qwen_api_custom_tool_with_wrong_mcp_example.yaml +34 -0
- massgen/configs/tools/custom_tools/qwen_api_wrong_custom_tool_with_mcp_example.yaml +34 -0
- massgen/configs/tools/custom_tools/qwen_local_custom_tool_example.yaml +24 -0
- massgen/configs/tools/custom_tools/qwen_local_custom_tool_example_no_path.yaml +22 -0
- massgen/configs/tools/custom_tools/qwen_local_custom_tool_with_mcp_example.yaml +35 -0
- massgen/configs/tools/custom_tools/qwen_local_custom_tool_with_wrong_mcp_example.yaml +33 -0
- massgen/configs/tools/custom_tools/qwen_local_wrong_custom_tool_with_mcp_example.yaml +33 -0
- massgen/configs/tools/filesystem/claude_code_context_sharing.yaml +1 -1
- massgen/configs/tools/planning/five_agents_discord_mcp_planning_mode.yaml +7 -29
- massgen/configs/tools/planning/five_agents_filesystem_mcp_planning_mode.yaml +5 -6
- massgen/configs/tools/planning/five_agents_notion_mcp_planning_mode.yaml +4 -4
- massgen/configs/tools/planning/five_agents_twitter_mcp_planning_mode.yaml +4 -4
- massgen/configs/tools/planning/gpt5_mini_case_study_mcp_planning_mode.yaml +2 -2
- massgen/configs/voting/gemini_gpt_voting_sensitivity.yaml +67 -0
- massgen/formatter/_chat_completions_formatter.py +104 -0
- massgen/formatter/_claude_formatter.py +120 -0
- massgen/formatter/_gemini_formatter.py +448 -0
- massgen/formatter/_response_formatter.py +88 -0
- massgen/frontend/coordination_ui.py +4 -2
- massgen/logger_config.py +35 -3
- massgen/message_templates.py +56 -6
- massgen/orchestrator.py +512 -16
- massgen/stream_chunk/base.py +3 -0
- massgen/tests/custom_tools_example.py +392 -0
- massgen/tests/mcp_test_server.py +17 -7
- massgen/tests/test_config_builder.py +423 -0
- massgen/tests/test_custom_tools.py +401 -0
- massgen/tests/test_intelligent_planning_mode.py +643 -0
- massgen/tests/test_tools.py +127 -0
- massgen/token_manager/token_manager.py +13 -4
- massgen/tool/README.md +935 -0
- massgen/tool/__init__.py +39 -0
- massgen/tool/_async_helpers.py +70 -0
- massgen/tool/_basic/__init__.py +8 -0
- massgen/tool/_basic/_two_num_tool.py +24 -0
- massgen/tool/_code_executors/__init__.py +10 -0
- massgen/tool/_code_executors/_python_executor.py +74 -0
- massgen/tool/_code_executors/_shell_executor.py +61 -0
- massgen/tool/_exceptions.py +39 -0
- massgen/tool/_file_handlers/__init__.py +10 -0
- massgen/tool/_file_handlers/_file_operations.py +218 -0
- massgen/tool/_manager.py +634 -0
- massgen/tool/_registered_tool.py +88 -0
- massgen/tool/_result.py +66 -0
- massgen/tool/_self_evolution/_github_issue_analyzer.py +369 -0
- massgen/tool/docs/builtin_tools.md +681 -0
- massgen/tool/docs/exceptions.md +794 -0
- massgen/tool/docs/execution_results.md +691 -0
- massgen/tool/docs/manager.md +887 -0
- massgen/tool/docs/workflow_toolkits.md +529 -0
- massgen/tool/workflow_toolkits/__init__.py +57 -0
- massgen/tool/workflow_toolkits/base.py +55 -0
- massgen/tool/workflow_toolkits/new_answer.py +126 -0
- massgen/tool/workflow_toolkits/vote.py +167 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.2.dist-info}/METADATA +87 -129
- {massgen-0.1.0a3.dist-info → massgen-0.1.2.dist-info}/RECORD +120 -44
- {massgen-0.1.0a3.dist-info → massgen-0.1.2.dist-info}/WHEEL +0 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.2.dist-info}/entry_points.txt +0 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.2.dist-info}/top_level.txt +0 -0
massgen/__init__.py
CHANGED
massgen/agent_config.py
CHANGED
|
@@ -58,6 +58,9 @@ class AgentConfig:
|
|
|
58
58
|
timeout_config: Timeout and resource limit configuration
|
|
59
59
|
coordination_config: Coordination behavior configuration (e.g., planning mode)
|
|
60
60
|
skip_coordination_rounds: Debug/test mode - skip voting rounds and go straight to final presentation (default: False)
|
|
61
|
+
voting_sensitivity: Controls how critical agents are when voting ("lenient", "balanced", "strict")
|
|
62
|
+
max_new_answers_per_agent: Maximum number of new answers each agent can provide (None = unlimited)
|
|
63
|
+
answer_novelty_requirement: How different new answers must be from existing ones ("lenient", "balanced", "strict")
|
|
61
64
|
"""
|
|
62
65
|
|
|
63
66
|
# Core backend configuration (includes tool enablement)
|
|
@@ -66,6 +69,11 @@ class AgentConfig:
|
|
|
66
69
|
# Framework configuration
|
|
67
70
|
message_templates: Optional["MessageTemplates"] = None
|
|
68
71
|
|
|
72
|
+
# Voting behavior configuration
|
|
73
|
+
voting_sensitivity: str = "lenient"
|
|
74
|
+
max_new_answers_per_agent: Optional[int] = None
|
|
75
|
+
answer_novelty_requirement: str = "lenient"
|
|
76
|
+
|
|
69
77
|
# Agent customization
|
|
70
78
|
agent_id: Optional[str] = None
|
|
71
79
|
_custom_system_instruction: Optional[str] = field(default=None, init=False)
|
|
@@ -696,6 +704,9 @@ class AgentConfig:
|
|
|
696
704
|
"backend_params": self.backend_params,
|
|
697
705
|
"agent_id": self.agent_id,
|
|
698
706
|
"custom_system_instruction": self.custom_system_instruction,
|
|
707
|
+
"voting_sensitivity": self.voting_sensitivity,
|
|
708
|
+
"max_new_answers_per_agent": self.max_new_answers_per_agent,
|
|
709
|
+
"answer_novelty_requirement": self.answer_novelty_requirement,
|
|
699
710
|
"timeout_config": {
|
|
700
711
|
"orchestrator_timeout_seconds": self.timeout_config.orchestrator_timeout_seconds,
|
|
701
712
|
},
|
|
@@ -730,6 +741,9 @@ class AgentConfig:
|
|
|
730
741
|
backend_params = data.get("backend_params", {})
|
|
731
742
|
agent_id = data.get("agent_id")
|
|
732
743
|
custom_system_instruction = data.get("custom_system_instruction")
|
|
744
|
+
voting_sensitivity = data.get("voting_sensitivity", "lenient")
|
|
745
|
+
max_new_answers_per_agent = data.get("max_new_answers_per_agent")
|
|
746
|
+
answer_novelty_requirement = data.get("answer_novelty_requirement", "lenient")
|
|
733
747
|
|
|
734
748
|
# Handle timeout_config
|
|
735
749
|
timeout_config = TimeoutConfig()
|
|
@@ -756,6 +770,9 @@ class AgentConfig:
|
|
|
756
770
|
message_templates=message_templates,
|
|
757
771
|
agent_id=agent_id,
|
|
758
772
|
custom_system_instruction=custom_system_instruction,
|
|
773
|
+
voting_sensitivity=voting_sensitivity,
|
|
774
|
+
max_new_answers_per_agent=max_new_answers_per_agent,
|
|
775
|
+
answer_novelty_requirement=answer_novelty_requirement,
|
|
759
776
|
timeout_config=timeout_config,
|
|
760
777
|
coordination_config=coordination_config,
|
|
761
778
|
)
|
|
@@ -23,6 +23,7 @@ class ChatCompletionsAPIParamsHandler(APIParamsHandlerBase):
|
|
|
23
23
|
"enable_code_interpreter",
|
|
24
24
|
"allowed_tools",
|
|
25
25
|
"exclude_tools",
|
|
26
|
+
"custom_tools", # Custom tools configuration (processed separately)
|
|
26
27
|
},
|
|
27
28
|
)
|
|
28
29
|
|
|
@@ -30,7 +31,13 @@ class ChatCompletionsAPIParamsHandler(APIParamsHandlerBase):
|
|
|
30
31
|
"""Get provider tools for Chat Completions format."""
|
|
31
32
|
provider_tools = []
|
|
32
33
|
|
|
33
|
-
if
|
|
34
|
+
# Check if this is Grok backend - Grok uses extra_body.search_parameters instead of function tools
|
|
35
|
+
backend_provider = getattr(self.backend, "get_provider_name", lambda: "")()
|
|
36
|
+
is_grok = backend_provider.lower() == "grok"
|
|
37
|
+
|
|
38
|
+
# Add web_search function tool for non-Grok backends
|
|
39
|
+
# Grok handles web search via extra_body.search_parameters (set in grok.py)
|
|
40
|
+
if all_params.get("enable_web_search", False) and not is_grok:
|
|
34
41
|
provider_tools.append(
|
|
35
42
|
{
|
|
36
43
|
"type": "function",
|
|
@@ -114,11 +121,17 @@ class ChatCompletionsAPIParamsHandler(APIParamsHandlerBase):
|
|
|
114
121
|
if provider_tools:
|
|
115
122
|
combined_tools.extend(provider_tools)
|
|
116
123
|
|
|
117
|
-
#
|
|
124
|
+
# Workflow tools
|
|
118
125
|
if tools:
|
|
119
126
|
converted_tools = self.formatter.format_tools(tools)
|
|
120
127
|
combined_tools.extend(converted_tools)
|
|
121
128
|
|
|
129
|
+
# Add custom tools
|
|
130
|
+
custom_tools = self.custom_tool_manager.registered_tools
|
|
131
|
+
if custom_tools:
|
|
132
|
+
converted_custom_tools = self.formatter.format_custom_tools(custom_tools)
|
|
133
|
+
combined_tools.extend(converted_custom_tools)
|
|
134
|
+
|
|
122
135
|
# MCP tools
|
|
123
136
|
mcp_tools = self.get_mcp_tools()
|
|
124
137
|
if mcp_tools:
|
|
@@ -22,6 +22,7 @@ class ClaudeAPIParamsHandler(APIParamsHandlerBase):
|
|
|
22
22
|
"enable_code_execution",
|
|
23
23
|
"allowed_tools",
|
|
24
24
|
"exclude_tools",
|
|
25
|
+
"custom_tools", # Custom tools configuration (processed separately)
|
|
25
26
|
"_has_files_api_files",
|
|
26
27
|
},
|
|
27
28
|
)
|
|
@@ -97,11 +98,17 @@ class ClaudeAPIParamsHandler(APIParamsHandlerBase):
|
|
|
97
98
|
if provider_tools:
|
|
98
99
|
combined_tools.extend(provider_tools)
|
|
99
100
|
|
|
100
|
-
#
|
|
101
|
+
# Workflow tools
|
|
101
102
|
if tools:
|
|
102
103
|
converted_tools = self.formatter.format_tools(tools)
|
|
103
104
|
combined_tools.extend(converted_tools)
|
|
104
105
|
|
|
106
|
+
# Add custom tools
|
|
107
|
+
custom_tools = self.custom_tool_manager.registered_tools
|
|
108
|
+
if custom_tools:
|
|
109
|
+
converted_custom_tools = self.formatter.format_custom_tools(custom_tools)
|
|
110
|
+
combined_tools.extend(converted_custom_tools)
|
|
111
|
+
|
|
105
112
|
# MCP tools
|
|
106
113
|
mcp_tools = self.get_mcp_tools()
|
|
107
114
|
if mcp_tools:
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Gemini API parameters handler building SDK config with parameter mapping and exclusions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, Dict, List, Set
|
|
7
|
+
|
|
8
|
+
from ._api_params_handler_base import APIParamsHandlerBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GeminiAPIParamsHandler(APIParamsHandlerBase):
|
|
12
|
+
def get_excluded_params(self) -> Set[str]:
|
|
13
|
+
base = self.get_base_excluded_params()
|
|
14
|
+
extra = {
|
|
15
|
+
"enable_web_search",
|
|
16
|
+
"enable_code_execution",
|
|
17
|
+
"use_multi_mcp",
|
|
18
|
+
"mcp_sdk_auto",
|
|
19
|
+
"allowed_tools",
|
|
20
|
+
"exclude_tools",
|
|
21
|
+
"custom_tools",
|
|
22
|
+
}
|
|
23
|
+
return set(base) | extra
|
|
24
|
+
|
|
25
|
+
def get_provider_tools(self, all_params: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
26
|
+
"""
|
|
27
|
+
These are SDK Tool objects (from google.genai.types), not JSON tool declarations.
|
|
28
|
+
"""
|
|
29
|
+
tools: List[Any] = []
|
|
30
|
+
|
|
31
|
+
if all_params.get("enable_web_search", False):
|
|
32
|
+
try:
|
|
33
|
+
from google.genai.types import GoogleSearch, Tool
|
|
34
|
+
|
|
35
|
+
tools.append(Tool(google_search=GoogleSearch()))
|
|
36
|
+
except Exception:
|
|
37
|
+
# Gracefully ignore if SDK not available
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
if all_params.get("enable_code_execution", False):
|
|
41
|
+
try:
|
|
42
|
+
from google.genai.types import Tool, ToolCodeExecution
|
|
43
|
+
|
|
44
|
+
tools.append(Tool(code_execution=ToolCodeExecution()))
|
|
45
|
+
except Exception:
|
|
46
|
+
# Gracefully ignore if SDK not available
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
return tools
|
|
50
|
+
|
|
51
|
+
async def build_api_params(self, messages: List[Dict[str, Any]], tools: List[Dict[str, Any]], all_params: Dict[str, Any]) -> Dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Build a config dict for google-genai Client.generate_content_stream.
|
|
54
|
+
- Map max_tokens -> max_output_tokens
|
|
55
|
+
- Do not include 'model' here; caller extracts it
|
|
56
|
+
- Do not add builtin tools; stream logic handles them
|
|
57
|
+
- Do not handle MCP tools or coordination schema here
|
|
58
|
+
"""
|
|
59
|
+
excluded = self.get_excluded_params()
|
|
60
|
+
config: Dict[str, Any] = {}
|
|
61
|
+
|
|
62
|
+
for key, value in all_params.items():
|
|
63
|
+
if key in excluded or value is None:
|
|
64
|
+
continue
|
|
65
|
+
if key == "max_tokens":
|
|
66
|
+
config["max_output_tokens"] = value
|
|
67
|
+
elif key == "model":
|
|
68
|
+
# Caller will extract model separately
|
|
69
|
+
continue
|
|
70
|
+
else:
|
|
71
|
+
config[key] = value
|
|
72
|
+
|
|
73
|
+
return config
|
|
@@ -22,6 +22,7 @@ class ResponseAPIParamsHandler(APIParamsHandlerBase):
|
|
|
22
22
|
"enable_code_interpreter",
|
|
23
23
|
"allowed_tools",
|
|
24
24
|
"exclude_tools",
|
|
25
|
+
"custom_tools", # Custom tools configuration (processed separately)
|
|
25
26
|
"_has_file_search_files", # Internal flag for file search tracking
|
|
26
27
|
},
|
|
27
28
|
)
|
|
@@ -88,11 +89,17 @@ class ResponseAPIParamsHandler(APIParamsHandlerBase):
|
|
|
88
89
|
if provider_tools:
|
|
89
90
|
combined_tools.extend(provider_tools)
|
|
90
91
|
|
|
91
|
-
# Add
|
|
92
|
+
# Add workflow tools
|
|
92
93
|
if tools:
|
|
93
94
|
converted_tools = self.formatter.format_tools(tools)
|
|
94
95
|
combined_tools.extend(converted_tools)
|
|
95
96
|
|
|
97
|
+
# Add custom tools
|
|
98
|
+
custom_tools = self.custom_tool_manager.registered_tools
|
|
99
|
+
if custom_tools:
|
|
100
|
+
converted_custom_tools = self.formatter.format_custom_tools(custom_tools)
|
|
101
|
+
combined_tools.extend(converted_custom_tools)
|
|
102
|
+
|
|
96
103
|
# Add MCP tools (use OpenAI format)
|
|
97
104
|
mcp_tools = self._convert_mcp_tools_to_openai_format()
|
|
98
105
|
if mcp_tools:
|
massgen/backend/base.py
CHANGED
|
@@ -59,9 +59,22 @@ class LLMBackend(ABC):
|
|
|
59
59
|
# Initialize utility classes
|
|
60
60
|
self.token_usage = TokenUsage()
|
|
61
61
|
|
|
62
|
+
# # Initialize tool manager
|
|
63
|
+
# self.custom_tool_manager = ToolManager()
|
|
64
|
+
|
|
65
|
+
# # Register custom tools if specified
|
|
66
|
+
# custom_tools = kwargs.get("custom_tools", [])
|
|
67
|
+
# if custom_tools:
|
|
68
|
+
# self._register_custom_tools(custom_tools)
|
|
69
|
+
|
|
62
70
|
# Planning mode flag - when True, MCP tools should be blocked during coordination
|
|
63
71
|
self._planning_mode_enabled: bool = False
|
|
64
72
|
|
|
73
|
+
# Selective tool blocking - list of specific MCP tools to block during planning mode
|
|
74
|
+
# When planning_mode is enabled, only these specific tools are blocked
|
|
75
|
+
# If empty, ALL MCP tools are blocked (backward compatible behavior)
|
|
76
|
+
self._planning_mode_blocked_tools: set = set()
|
|
77
|
+
|
|
65
78
|
self.token_calculator = TokenCostCalculator()
|
|
66
79
|
|
|
67
80
|
# Filesystem manager integration
|
|
@@ -125,6 +138,29 @@ class LLMBackend(ABC):
|
|
|
125
138
|
self.api_params_handler = None
|
|
126
139
|
self.coordination_stage = None
|
|
127
140
|
|
|
141
|
+
# def _register_custom_tools(self, tool_names: list[str]) -> None:
|
|
142
|
+
# """Register custom tool functions.
|
|
143
|
+
|
|
144
|
+
# Args:
|
|
145
|
+
# tool_names: List of tool names to register
|
|
146
|
+
# """
|
|
147
|
+
# import importlib
|
|
148
|
+
|
|
149
|
+
# for tool_name in tool_names:
|
|
150
|
+
# try:
|
|
151
|
+
# # Try to import from tool module
|
|
152
|
+
# module = importlib.import_module("massgen.tool")
|
|
153
|
+
# if hasattr(module, tool_name):
|
|
154
|
+
# tool_func = getattr(module, tool_name)
|
|
155
|
+
# self.custom_tool_manager.add_tool_function(tool_func)
|
|
156
|
+
# print(f"Successfully registered custom tool: {tool_name}")
|
|
157
|
+
# else:
|
|
158
|
+
# print(f"Warning: Tool '{tool_name}' not found in massgen.tool")
|
|
159
|
+
# except ImportError as e:
|
|
160
|
+
# print(f"Warning: Could not import tool module: {e}")
|
|
161
|
+
# except Exception as e:
|
|
162
|
+
# print(f"Error registering tool '{tool_name}': {e}")
|
|
163
|
+
|
|
128
164
|
def _setup_permission_hooks(self):
|
|
129
165
|
"""Setup permission hooks for function-based backends (default behavior)."""
|
|
130
166
|
# Create per-agent hook manager
|
|
@@ -434,6 +470,53 @@ class LLMBackend(ABC):
|
|
|
434
470
|
"""
|
|
435
471
|
return self._planning_mode_enabled
|
|
436
472
|
|
|
473
|
+
def set_planning_mode_blocked_tools(self, tool_names: set) -> None:
|
|
474
|
+
"""
|
|
475
|
+
Set specific MCP tools to block during planning mode.
|
|
476
|
+
|
|
477
|
+
This enables selective tool blocking - only the specified tools will be blocked
|
|
478
|
+
when planning mode is enabled, allowing other MCP tools to be used.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
tool_names: Set of MCP tool names to block (e.g., {'mcp__discord__discord_send'})
|
|
482
|
+
If empty set, ALL MCP tools are blocked (backward compatible)
|
|
483
|
+
"""
|
|
484
|
+
self._planning_mode_blocked_tools = set(tool_names)
|
|
485
|
+
|
|
486
|
+
def get_planning_mode_blocked_tools(self) -> set:
|
|
487
|
+
"""
|
|
488
|
+
Get the set of MCP tools currently blocked in planning mode.
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
Set of blocked MCP tool names. Empty set means ALL MCP tools are blocked.
|
|
492
|
+
"""
|
|
493
|
+
return self._planning_mode_blocked_tools.copy()
|
|
494
|
+
|
|
495
|
+
def is_mcp_tool_blocked(self, tool_name: str) -> bool:
|
|
496
|
+
"""
|
|
497
|
+
Check if a specific MCP tool is blocked in planning mode.
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
tool_name: Name of the MCP tool to check (e.g., 'mcp__discord__discord_send')
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
True if the tool should be blocked, False otherwise
|
|
504
|
+
|
|
505
|
+
Note:
|
|
506
|
+
- If planning mode is disabled, returns False (no blocking)
|
|
507
|
+
- If planning mode is enabled and blocked_tools is empty, returns True (block ALL)
|
|
508
|
+
- If planning mode is enabled and blocked_tools is set, returns True only if tool is in the set
|
|
509
|
+
"""
|
|
510
|
+
if not self._planning_mode_enabled:
|
|
511
|
+
return False
|
|
512
|
+
|
|
513
|
+
# Empty set means block ALL MCP tools (backward compatible behavior)
|
|
514
|
+
if not self._planning_mode_blocked_tools:
|
|
515
|
+
return True
|
|
516
|
+
|
|
517
|
+
# Otherwise, block only if tool is in the blocked set
|
|
518
|
+
return tool_name in self._planning_mode_blocked_tools
|
|
519
|
+
|
|
437
520
|
async def _cleanup_client(self, client: Any) -> None:
|
|
438
521
|
"""Clean up OpenAI client resources."""
|
|
439
522
|
try:
|