massgen 0.1.2__py3-none-any.whl → 0.1.4__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 +33 -7
- massgen/api_params_handler/_api_params_handler_base.py +3 -0
- massgen/api_params_handler/_chat_completions_api_params_handler.py +4 -0
- massgen/api_params_handler/_claude_api_params_handler.py +4 -0
- massgen/api_params_handler/_gemini_api_params_handler.py +4 -0
- massgen/api_params_handler/_response_api_params_handler.py +4 -0
- massgen/backend/azure_openai.py +9 -1
- massgen/backend/base.py +4 -0
- massgen/backend/base_with_custom_tool_and_mcp.py +25 -5
- massgen/backend/claude_code.py +9 -1
- massgen/backend/docs/permissions_and_context_files.md +2 -2
- massgen/backend/gemini.py +35 -6
- massgen/backend/gemini_utils.py +30 -0
- massgen/backend/response.py +2 -0
- massgen/chat_agent.py +9 -3
- massgen/cli.py +291 -43
- massgen/config_builder.py +163 -18
- massgen/configs/README.md +69 -14
- massgen/configs/debug/restart_test_controlled.yaml +60 -0
- massgen/configs/debug/restart_test_controlled_filesystem.yaml +73 -0
- massgen/configs/tools/code-execution/docker_with_sudo.yaml +35 -0
- massgen/configs/tools/custom_tools/computer_use_browser_example.yaml +56 -0
- massgen/configs/tools/custom_tools/computer_use_docker_example.yaml +65 -0
- massgen/configs/tools/custom_tools/computer_use_example.yaml +50 -0
- massgen/configs/tools/custom_tools/crawl4ai_example.yaml +55 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_file_generation_multi.yaml +61 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_file_generation_single.yaml +29 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_image_generation_multi.yaml +51 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_image_generation_single.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_speech_generation_multi.yaml +55 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_speech_generation_single.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_video_generation_multi.yaml +47 -0
- massgen/configs/tools/custom_tools/multimodal_tools/text_to_video_generation_single.yaml +29 -0
- massgen/configs/tools/custom_tools/multimodal_tools/understand_audio.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/understand_file.yaml +34 -0
- massgen/configs/tools/custom_tools/multimodal_tools/understand_image.yaml +33 -0
- massgen/configs/tools/custom_tools/multimodal_tools/understand_video.yaml +34 -0
- massgen/configs/tools/custom_tools/multimodal_tools/youtube_video_analysis.yaml +59 -0
- massgen/docker/README.md +83 -0
- massgen/filesystem_manager/_code_execution_server.py +22 -7
- massgen/filesystem_manager/_docker_manager.py +21 -1
- massgen/filesystem_manager/_filesystem_manager.py +9 -0
- massgen/filesystem_manager/_path_permission_manager.py +148 -0
- massgen/filesystem_manager/_workspace_tools_server.py +0 -997
- massgen/formatter/_gemini_formatter.py +73 -0
- massgen/frontend/coordination_ui.py +175 -257
- massgen/frontend/displays/base_display.py +29 -0
- massgen/frontend/displays/rich_terminal_display.py +155 -9
- massgen/frontend/displays/simple_display.py +21 -0
- massgen/frontend/displays/terminal_display.py +22 -2
- massgen/logger_config.py +50 -6
- massgen/message_templates.py +283 -15
- massgen/orchestrator.py +335 -38
- massgen/tests/test_binary_file_blocking.py +274 -0
- massgen/tests/test_case_studies.md +12 -12
- massgen/tests/test_code_execution.py +178 -0
- massgen/tests/test_multimodal_size_limits.py +407 -0
- massgen/tests/test_orchestration_restart.py +204 -0
- massgen/tool/__init__.py +4 -0
- massgen/tool/_manager.py +7 -2
- massgen/tool/_multimodal_tools/image_to_image_generation.py +293 -0
- massgen/tool/_multimodal_tools/text_to_file_generation.py +455 -0
- massgen/tool/_multimodal_tools/text_to_image_generation.py +222 -0
- massgen/tool/_multimodal_tools/text_to_speech_continue_generation.py +226 -0
- massgen/tool/_multimodal_tools/text_to_speech_transcription_generation.py +217 -0
- massgen/tool/_multimodal_tools/text_to_video_generation.py +223 -0
- massgen/tool/_multimodal_tools/understand_audio.py +211 -0
- massgen/tool/_multimodal_tools/understand_file.py +555 -0
- massgen/tool/_multimodal_tools/understand_image.py +316 -0
- massgen/tool/_multimodal_tools/understand_video.py +340 -0
- massgen/tool/_web_tools/crawl4ai_tool.py +718 -0
- massgen/tool/docs/multimodal_tools.md +1368 -0
- massgen/tool/workflow_toolkits/__init__.py +26 -0
- massgen/tool/workflow_toolkits/post_evaluation.py +216 -0
- massgen/utils.py +1 -0
- {massgen-0.1.2.dist-info → massgen-0.1.4.dist-info}/METADATA +101 -69
- {massgen-0.1.2.dist-info → massgen-0.1.4.dist-info}/RECORD +82 -46
- {massgen-0.1.2.dist-info → massgen-0.1.4.dist-info}/WHEEL +0 -0
- {massgen-0.1.2.dist-info → massgen-0.1.4.dist-info}/entry_points.txt +0 -0
- {massgen-0.1.2.dist-info → massgen-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.1.2.dist-info → massgen-0.1.4.dist-info}/top_level.txt +0 -0
massgen/__init__.py
CHANGED
massgen/agent_config.py
CHANGED
|
@@ -35,12 +35,17 @@ class CoordinationConfig:
|
|
|
35
35
|
Only the winning agent executes actions during final presentation.
|
|
36
36
|
If False, agents execute actions during coordination (default behavior).
|
|
37
37
|
planning_mode_instruction: Custom instruction to add when planning mode is enabled.
|
|
38
|
+
max_orchestration_restarts: Maximum number of times orchestration can be restarted after
|
|
39
|
+
post-evaluation determines the answer is insufficient.
|
|
40
|
+
For example, max_orchestration_restarts=2 allows 3 total attempts
|
|
41
|
+
(initial + 2 restarts). Default is 0 (no restarts).
|
|
38
42
|
"""
|
|
39
43
|
|
|
40
44
|
enable_planning_mode: bool = False
|
|
41
45
|
planning_mode_instruction: str = (
|
|
42
46
|
"During coordination, describe what you would do without actually executing actions. Only provide concrete implementation details without calling external APIs or tools."
|
|
43
47
|
)
|
|
48
|
+
max_orchestration_restarts: int = 0
|
|
44
49
|
|
|
45
50
|
|
|
46
51
|
@dataclass
|
|
@@ -87,6 +92,9 @@ class AgentConfig:
|
|
|
87
92
|
# Debug/test mode - skip coordination rounds and go straight to final presentation
|
|
88
93
|
skip_coordination_rounds: bool = False
|
|
89
94
|
|
|
95
|
+
# Debug mode for restart feature - override final answer on attempt 1 only
|
|
96
|
+
debug_final_answer: Optional[str] = None
|
|
97
|
+
|
|
90
98
|
@property
|
|
91
99
|
def custom_system_instruction(self) -> Optional[str]:
|
|
92
100
|
"""
|
|
@@ -432,7 +440,8 @@ class AgentConfig:
|
|
|
432
440
|
import copy
|
|
433
441
|
|
|
434
442
|
new_config = copy.deepcopy(self)
|
|
435
|
-
|
|
443
|
+
# Set private attribute directly to avoid deprecation warning
|
|
444
|
+
new_config._custom_system_instruction = instruction
|
|
436
445
|
return new_config
|
|
437
446
|
|
|
438
447
|
def with_agent_id(self, agent_id: str) -> "AgentConfig":
|
|
@@ -538,7 +547,8 @@ class AgentConfig:
|
|
|
538
547
|
else:
|
|
539
548
|
raise ValueError(f"Domain expert configuration not available for backend: {backend}")
|
|
540
549
|
|
|
541
|
-
|
|
550
|
+
# Set private attribute directly to avoid deprecation warning
|
|
551
|
+
config._custom_system_instruction = instruction
|
|
542
552
|
return config
|
|
543
553
|
|
|
544
554
|
# =============================================================================
|
|
@@ -567,9 +577,10 @@ class AgentConfig:
|
|
|
567
577
|
conversation = templates.build_initial_conversation(task=task, agent_summaries=agent_summaries, valid_agent_ids=valid_agent_ids)
|
|
568
578
|
|
|
569
579
|
# Add custom system instruction if provided
|
|
570
|
-
|
|
580
|
+
# Access private attribute to avoid deprecation warning
|
|
581
|
+
if self._custom_system_instruction:
|
|
571
582
|
base_system = conversation["system_message"]
|
|
572
|
-
conversation["system_message"] = f"{self.
|
|
583
|
+
conversation["system_message"] = f"{self._custom_system_instruction}\n\n{base_system}"
|
|
573
584
|
|
|
574
585
|
# Add backend configuration
|
|
575
586
|
conversation.update(
|
|
@@ -703,7 +714,8 @@ class AgentConfig:
|
|
|
703
714
|
result = {
|
|
704
715
|
"backend_params": self.backend_params,
|
|
705
716
|
"agent_id": self.agent_id,
|
|
706
|
-
|
|
717
|
+
# Access private attribute to avoid deprecation warning
|
|
718
|
+
"custom_system_instruction": self._custom_system_instruction,
|
|
707
719
|
"voting_sensitivity": self.voting_sensitivity,
|
|
708
720
|
"max_new_answers_per_agent": self.max_new_answers_per_agent,
|
|
709
721
|
"answer_novelty_requirement": self.answer_novelty_requirement,
|
|
@@ -716,8 +728,12 @@ class AgentConfig:
|
|
|
716
728
|
result["coordination_config"] = {
|
|
717
729
|
"enable_planning_mode": self.coordination_config.enable_planning_mode,
|
|
718
730
|
"planning_mode_instruction": self.coordination_config.planning_mode_instruction,
|
|
731
|
+
"max_orchestration_restarts": self.coordination_config.max_orchestration_restarts,
|
|
719
732
|
}
|
|
720
733
|
|
|
734
|
+
# Handle debug fields
|
|
735
|
+
result["debug_final_answer"] = self.debug_final_answer
|
|
736
|
+
|
|
721
737
|
# Handle message_templates serialization
|
|
722
738
|
if self.message_templates is not None:
|
|
723
739
|
try:
|
|
@@ -757,6 +773,9 @@ class AgentConfig:
|
|
|
757
773
|
if coordination_data:
|
|
758
774
|
coordination_config = CoordinationConfig(**coordination_data)
|
|
759
775
|
|
|
776
|
+
# Handle debug fields
|
|
777
|
+
debug_final_answer = data.get("debug_final_answer")
|
|
778
|
+
|
|
760
779
|
# Handle message_templates
|
|
761
780
|
message_templates = None
|
|
762
781
|
template_data = data.get("message_templates")
|
|
@@ -765,17 +784,24 @@ class AgentConfig:
|
|
|
765
784
|
|
|
766
785
|
message_templates = MessageTemplates(**template_data)
|
|
767
786
|
|
|
768
|
-
|
|
787
|
+
config = cls(
|
|
769
788
|
backend_params=backend_params,
|
|
770
789
|
message_templates=message_templates,
|
|
771
790
|
agent_id=agent_id,
|
|
772
|
-
custom_system_instruction=custom_system_instruction,
|
|
773
791
|
voting_sensitivity=voting_sensitivity,
|
|
774
792
|
max_new_answers_per_agent=max_new_answers_per_agent,
|
|
775
793
|
answer_novelty_requirement=answer_novelty_requirement,
|
|
776
794
|
timeout_config=timeout_config,
|
|
777
795
|
coordination_config=coordination_config,
|
|
778
796
|
)
|
|
797
|
+
config.debug_final_answer = debug_final_answer
|
|
798
|
+
return config
|
|
799
|
+
|
|
800
|
+
# Set custom_system_instruction separately to avoid deprecation warning
|
|
801
|
+
if custom_system_instruction is not None:
|
|
802
|
+
config._custom_system_instruction = custom_system_instruction
|
|
803
|
+
|
|
804
|
+
return config
|
|
779
805
|
|
|
780
806
|
|
|
781
807
|
# =============================================================================
|
|
@@ -56,8 +56,10 @@ class APIParamsHandlerBase(ABC):
|
|
|
56
56
|
# Filesystem manager parameters (handled by base class)
|
|
57
57
|
"cwd",
|
|
58
58
|
"agent_temporary_workspace",
|
|
59
|
+
"agent_temporary_workspace_parent",
|
|
59
60
|
"context_paths",
|
|
60
61
|
"context_write_access_enabled",
|
|
62
|
+
"enforce_read_before_delete",
|
|
61
63
|
"enable_image_generation",
|
|
62
64
|
"enable_mcp_command_line",
|
|
63
65
|
"command_line_allowed_commands",
|
|
@@ -67,6 +69,7 @@ class APIParamsHandlerBase(ABC):
|
|
|
67
69
|
"command_line_docker_memory_limit",
|
|
68
70
|
"command_line_docker_cpu_limit",
|
|
69
71
|
"command_line_docker_network_mode",
|
|
72
|
+
"command_line_docker_enable_sudo",
|
|
70
73
|
# Backend identification (handled by orchestrator)
|
|
71
74
|
"enable_audio_generation", # Audio generation parameter
|
|
72
75
|
"type",
|
|
@@ -24,6 +24,10 @@ class ChatCompletionsAPIParamsHandler(APIParamsHandlerBase):
|
|
|
24
24
|
"allowed_tools",
|
|
25
25
|
"exclude_tools",
|
|
26
26
|
"custom_tools", # Custom tools configuration (processed separately)
|
|
27
|
+
"enable_file_generation", # Internal flag for file generation (used in system messages only)
|
|
28
|
+
"enable_image_generation", # Internal flag for image generation (used in system messages only)
|
|
29
|
+
"enable_audio_generation", # Internal flag for audio generation (used in system messages only)
|
|
30
|
+
"enable_video_generation", # Internal flag for video generation (used in system messages only)
|
|
27
31
|
},
|
|
28
32
|
)
|
|
29
33
|
|
|
@@ -24,6 +24,10 @@ class ClaudeAPIParamsHandler(APIParamsHandlerBase):
|
|
|
24
24
|
"exclude_tools",
|
|
25
25
|
"custom_tools", # Custom tools configuration (processed separately)
|
|
26
26
|
"_has_files_api_files",
|
|
27
|
+
"enable_file_generation", # Internal flag for file generation (used in system messages only)
|
|
28
|
+
"enable_image_generation", # Internal flag for image generation (used in system messages only)
|
|
29
|
+
"enable_audio_generation", # Internal flag for audio generation (used in system messages only)
|
|
30
|
+
"enable_video_generation", # Internal flag for video generation (used in system messages only)
|
|
27
31
|
},
|
|
28
32
|
)
|
|
29
33
|
|
|
@@ -19,6 +19,10 @@ class GeminiAPIParamsHandler(APIParamsHandlerBase):
|
|
|
19
19
|
"allowed_tools",
|
|
20
20
|
"exclude_tools",
|
|
21
21
|
"custom_tools",
|
|
22
|
+
"enable_file_generation", # Internal flag for file generation (used in system messages only)
|
|
23
|
+
"enable_image_generation", # Internal flag for image generation (used in system messages only)
|
|
24
|
+
"enable_audio_generation", # Internal flag for audio generation (used in system messages only)
|
|
25
|
+
"enable_video_generation", # Internal flag for video generation (used in system messages only)
|
|
22
26
|
}
|
|
23
27
|
return set(base) | extra
|
|
24
28
|
|
|
@@ -24,6 +24,10 @@ class ResponseAPIParamsHandler(APIParamsHandlerBase):
|
|
|
24
24
|
"exclude_tools",
|
|
25
25
|
"custom_tools", # Custom tools configuration (processed separately)
|
|
26
26
|
"_has_file_search_files", # Internal flag for file search tracking
|
|
27
|
+
"enable_file_generation", # Internal flag for file generation (used in system messages only)
|
|
28
|
+
"enable_image_generation", # Internal flag for image generation (used in system messages only)
|
|
29
|
+
"enable_audio_generation", # Internal flag for audio generation (used in system messages only)
|
|
30
|
+
"enable_video_generation", # Internal flag for video generation (used in system messages only)
|
|
27
31
|
},
|
|
28
32
|
)
|
|
29
33
|
|
massgen/backend/azure_openai.py
CHANGED
|
@@ -94,7 +94,7 @@ class AzureOpenAIBackend(LLMBackend):
|
|
|
94
94
|
raise ValueError("Azure OpenAI requires a deployment name. Pass it as the 'model' parameter.")
|
|
95
95
|
|
|
96
96
|
# Check if workflow tools are present
|
|
97
|
-
workflow_tools = [t for t in tools if t.get("function", {}).get("name") in ["new_answer", "vote"]] if tools else []
|
|
97
|
+
workflow_tools = [t for t in tools if t.get("function", {}).get("name") in ["new_answer", "vote", "submit", "restart_orchestration"]] if tools else []
|
|
98
98
|
has_workflow_tools = len(workflow_tools) > 0
|
|
99
99
|
|
|
100
100
|
# Modify messages to include workflow tool instructions if needed
|
|
@@ -270,6 +270,14 @@ class AzureOpenAIBackend(LLMBackend):
|
|
|
270
270
|
system_parts.append(f' Usage: {{"tool_name": "vote", ' f'"arguments": {{"agent_id": "agent1", ' f'"reason": "explanation"}}}} // Choose agent_id from: {agent_list}')
|
|
271
271
|
else:
|
|
272
272
|
system_parts.append(' Usage: {"tool_name": "vote", ' '"arguments": {"agent_id": "agent1", ' '"reason": "explanation"}}')
|
|
273
|
+
elif name == "submit":
|
|
274
|
+
system_parts.append(
|
|
275
|
+
' Usage: {"tool_name": "submit", ' '"arguments": {"confirmed": true}}',
|
|
276
|
+
)
|
|
277
|
+
elif name == "restart_orchestration":
|
|
278
|
+
system_parts.append(
|
|
279
|
+
' Usage: {"tool_name": "restart_orchestration", ' '"arguments": {"reason": "The answer is incomplete because...", ' '"instructions": "In the next attempt, please..."}}',
|
|
280
|
+
)
|
|
273
281
|
|
|
274
282
|
system_parts.append("\n--- MassGen Workflow Instructions ---")
|
|
275
283
|
system_parts.append("IMPORTANT: You must respond with a structured JSON decision at the end of your response.")
|
massgen/backend/base.py
CHANGED
|
@@ -112,6 +112,7 @@ class LLMBackend(ABC):
|
|
|
112
112
|
"command_line_docker_memory_limit": kwargs.get("command_line_docker_memory_limit"),
|
|
113
113
|
"command_line_docker_cpu_limit": kwargs.get("command_line_docker_cpu_limit"),
|
|
114
114
|
"command_line_docker_network_mode": network_mode,
|
|
115
|
+
"command_line_docker_enable_sudo": kwargs.get("command_line_docker_enable_sudo", False),
|
|
115
116
|
"enable_audio_generation": kwargs.get("enable_audio_generation", False),
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -188,8 +189,10 @@ class LLMBackend(ABC):
|
|
|
188
189
|
# Filesystem manager parameters (handled by base class)
|
|
189
190
|
"cwd",
|
|
190
191
|
"agent_temporary_workspace",
|
|
192
|
+
"agent_temporary_workspace_parent",
|
|
191
193
|
"context_paths",
|
|
192
194
|
"context_write_access_enabled",
|
|
195
|
+
"enforce_read_before_delete",
|
|
193
196
|
"enable_image_generation",
|
|
194
197
|
"enable_mcp_command_line",
|
|
195
198
|
"command_line_allowed_commands",
|
|
@@ -199,6 +202,7 @@ class LLMBackend(ABC):
|
|
|
199
202
|
"command_line_docker_memory_limit",
|
|
200
203
|
"command_line_docker_cpu_limit",
|
|
201
204
|
"command_line_docker_network_mode",
|
|
205
|
+
"command_line_docker_enable_sudo",
|
|
202
206
|
# Backend identification (handled by orchestrator)
|
|
203
207
|
"type",
|
|
204
208
|
"agent_id",
|
|
@@ -284,9 +284,19 @@ class CustomToolAndMCPBackend(LLMBackend):
|
|
|
284
284
|
|
|
285
285
|
# Register each function with its corresponding values
|
|
286
286
|
for i, func in enumerate(functions):
|
|
287
|
+
# Inject agent_cwd into preset_args if filesystem_manager is available
|
|
288
|
+
final_preset_args = preset_args_list[i].copy() if preset_args_list[i] else {}
|
|
289
|
+
if self.filesystem_manager and self.filesystem_manager.cwd:
|
|
290
|
+
final_preset_args["agent_cwd"] = self.filesystem_manager.cwd
|
|
291
|
+
logger.info(f"Injecting agent_cwd for {func}: {self.filesystem_manager.cwd}")
|
|
292
|
+
elif self.filesystem_manager:
|
|
293
|
+
logger.warning(f"filesystem_manager exists but cwd is None for {func}")
|
|
294
|
+
else:
|
|
295
|
+
logger.warning(f"No filesystem_manager available for {func}")
|
|
296
|
+
|
|
287
297
|
# Load the function first if custom name is needed
|
|
288
298
|
if names[i] and names[i] != func:
|
|
289
|
-
#
|
|
299
|
+
# Load function to apply custom name
|
|
290
300
|
if path:
|
|
291
301
|
loaded_func = self.custom_tool_manager._load_function_from_path(path, func)
|
|
292
302
|
else:
|
|
@@ -296,7 +306,6 @@ class CustomToolAndMCPBackend(LLMBackend):
|
|
|
296
306
|
logger.error(f"Could not load function '{func}' from path: {path}")
|
|
297
307
|
continue
|
|
298
308
|
|
|
299
|
-
# Apply custom name by modifying __name__ attribute
|
|
300
309
|
loaded_func.__name__ = names[i]
|
|
301
310
|
|
|
302
311
|
# Register with loaded function (no path needed)
|
|
@@ -304,7 +313,7 @@ class CustomToolAndMCPBackend(LLMBackend):
|
|
|
304
313
|
path=None,
|
|
305
314
|
func=loaded_func,
|
|
306
315
|
category=category,
|
|
307
|
-
preset_args=
|
|
316
|
+
preset_args=final_preset_args,
|
|
308
317
|
description=descriptions[i],
|
|
309
318
|
)
|
|
310
319
|
else:
|
|
@@ -313,7 +322,7 @@ class CustomToolAndMCPBackend(LLMBackend):
|
|
|
313
322
|
path=path,
|
|
314
323
|
func=func,
|
|
315
324
|
category=category,
|
|
316
|
-
preset_args=
|
|
325
|
+
preset_args=final_preset_args,
|
|
317
326
|
description=descriptions[i],
|
|
318
327
|
)
|
|
319
328
|
|
|
@@ -404,9 +413,19 @@ class CustomToolAndMCPBackend(LLMBackend):
|
|
|
404
413
|
"""
|
|
405
414
|
import json
|
|
406
415
|
|
|
416
|
+
# Parse arguments
|
|
417
|
+
arguments = json.loads(call["arguments"]) if isinstance(call["arguments"], str) else call["arguments"]
|
|
418
|
+
|
|
419
|
+
# Ensure agent_cwd is always injected if filesystem_manager is available
|
|
420
|
+
# This provides a fallback in case preset_args didn't work during registration
|
|
421
|
+
if self.filesystem_manager and self.filesystem_manager.cwd:
|
|
422
|
+
if "agent_cwd" not in arguments or arguments.get("agent_cwd") is None:
|
|
423
|
+
arguments["agent_cwd"] = self.filesystem_manager.cwd
|
|
424
|
+
logger.info(f"Dynamically injected agent_cwd at execution time: {self.filesystem_manager.cwd}")
|
|
425
|
+
|
|
407
426
|
tool_request = {
|
|
408
427
|
"name": call["name"],
|
|
409
|
-
"input":
|
|
428
|
+
"input": arguments,
|
|
410
429
|
}
|
|
411
430
|
|
|
412
431
|
result_text = ""
|
|
@@ -1120,6 +1139,7 @@ class CustomToolAndMCPBackend(LLMBackend):
|
|
|
1120
1139
|
**kwargs,
|
|
1121
1140
|
) -> AsyncGenerator[StreamChunk, None]:
|
|
1122
1141
|
"""Simple passthrough streaming without MCP processing."""
|
|
1142
|
+
|
|
1123
1143
|
agent_id = kwargs.get("agent_id", None)
|
|
1124
1144
|
all_params = {**self.config, **kwargs}
|
|
1125
1145
|
processed_messages = await self._process_upload_files(messages, all_params)
|
massgen/backend/claude_code.py
CHANGED
|
@@ -795,7 +795,7 @@ class ClaudeCodeBackend(LLMBackend):
|
|
|
795
795
|
|
|
796
796
|
# Add workflow tools information if present
|
|
797
797
|
if tools:
|
|
798
|
-
workflow_tools = [t for t in tools if t.get("function", {}).get("name") in ["new_answer", "vote"]]
|
|
798
|
+
workflow_tools = [t for t in tools if t.get("function", {}).get("name") in ["new_answer", "vote", "submit", "restart_orchestration"]]
|
|
799
799
|
if workflow_tools:
|
|
800
800
|
system_parts.append("\n--- Coordination Actions ---")
|
|
801
801
|
for tool in workflow_tools:
|
|
@@ -823,6 +823,14 @@ class ClaudeCodeBackend(LLMBackend):
|
|
|
823
823
|
system_parts.append(f' Usage: {{"tool_name": "vote", ' f'"arguments": {{"agent_id": "agent1", ' f'"reason": "explanation"}}}} // Choose agent_id from: {agent_list}')
|
|
824
824
|
else:
|
|
825
825
|
system_parts.append(' Usage: {"tool_name": "vote", ' '"arguments": {"agent_id": "agent1", ' '"reason": "explanation"}}')
|
|
826
|
+
elif name == "submit":
|
|
827
|
+
system_parts.append(
|
|
828
|
+
' Usage: {"tool_name": "submit", ' '"arguments": {"confirmed": true}}',
|
|
829
|
+
)
|
|
830
|
+
elif name == "restart_orchestration":
|
|
831
|
+
system_parts.append(
|
|
832
|
+
' Usage: {"tool_name": "restart_orchestration", ' '"arguments": {"reason": "The answer is incomplete because...", ' '"instructions": "In the next attempt, please..."}}',
|
|
833
|
+
)
|
|
826
834
|
|
|
827
835
|
system_parts.append("\n--- MassGen Coordination Instructions ---")
|
|
828
836
|
system_parts.append("IMPORTANT: You must respond with a structured JSON decision at the end of your response.")
|
|
@@ -1067,8 +1067,8 @@ Files delivered:
|
|
|
1067
1067
|
- **Multi-Turn Design**: `docs/dev_notes/multi_turn_filesystem_design.md` - Detailed architecture for session persistence and turn-based workflows
|
|
1068
1068
|
- **MCP Integration**: `docs/dev_notes/gemini_filesystem_mcp_design.md` - How filesystem access works through Model Context Protocol
|
|
1069
1069
|
- **Context Sharing**: `docs/dev_notes/v0.0.14-context.md` - Original context sharing design
|
|
1070
|
-
- **User Context Paths**: `docs/case_studies/user-context-path-support-with-copy-mcp.md` - Case study on adding user-specified paths
|
|
1071
|
-
- **Claude Code Workspace**: `docs/case_studies/claude-code-workspace-management.md` - Native filesystem integration patterns
|
|
1070
|
+
- **User Context Paths**: `docs/source/examples/case_studies/user-context-path-support-with-copy-mcp.md` - Case study on adding user-specified paths
|
|
1071
|
+
- **Claude Code Workspace**: `docs/source/examples/case_studies/claude-code-workspace-management.md` - Native filesystem integration patterns
|
|
1072
1072
|
|
|
1073
1073
|
## Conclusion
|
|
1074
1074
|
|
massgen/backend/gemini.py
CHANGED
|
@@ -20,6 +20,7 @@ TECHNICAL SOLUTION:
|
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
import json
|
|
23
|
+
import logging
|
|
23
24
|
import os
|
|
24
25
|
import time
|
|
25
26
|
from typing import Any, AsyncGenerator, Dict, List, Optional
|
|
@@ -39,6 +40,19 @@ from .gemini_mcp_manager import GeminiMCPManager
|
|
|
39
40
|
from .gemini_trackers import MCPCallTracker, MCPResponseExtractor, MCPResponseTracker
|
|
40
41
|
from .gemini_utils import CoordinationResponse
|
|
41
42
|
|
|
43
|
+
|
|
44
|
+
# Suppress Gemini SDK logger warning about non-text parts in response
|
|
45
|
+
# Using custom filter per https://github.com/googleapis/python-genai/issues/850
|
|
46
|
+
class NoFunctionCallWarning(logging.Filter):
|
|
47
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
48
|
+
message = record.getMessage()
|
|
49
|
+
if "there are non-text parts in the response:" in message:
|
|
50
|
+
return False
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
logging.getLogger("google_genai.types").addFilter(NoFunctionCallWarning())
|
|
55
|
+
|
|
42
56
|
try:
|
|
43
57
|
from pydantic import BaseModel, Field
|
|
44
58
|
except ImportError:
|
|
@@ -220,6 +234,7 @@ class GeminiBackend(CustomToolAndMCPBackend):
|
|
|
220
234
|
|
|
221
235
|
# Analyze tool types
|
|
222
236
|
is_coordination = self.formatter.has_coordination_tools(tools)
|
|
237
|
+
is_post_evaluation = self.formatter.has_post_evaluation_tools(tools)
|
|
223
238
|
|
|
224
239
|
valid_agent_ids = None
|
|
225
240
|
|
|
@@ -239,6 +254,9 @@ class GeminiBackend(CustomToolAndMCPBackend):
|
|
|
239
254
|
# For coordination requests, modify the prompt to use structured output
|
|
240
255
|
if is_coordination:
|
|
241
256
|
full_content = self.formatter.build_structured_output_prompt(full_content, valid_agent_ids)
|
|
257
|
+
elif is_post_evaluation:
|
|
258
|
+
# For post-evaluation, modify prompt to use structured output
|
|
259
|
+
full_content = self.formatter.build_post_evaluation_prompt(full_content)
|
|
242
260
|
|
|
243
261
|
# Use google-genai package
|
|
244
262
|
client = genai.Client(api_key=self.api_key)
|
|
@@ -277,6 +295,16 @@ class GeminiBackend(CustomToolAndMCPBackend):
|
|
|
277
295
|
else:
|
|
278
296
|
# Tools or sessions are present; fallback to text parsing
|
|
279
297
|
pass
|
|
298
|
+
elif is_post_evaluation:
|
|
299
|
+
# For post-evaluation, use JSON response format for structured decisions
|
|
300
|
+
from .gemini_utils import PostEvaluationResponse
|
|
301
|
+
|
|
302
|
+
if (not using_sdk_mcp) and (not using_custom_tools) and (not all_tools):
|
|
303
|
+
config["response_mime_type"] = "application/json"
|
|
304
|
+
config["response_schema"] = PostEvaluationResponse.model_json_schema()
|
|
305
|
+
else:
|
|
306
|
+
# Tools or sessions are present; fallback to text parsing
|
|
307
|
+
pass
|
|
280
308
|
# Log messages being sent after builtin_tools is defined
|
|
281
309
|
log_backend_agent_message(
|
|
282
310
|
agent_id or "default",
|
|
@@ -1603,11 +1631,11 @@ class GeminiBackend(CustomToolAndMCPBackend):
|
|
|
1603
1631
|
|
|
1604
1632
|
content = full_content_text
|
|
1605
1633
|
|
|
1606
|
-
# Process tool calls -
|
|
1634
|
+
# Process tool calls - coordination and post-evaluation tool calls (MCP manual mode removed)
|
|
1607
1635
|
tool_calls_detected: List[Dict[str, Any]] = []
|
|
1608
1636
|
|
|
1609
|
-
#
|
|
1610
|
-
if is_coordination and content.strip() and not tool_calls_detected:
|
|
1637
|
+
# Process coordination tools OR post-evaluation tools if present
|
|
1638
|
+
if (is_coordination or is_post_evaluation) and content.strip() and not tool_calls_detected:
|
|
1611
1639
|
# For structured output mode, the entire content is JSON
|
|
1612
1640
|
structured_response = None
|
|
1613
1641
|
# Try multiple parsing strategies
|
|
@@ -1626,14 +1654,15 @@ class GeminiBackend(CustomToolAndMCPBackend):
|
|
|
1626
1654
|
# Log conversion to tool calls (summary)
|
|
1627
1655
|
log_stream_chunk("backend.gemini", "tool_calls", tool_calls, agent_id)
|
|
1628
1656
|
|
|
1629
|
-
# Log each
|
|
1657
|
+
# Log each tool call for analytics/debugging
|
|
1658
|
+
tool_type = "post_evaluation" if is_post_evaluation else "coordination"
|
|
1630
1659
|
try:
|
|
1631
1660
|
for tool_call in tool_calls:
|
|
1632
1661
|
log_tool_call(
|
|
1633
1662
|
agent_id,
|
|
1634
|
-
tool_call.get("function", {}).get("name", "
|
|
1663
|
+
tool_call.get("function", {}).get("name", f"unknown_{tool_type}_tool"),
|
|
1635
1664
|
tool_call.get("function", {}).get("arguments", {}),
|
|
1636
|
-
result="
|
|
1665
|
+
result=f"{tool_type}_tool_called",
|
|
1637
1666
|
backend_name="gemini",
|
|
1638
1667
|
)
|
|
1639
1668
|
except Exception:
|
massgen/backend/gemini_utils.py
CHANGED
|
@@ -20,6 +20,13 @@ class ActionType(enum.Enum):
|
|
|
20
20
|
NEW_ANSWER = "new_answer"
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
class PostEvaluationActionType(enum.Enum):
|
|
24
|
+
"""Action types for post-evaluation structured output."""
|
|
25
|
+
|
|
26
|
+
SUBMIT = "submit"
|
|
27
|
+
RESTART = "restart"
|
|
28
|
+
|
|
29
|
+
|
|
23
30
|
class VoteAction(BaseModel):
|
|
24
31
|
"""Structured output for voting action."""
|
|
25
32
|
|
|
@@ -41,3 +48,26 @@ class CoordinationResponse(BaseModel):
|
|
|
41
48
|
action_type: ActionType = Field(description="Type of action to take")
|
|
42
49
|
vote_data: Optional[VoteAction] = Field(default=None, description="Vote data if action is vote")
|
|
43
50
|
answer_data: Optional[NewAnswerAction] = Field(default=None, description="Answer data if action is new_answer")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class SubmitAction(BaseModel):
|
|
54
|
+
"""Structured output for submit action (post-evaluation)."""
|
|
55
|
+
|
|
56
|
+
action: PostEvaluationActionType = Field(default=PostEvaluationActionType.SUBMIT, description="Action type")
|
|
57
|
+
confirmed: bool = Field(default=True, description="Confirmation that answer is satisfactory")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class RestartAction(BaseModel):
|
|
61
|
+
"""Structured output for restart action (post-evaluation)."""
|
|
62
|
+
|
|
63
|
+
action: PostEvaluationActionType = Field(default=PostEvaluationActionType.RESTART, description="Action type")
|
|
64
|
+
reason: str = Field(description="Clear explanation of why the answer is insufficient")
|
|
65
|
+
instructions: str = Field(description="Detailed, actionable guidance for agents on the next attempt")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PostEvaluationResponse(BaseModel):
|
|
69
|
+
"""Structured response for post-evaluation actions."""
|
|
70
|
+
|
|
71
|
+
action_type: PostEvaluationActionType = Field(description="Type of post-evaluation action to take")
|
|
72
|
+
submit_data: Optional[SubmitAction] = Field(default=None, description="Submit data if action is submit")
|
|
73
|
+
restart_data: Optional[RestartAction] = Field(default=None, description="Restart data if action is restart")
|
massgen/backend/response.py
CHANGED
|
@@ -57,6 +57,7 @@ class ResponseBackend(CustomToolAndMCPBackend):
|
|
|
57
57
|
|
|
58
58
|
Wraps parent implementation to ensure File Search cleanup happens after streaming completes.
|
|
59
59
|
"""
|
|
60
|
+
|
|
60
61
|
try:
|
|
61
62
|
async for chunk in super().stream_with_tools(messages, tools, **kwargs):
|
|
62
63
|
yield chunk
|
|
@@ -145,6 +146,7 @@ class ResponseBackend(CustomToolAndMCPBackend):
|
|
|
145
146
|
**kwargs,
|
|
146
147
|
) -> AsyncGenerator[StreamChunk, None]:
|
|
147
148
|
"""Recursively stream MCP responses, executing function calls as needed."""
|
|
149
|
+
|
|
148
150
|
agent_id = kwargs.get("agent_id")
|
|
149
151
|
|
|
150
152
|
# Build API params for this iteration
|
massgen/chat_agent.py
CHANGED
|
@@ -365,10 +365,15 @@ class ConfigurableAgent(SingleAgent):
|
|
|
365
365
|
backend: LLM backend
|
|
366
366
|
session_id: Optional session identifier
|
|
367
367
|
"""
|
|
368
|
+
# Extract system message without triggering deprecation warning
|
|
369
|
+
system_message = None
|
|
370
|
+
if hasattr(config, "_custom_system_instruction"):
|
|
371
|
+
system_message = config._custom_system_instruction
|
|
372
|
+
|
|
368
373
|
super().__init__(
|
|
369
374
|
backend=backend,
|
|
370
375
|
agent_id=config.agent_id,
|
|
371
|
-
system_message=
|
|
376
|
+
system_message=system_message,
|
|
372
377
|
session_id=session_id,
|
|
373
378
|
)
|
|
374
379
|
self.config = config
|
|
@@ -411,8 +416,9 @@ class ConfigurableAgent(SingleAgent):
|
|
|
411
416
|
return backend_params["append_system_prompt"]
|
|
412
417
|
|
|
413
418
|
# Fall back to custom_system_instruction (deprecated but still supported)
|
|
414
|
-
|
|
415
|
-
|
|
419
|
+
# Access private attribute directly to avoid deprecation warning
|
|
420
|
+
if self.config and hasattr(self.config, "_custom_system_instruction") and self.config._custom_system_instruction:
|
|
421
|
+
return self.config._custom_system_instruction
|
|
416
422
|
|
|
417
423
|
# Finally fall back to parent class implementation
|
|
418
424
|
return super().get_configurable_system_message()
|