massgen 0.1.0a3__py3-none-any.whl → 0.1.1__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 +8 -1
- 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 +31 -0
- massgen/backend/{base_with_mcp.py → base_with_custom_tool_and_mcp.py} +282 -11
- massgen/backend/chat_completions.py +182 -92
- 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 +1275 -1607
- massgen/backend/gemini_mcp_manager.py +545 -0
- massgen/backend/gemini_trackers.py +344 -0
- massgen/backend/gemini_utils.py +43 -0
- massgen/backend/response.py +129 -70
- massgen/cli.py +577 -110
- massgen/config_builder.py +376 -27
- massgen/configs/README.md +111 -80
- massgen/configs/basic/multi/three_agents_default.yaml +1 -1
- 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/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 +179 -10
- 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_tools.py +127 -0
- 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.1.dist-info}/METADATA +89 -131
- {massgen-0.1.0a3.dist-info → massgen-0.1.1.dist-info}/RECORD +111 -36
- {massgen-0.1.0a3.dist-info → massgen-0.1.1.dist-info}/WHEEL +0 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.1.dist-info}/entry_points.txt +0 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.1.dist-info}/licenses/LICENSE +0 -0
- {massgen-0.1.0a3.dist-info → massgen-0.1.1.dist-info}/top_level.txt +0 -0
massgen/cli.py
CHANGED
|
@@ -8,30 +8,34 @@ Supports both interactive mode and single-question mode.
|
|
|
8
8
|
|
|
9
9
|
Usage examples:
|
|
10
10
|
# Use YAML/JSON configuration file
|
|
11
|
-
|
|
11
|
+
massgen --config config.yaml "What is the capital of France?"
|
|
12
12
|
|
|
13
13
|
# Quick setup with backend and model
|
|
14
|
-
|
|
14
|
+
massgen --backend openai --model gpt-4o-mini "What is 2+2?"
|
|
15
15
|
|
|
16
16
|
# Interactive mode
|
|
17
|
-
|
|
17
|
+
massgen --config config.yaml
|
|
18
|
+
massgen # Uses default config if available
|
|
18
19
|
|
|
19
20
|
# Multiple agents from config
|
|
20
|
-
|
|
21
|
+
massgen --config multi_agent.yaml "Compare different approaches to renewable energy"
|
|
21
22
|
"""
|
|
22
23
|
|
|
23
24
|
import argparse
|
|
24
25
|
import asyncio
|
|
26
|
+
import copy
|
|
25
27
|
import json
|
|
26
28
|
import os
|
|
27
29
|
import shutil
|
|
28
30
|
import sys
|
|
29
31
|
from datetime import datetime
|
|
30
32
|
from pathlib import Path
|
|
31
|
-
from typing import Any, Dict, List, Optional
|
|
33
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
32
34
|
|
|
35
|
+
import questionary
|
|
33
36
|
import yaml
|
|
34
37
|
from dotenv import load_dotenv
|
|
38
|
+
from prompt_toolkit.styles import Style
|
|
35
39
|
from rich.console import Console
|
|
36
40
|
from rich.panel import Panel
|
|
37
41
|
from rich.table import Table
|
|
@@ -48,7 +52,7 @@ from .backend.lmstudio import LMStudioBackend
|
|
|
48
52
|
from .backend.response import ResponseBackend
|
|
49
53
|
from .chat_agent import ConfigurableAgent, SingleAgent
|
|
50
54
|
from .frontend.coordination_ui import CoordinationUI
|
|
51
|
-
from .logger_config import _DEBUG_MODE, logger, setup_logging
|
|
55
|
+
from .logger_config import _DEBUG_MODE, logger, save_execution_metadata, setup_logging
|
|
52
56
|
from .orchestrator import Orchestrator
|
|
53
57
|
from .utils import get_backend_type_from_model
|
|
54
58
|
|
|
@@ -86,6 +90,22 @@ BRIGHT_WHITE = "\033[97m"
|
|
|
86
90
|
RESET = "\033[0m"
|
|
87
91
|
BOLD = "\033[1m"
|
|
88
92
|
|
|
93
|
+
# Custom questionary style for polished selection interface
|
|
94
|
+
MASSGEN_QUESTIONARY_STYLE = Style(
|
|
95
|
+
[
|
|
96
|
+
("qmark", "fg:#00d7ff bold"), # Bright cyan question mark
|
|
97
|
+
("question", "fg:#ffffff bold"), # White question text
|
|
98
|
+
("answer", "fg:#00d7ff bold"), # Bright cyan answer
|
|
99
|
+
("pointer", "fg:#00d7ff bold"), # Bright cyan pointer (▸)
|
|
100
|
+
("highlighted", "fg:#00d7ff bold"), # Bright cyan highlighted option
|
|
101
|
+
("selected", "fg:#00ff87"), # Bright green selected
|
|
102
|
+
("separator", "fg:#6c6c6c"), # Gray separators
|
|
103
|
+
("instruction", "fg:#808080"), # Gray instructions
|
|
104
|
+
("text", "fg:#ffffff"), # White text
|
|
105
|
+
("disabled", "fg:#6c6c6c italic"), # Gray disabled
|
|
106
|
+
],
|
|
107
|
+
)
|
|
108
|
+
|
|
89
109
|
|
|
90
110
|
class ConfigurationError(Exception):
|
|
91
111
|
"""Configuration error for CLI."""
|
|
@@ -802,105 +822,94 @@ async def run_question_with_history(
|
|
|
802
822
|
messages = history.copy()
|
|
803
823
|
messages.append({"role": "user", "content": question})
|
|
804
824
|
|
|
805
|
-
#
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
825
|
+
# In multiturn mode with session persistence, ALWAYS use orchestrator for proper final/ directory creation
|
|
826
|
+
# Single agents in multiturn mode need the orchestrator to create session artifacts (final/, workspace/, etc.)
|
|
827
|
+
# The orchestrator handles single agents efficiently by skipping unnecessary coordination
|
|
828
|
+
|
|
829
|
+
# Create orchestrator config with timeout settings
|
|
830
|
+
timeout_config = kwargs.get("timeout_config")
|
|
831
|
+
orchestrator_config = AgentConfig()
|
|
832
|
+
if timeout_config:
|
|
833
|
+
orchestrator_config.timeout_config = timeout_config
|
|
834
|
+
|
|
835
|
+
# Get orchestrator parameters from config
|
|
836
|
+
orchestrator_cfg = kwargs.get("orchestrator", {})
|
|
837
|
+
|
|
838
|
+
# Apply voting sensitivity if specified
|
|
839
|
+
if "voting_sensitivity" in orchestrator_cfg:
|
|
840
|
+
orchestrator_config.voting_sensitivity = orchestrator_cfg["voting_sensitivity"]
|
|
841
|
+
|
|
842
|
+
# Apply answer count limit if specified
|
|
843
|
+
if "max_new_answers_per_agent" in orchestrator_cfg:
|
|
844
|
+
orchestrator_config.max_new_answers_per_agent = orchestrator_cfg["max_new_answers_per_agent"]
|
|
845
|
+
|
|
846
|
+
# Apply answer novelty requirement if specified
|
|
847
|
+
if "answer_novelty_requirement" in orchestrator_cfg:
|
|
848
|
+
orchestrator_config.answer_novelty_requirement = orchestrator_cfg["answer_novelty_requirement"]
|
|
849
|
+
|
|
850
|
+
# Get context sharing parameters
|
|
851
|
+
snapshot_storage = orchestrator_cfg.get("snapshot_storage")
|
|
852
|
+
agent_temporary_workspace = orchestrator_cfg.get("agent_temporary_workspace")
|
|
853
|
+
session_storage = orchestrator_cfg.get("session_storage", "sessions") # Default to "sessions"
|
|
854
|
+
|
|
855
|
+
# Get debug/test parameters
|
|
856
|
+
if orchestrator_cfg.get("skip_coordination_rounds", False):
|
|
857
|
+
orchestrator_config.skip_coordination_rounds = True
|
|
858
|
+
|
|
859
|
+
# Load previous turns from session storage for multi-turn conversations
|
|
860
|
+
previous_turns = load_previous_turns(session_info, session_storage)
|
|
861
|
+
|
|
862
|
+
orchestrator = Orchestrator(
|
|
863
|
+
agents=agents,
|
|
864
|
+
config=orchestrator_config,
|
|
865
|
+
snapshot_storage=snapshot_storage,
|
|
866
|
+
agent_temporary_workspace=agent_temporary_workspace,
|
|
867
|
+
previous_turns=previous_turns,
|
|
868
|
+
)
|
|
869
|
+
# Create a fresh UI instance for each question to ensure clean state
|
|
870
|
+
ui = CoordinationUI(
|
|
871
|
+
display_type=ui_config.get("display_type", "rich_terminal"),
|
|
872
|
+
logging_enabled=ui_config.get("logging_enabled", True),
|
|
873
|
+
enable_final_presentation=True, # Required for multi-turn: ensures final answer is saved
|
|
874
|
+
)
|
|
835
875
|
|
|
876
|
+
# Determine display mode text
|
|
877
|
+
if len(agents) == 1:
|
|
878
|
+
mode_text = "Single Agent (Orchestrator)"
|
|
836
879
|
else:
|
|
837
|
-
|
|
838
|
-
# Create orchestrator config with timeout settings
|
|
839
|
-
timeout_config = kwargs.get("timeout_config")
|
|
840
|
-
orchestrator_config = AgentConfig()
|
|
841
|
-
if timeout_config:
|
|
842
|
-
orchestrator_config.timeout_config = timeout_config
|
|
843
|
-
|
|
844
|
-
# Get orchestrator parameters from config
|
|
845
|
-
orchestrator_cfg = kwargs.get("orchestrator", {})
|
|
846
|
-
|
|
847
|
-
# Get context sharing parameters
|
|
848
|
-
snapshot_storage = orchestrator_cfg.get("snapshot_storage")
|
|
849
|
-
agent_temporary_workspace = orchestrator_cfg.get("agent_temporary_workspace")
|
|
850
|
-
session_storage = orchestrator_cfg.get("session_storage", "sessions") # Default to "sessions"
|
|
851
|
-
|
|
852
|
-
# Get debug/test parameters
|
|
853
|
-
if orchestrator_cfg.get("skip_coordination_rounds", False):
|
|
854
|
-
orchestrator_config.skip_coordination_rounds = True
|
|
855
|
-
|
|
856
|
-
# Load previous turns from session storage for multi-turn conversations
|
|
857
|
-
previous_turns = load_previous_turns(session_info, session_storage)
|
|
858
|
-
|
|
859
|
-
orchestrator = Orchestrator(
|
|
860
|
-
agents=agents,
|
|
861
|
-
config=orchestrator_config,
|
|
862
|
-
snapshot_storage=snapshot_storage,
|
|
863
|
-
agent_temporary_workspace=agent_temporary_workspace,
|
|
864
|
-
previous_turns=previous_turns,
|
|
865
|
-
)
|
|
866
|
-
# Create a fresh UI instance for each question to ensure clean state
|
|
867
|
-
ui = CoordinationUI(
|
|
868
|
-
display_type=ui_config.get("display_type", "rich_terminal"),
|
|
869
|
-
logging_enabled=ui_config.get("logging_enabled", True),
|
|
870
|
-
enable_final_presentation=True, # Required for multi-turn: ensures final answer is saved
|
|
871
|
-
)
|
|
880
|
+
mode_text = "Multi-Agent"
|
|
872
881
|
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
882
|
+
print(f"\n🤖 {BRIGHT_CYAN}{mode_text}{RESET}", flush=True)
|
|
883
|
+
print(f"Agents: {', '.join(agents.keys())}", flush=True)
|
|
884
|
+
if history:
|
|
885
|
+
print(f"History: {len(history)//2} previous exchanges", flush=True)
|
|
886
|
+
print(f"Question: {question}", flush=True)
|
|
887
|
+
print("\n" + "=" * 60, flush=True)
|
|
879
888
|
|
|
880
|
-
|
|
881
|
-
|
|
889
|
+
# For multi-agent with history, we need to use a different approach
|
|
890
|
+
# that maintains coordination UI display while supporting conversation context
|
|
882
891
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
892
|
+
if history and len(history) > 0:
|
|
893
|
+
# Use coordination UI with conversation context
|
|
894
|
+
# Extract current question from messages
|
|
895
|
+
current_question = messages[-1].get("content", question) if messages else question
|
|
887
896
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
897
|
+
# Pass the full message context to the UI coordination
|
|
898
|
+
response_content = await ui.coordinate_with_context(orchestrator, current_question, messages)
|
|
899
|
+
else:
|
|
900
|
+
# Standard coordination for new conversations
|
|
901
|
+
response_content = await ui.coordinate(orchestrator, question)
|
|
902
|
+
|
|
903
|
+
# Handle session persistence if applicable
|
|
904
|
+
session_id_to_use, updated_turn, normalized_response = await handle_session_persistence(
|
|
905
|
+
orchestrator,
|
|
906
|
+
question,
|
|
907
|
+
session_info,
|
|
908
|
+
session_storage,
|
|
909
|
+
)
|
|
901
910
|
|
|
902
|
-
|
|
903
|
-
|
|
911
|
+
# Return normalized response so conversation history has correct paths
|
|
912
|
+
return (normalized_response or response_content, session_id_to_use, updated_turn)
|
|
904
913
|
|
|
905
914
|
|
|
906
915
|
async def run_single_question(question: str, agents: Dict[str, SingleAgent], ui_config: Dict[str, Any], **kwargs) -> str:
|
|
@@ -945,6 +954,18 @@ async def run_single_question(question: str, agents: Dict[str, SingleAgent], ui_
|
|
|
945
954
|
# Get orchestrator parameters from config
|
|
946
955
|
orchestrator_cfg = kwargs.get("orchestrator", {})
|
|
947
956
|
|
|
957
|
+
# Apply voting sensitivity if specified
|
|
958
|
+
if "voting_sensitivity" in orchestrator_cfg:
|
|
959
|
+
orchestrator_config.voting_sensitivity = orchestrator_cfg["voting_sensitivity"]
|
|
960
|
+
|
|
961
|
+
# Apply answer count limit if specified
|
|
962
|
+
if "max_new_answers_per_agent" in orchestrator_cfg:
|
|
963
|
+
orchestrator_config.max_new_answers_per_agent = orchestrator_cfg["max_new_answers_per_agent"]
|
|
964
|
+
|
|
965
|
+
# Apply answer novelty requirement if specified
|
|
966
|
+
if "answer_novelty_requirement" in orchestrator_cfg:
|
|
967
|
+
orchestrator_config.answer_novelty_requirement = orchestrator_cfg["answer_novelty_requirement"]
|
|
968
|
+
|
|
948
969
|
# Get context sharing parameters
|
|
949
970
|
snapshot_storage = orchestrator_cfg.get("snapshot_storage")
|
|
950
971
|
agent_temporary_workspace = orchestrator_cfg.get("agent_temporary_workspace")
|
|
@@ -1219,6 +1240,411 @@ def print_example_config(name: str):
|
|
|
1219
1240
|
sys.exit(1)
|
|
1220
1241
|
|
|
1221
1242
|
|
|
1243
|
+
def discover_available_configs() -> Dict[str, List[Tuple[str, Path]]]:
|
|
1244
|
+
"""Discover all available configuration files.
|
|
1245
|
+
|
|
1246
|
+
Returns:
|
|
1247
|
+
Dict with categories as keys and list of (display_name, path) tuples as values
|
|
1248
|
+
"""
|
|
1249
|
+
configs = {
|
|
1250
|
+
"User Configs": [],
|
|
1251
|
+
"Project Configs": [],
|
|
1252
|
+
"Current Directory": [],
|
|
1253
|
+
"Package Examples": [],
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
# 1. User configs (~/.config/massgen/agents/)
|
|
1257
|
+
user_agents_dir = Path.home() / ".config/massgen/agents"
|
|
1258
|
+
if user_agents_dir.exists():
|
|
1259
|
+
for config_file in sorted(user_agents_dir.glob("*.yaml")):
|
|
1260
|
+
display_name = config_file.stem
|
|
1261
|
+
configs["User Configs"].append((display_name, config_file))
|
|
1262
|
+
|
|
1263
|
+
# 2. Project configs (.massgen/)
|
|
1264
|
+
project_config_dir = Path.cwd() / ".massgen"
|
|
1265
|
+
if project_config_dir.exists():
|
|
1266
|
+
for config_file in sorted(project_config_dir.glob("*.yaml")):
|
|
1267
|
+
display_name = f".massgen/{config_file.name}"
|
|
1268
|
+
configs["Project Configs"].append((display_name, config_file))
|
|
1269
|
+
|
|
1270
|
+
# 3. Current directory (*.yaml files, excluding .massgen/ and non-massgen configs)
|
|
1271
|
+
# Filter out common non-massgen YAML files
|
|
1272
|
+
exclude_patterns = {
|
|
1273
|
+
".pre-commit-config.yaml",
|
|
1274
|
+
".readthedocs.yaml",
|
|
1275
|
+
".github",
|
|
1276
|
+
"docker-compose",
|
|
1277
|
+
"ansible",
|
|
1278
|
+
"kubernetes",
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
for config_file in sorted(Path.cwd().glob("*.yaml")):
|
|
1282
|
+
# Skip if inside .massgen/ (already covered)
|
|
1283
|
+
if ".massgen" in str(config_file):
|
|
1284
|
+
continue
|
|
1285
|
+
|
|
1286
|
+
# Skip common non-massgen config files
|
|
1287
|
+
file_name = config_file.name.lower()
|
|
1288
|
+
if any(pattern in file_name for pattern in exclude_patterns):
|
|
1289
|
+
continue
|
|
1290
|
+
|
|
1291
|
+
display_name = config_file.name
|
|
1292
|
+
configs["Current Directory"].append((display_name, config_file))
|
|
1293
|
+
|
|
1294
|
+
# 4. Package examples (massgen/configs/)
|
|
1295
|
+
try:
|
|
1296
|
+
from importlib.resources import files
|
|
1297
|
+
|
|
1298
|
+
configs_root = files("massgen") / "configs"
|
|
1299
|
+
|
|
1300
|
+
# Organize by subdirectory
|
|
1301
|
+
for config_file in sorted(configs_root.rglob("*.yaml")):
|
|
1302
|
+
# Get relative path from configs root
|
|
1303
|
+
rel_path = str(config_file).replace(str(configs_root) + "/", "")
|
|
1304
|
+
# Skip README and docs
|
|
1305
|
+
if "README" in rel_path or "BACKEND_CONFIGURATION" in rel_path:
|
|
1306
|
+
continue
|
|
1307
|
+
# Use relative path as display name
|
|
1308
|
+
display_name = rel_path.replace(".yaml", "")
|
|
1309
|
+
configs["Package Examples"].append((display_name, Path(str(config_file))))
|
|
1310
|
+
|
|
1311
|
+
except Exception as e:
|
|
1312
|
+
logger.warning(f"Could not load package examples: {e}")
|
|
1313
|
+
|
|
1314
|
+
# Remove empty categories
|
|
1315
|
+
configs = {k: v for k, v in configs.items() if v}
|
|
1316
|
+
|
|
1317
|
+
return configs
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
def interactive_config_selector() -> Optional[str]:
|
|
1321
|
+
"""Interactively select a configuration file.
|
|
1322
|
+
|
|
1323
|
+
Shows user/project/current directory configs directly in a flat list.
|
|
1324
|
+
Package examples are shown hierarchically (category → config).
|
|
1325
|
+
|
|
1326
|
+
Returns:
|
|
1327
|
+
Path to selected config file, or None if cancelled
|
|
1328
|
+
"""
|
|
1329
|
+
# Create console instance for rich output
|
|
1330
|
+
selector_console = Console()
|
|
1331
|
+
|
|
1332
|
+
# Discover all available configs
|
|
1333
|
+
configs = discover_available_configs()
|
|
1334
|
+
|
|
1335
|
+
if not configs:
|
|
1336
|
+
selector_console.print(
|
|
1337
|
+
"\n[yellow]⚠️ No configurations found![/yellow]",
|
|
1338
|
+
)
|
|
1339
|
+
selector_console.print("[dim]Create one with: massgen --init[/dim]\n")
|
|
1340
|
+
return None
|
|
1341
|
+
|
|
1342
|
+
# Create a summary table showing what's available
|
|
1343
|
+
summary_table = Table(
|
|
1344
|
+
show_header=True,
|
|
1345
|
+
header_style="bold bright_white",
|
|
1346
|
+
border_style="bright_black",
|
|
1347
|
+
box=None,
|
|
1348
|
+
padding=(0, 1),
|
|
1349
|
+
width=88,
|
|
1350
|
+
)
|
|
1351
|
+
summary_table.add_column("Category", style="bright_cyan", no_wrap=True, width=25)
|
|
1352
|
+
summary_table.add_column("Count", justify="center", style="bright_yellow", width=10)
|
|
1353
|
+
summary_table.add_column("Location", style="dim")
|
|
1354
|
+
|
|
1355
|
+
# Build summary and choices
|
|
1356
|
+
choices = []
|
|
1357
|
+
|
|
1358
|
+
# Build summary table (overview only - no duplication)
|
|
1359
|
+
# User configs
|
|
1360
|
+
if "User Configs" in configs and configs["User Configs"]:
|
|
1361
|
+
summary_table.add_row(
|
|
1362
|
+
"👤 Your Configs",
|
|
1363
|
+
str(len(configs["User Configs"])),
|
|
1364
|
+
"~/.config/massgen/agents/",
|
|
1365
|
+
)
|
|
1366
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1367
|
+
for display_name, path in configs["User Configs"]:
|
|
1368
|
+
choices.append(
|
|
1369
|
+
questionary.Choice(
|
|
1370
|
+
title=f" 👤 {display_name}",
|
|
1371
|
+
value=str(path),
|
|
1372
|
+
),
|
|
1373
|
+
)
|
|
1374
|
+
|
|
1375
|
+
# Project configs
|
|
1376
|
+
if "Project Configs" in configs and configs["Project Configs"]:
|
|
1377
|
+
summary_table.add_row(
|
|
1378
|
+
"📁 Project Configs",
|
|
1379
|
+
str(len(configs["Project Configs"])),
|
|
1380
|
+
".massgen/",
|
|
1381
|
+
)
|
|
1382
|
+
if choices:
|
|
1383
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1384
|
+
else:
|
|
1385
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1386
|
+
for display_name, path in configs["Project Configs"]:
|
|
1387
|
+
choices.append(
|
|
1388
|
+
questionary.Choice(
|
|
1389
|
+
title=f" 📁 {display_name}",
|
|
1390
|
+
value=str(path),
|
|
1391
|
+
),
|
|
1392
|
+
)
|
|
1393
|
+
|
|
1394
|
+
# Current directory configs
|
|
1395
|
+
if "Current Directory" in configs and configs["Current Directory"]:
|
|
1396
|
+
summary_table.add_row(
|
|
1397
|
+
"📂 Current Directory",
|
|
1398
|
+
str(len(configs["Current Directory"])),
|
|
1399
|
+
f"*.yaml in {Path.cwd().name}/",
|
|
1400
|
+
)
|
|
1401
|
+
if choices:
|
|
1402
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1403
|
+
else:
|
|
1404
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1405
|
+
for display_name, path in configs["Current Directory"]:
|
|
1406
|
+
choices.append(
|
|
1407
|
+
questionary.Choice(
|
|
1408
|
+
title=f" 📂 {display_name}",
|
|
1409
|
+
value=str(path),
|
|
1410
|
+
),
|
|
1411
|
+
)
|
|
1412
|
+
|
|
1413
|
+
# Package examples
|
|
1414
|
+
if "Package Examples" in configs and configs["Package Examples"]:
|
|
1415
|
+
summary_table.add_row(
|
|
1416
|
+
"📦 Package Examples",
|
|
1417
|
+
str(len(configs["Package Examples"])),
|
|
1418
|
+
"Built-in examples (hierarchical browser)",
|
|
1419
|
+
)
|
|
1420
|
+
if choices:
|
|
1421
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1422
|
+
choices.append(
|
|
1423
|
+
questionary.Choice(
|
|
1424
|
+
title=f" 📦 Browse {len(configs['Package Examples'])} example configs →",
|
|
1425
|
+
value="__browse_examples__",
|
|
1426
|
+
),
|
|
1427
|
+
)
|
|
1428
|
+
|
|
1429
|
+
# Display summary table in a panel
|
|
1430
|
+
selector_console.print()
|
|
1431
|
+
selector_console.print(
|
|
1432
|
+
Panel(
|
|
1433
|
+
summary_table,
|
|
1434
|
+
title="[bold bright_cyan]🚀 Select a Configuration[/bold bright_cyan]",
|
|
1435
|
+
border_style="bright_cyan",
|
|
1436
|
+
padding=(0, 1),
|
|
1437
|
+
width=90,
|
|
1438
|
+
),
|
|
1439
|
+
)
|
|
1440
|
+
|
|
1441
|
+
# Add cancel option
|
|
1442
|
+
choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1443
|
+
choices.append(questionary.Choice(title=" ❌ Cancel", value="__cancel__"))
|
|
1444
|
+
|
|
1445
|
+
# Show the selector
|
|
1446
|
+
selector_console.print()
|
|
1447
|
+
selected = questionary.select(
|
|
1448
|
+
"Select a configuration:",
|
|
1449
|
+
choices=choices,
|
|
1450
|
+
use_shortcuts=True,
|
|
1451
|
+
use_arrow_keys=True,
|
|
1452
|
+
style=MASSGEN_QUESTIONARY_STYLE,
|
|
1453
|
+
pointer="▸",
|
|
1454
|
+
).ask()
|
|
1455
|
+
|
|
1456
|
+
if selected is None or selected == "__cancel__":
|
|
1457
|
+
selector_console.print("\n[yellow]⚠️ Selection cancelled[/yellow]\n")
|
|
1458
|
+
return None
|
|
1459
|
+
|
|
1460
|
+
# If user wants to browse package examples, show hierarchical navigation
|
|
1461
|
+
if selected == "__browse_examples__":
|
|
1462
|
+
return _select_package_example(configs["Package Examples"], selector_console)
|
|
1463
|
+
|
|
1464
|
+
# Otherwise, return the selected config path
|
|
1465
|
+
selector_console.print(f"\n[bold green]✓ Selected:[/bold green] [cyan]{selected}[/cyan]\n")
|
|
1466
|
+
return selected
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
def _select_package_example(examples: List[Tuple[str, Path]], console: Console) -> Optional[str]:
|
|
1470
|
+
"""Show hierarchical navigation for package examples.
|
|
1471
|
+
|
|
1472
|
+
Args:
|
|
1473
|
+
examples: List of (display_name, path) tuples
|
|
1474
|
+
console: Rich console for output
|
|
1475
|
+
|
|
1476
|
+
Returns:
|
|
1477
|
+
Path to selected config, or None if cancelled/back
|
|
1478
|
+
"""
|
|
1479
|
+
# Organize examples by category (first directory in path)
|
|
1480
|
+
categories = {}
|
|
1481
|
+
for display_name, path in examples:
|
|
1482
|
+
# Extract category from display name (e.g., "basic/multi/config" -> "basic")
|
|
1483
|
+
parts = display_name.split("/")
|
|
1484
|
+
category = parts[0] if len(parts) > 1 else "other"
|
|
1485
|
+
|
|
1486
|
+
if category not in categories:
|
|
1487
|
+
categories[category] = []
|
|
1488
|
+
categories[category].append((display_name, path))
|
|
1489
|
+
|
|
1490
|
+
# Emoji mapping for categories
|
|
1491
|
+
category_emojis = {
|
|
1492
|
+
"basic": "🎯",
|
|
1493
|
+
"tools": "🛠️",
|
|
1494
|
+
"providers": "🌐",
|
|
1495
|
+
"configs": "⚙️",
|
|
1496
|
+
"other": "📋",
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
# Create category summary table
|
|
1500
|
+
category_table = Table(
|
|
1501
|
+
show_header=True,
|
|
1502
|
+
header_style="bold bright_white",
|
|
1503
|
+
border_style="bright_black",
|
|
1504
|
+
box=None,
|
|
1505
|
+
padding=(0, 1),
|
|
1506
|
+
width=88,
|
|
1507
|
+
)
|
|
1508
|
+
category_table.add_column("Category", style="bright_cyan", no_wrap=True, width=20)
|
|
1509
|
+
category_table.add_column("Count", justify="center", style="bright_yellow", width=10)
|
|
1510
|
+
category_table.add_column("Description", style="dim")
|
|
1511
|
+
|
|
1512
|
+
# Category descriptions
|
|
1513
|
+
category_descriptions = {
|
|
1514
|
+
"basic": "Simple configurations for getting started",
|
|
1515
|
+
"tools": "Configs demonstrating tool integrations",
|
|
1516
|
+
"providers": "Provider-specific example configs",
|
|
1517
|
+
"configs": "Advanced configuration examples",
|
|
1518
|
+
"other": "Miscellaneous configurations",
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
# Build category table and choices
|
|
1522
|
+
category_choices = []
|
|
1523
|
+
for category in sorted(categories.keys()):
|
|
1524
|
+
count = len(categories[category])
|
|
1525
|
+
emoji = category_emojis.get(category, "📁")
|
|
1526
|
+
description = category_descriptions.get(category, "Example configurations")
|
|
1527
|
+
|
|
1528
|
+
category_table.add_row(
|
|
1529
|
+
f"{emoji} {category.title()}",
|
|
1530
|
+
str(count),
|
|
1531
|
+
description,
|
|
1532
|
+
)
|
|
1533
|
+
|
|
1534
|
+
category_choices.append(
|
|
1535
|
+
questionary.Choice(
|
|
1536
|
+
title=f" {emoji} {category.title()} ({count} config{'s' if count != 1 else ''})",
|
|
1537
|
+
value=category,
|
|
1538
|
+
),
|
|
1539
|
+
)
|
|
1540
|
+
|
|
1541
|
+
# Display category summary in a panel
|
|
1542
|
+
console.print()
|
|
1543
|
+
console.print(
|
|
1544
|
+
Panel(
|
|
1545
|
+
category_table,
|
|
1546
|
+
title="[bold bright_yellow]📦 Package Examples - Select Category[/bold bright_yellow]",
|
|
1547
|
+
border_style="bright_yellow",
|
|
1548
|
+
padding=(0, 1),
|
|
1549
|
+
width=90,
|
|
1550
|
+
),
|
|
1551
|
+
)
|
|
1552
|
+
|
|
1553
|
+
# Add back option
|
|
1554
|
+
category_choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1555
|
+
category_choices.append(questionary.Choice(title=" ← Back to main menu", value="__back__"))
|
|
1556
|
+
|
|
1557
|
+
# Step 1: Select category
|
|
1558
|
+
console.print()
|
|
1559
|
+
selected_category = questionary.select(
|
|
1560
|
+
"Select a category:",
|
|
1561
|
+
choices=category_choices,
|
|
1562
|
+
use_shortcuts=True,
|
|
1563
|
+
use_arrow_keys=True,
|
|
1564
|
+
style=MASSGEN_QUESTIONARY_STYLE,
|
|
1565
|
+
pointer="▸",
|
|
1566
|
+
).ask()
|
|
1567
|
+
|
|
1568
|
+
if selected_category is None or selected_category == "__cancel__":
|
|
1569
|
+
console.print("\n[yellow]⚠️ Selection cancelled[/yellow]\n")
|
|
1570
|
+
return None
|
|
1571
|
+
|
|
1572
|
+
if selected_category == "__back__":
|
|
1573
|
+
# Go back to main selector
|
|
1574
|
+
return interactive_config_selector()
|
|
1575
|
+
|
|
1576
|
+
# Create configs table
|
|
1577
|
+
emoji = category_emojis.get(selected_category, "📁")
|
|
1578
|
+
configs_table = Table(
|
|
1579
|
+
show_header=True,
|
|
1580
|
+
header_style="bold bright_white",
|
|
1581
|
+
border_style="bright_black",
|
|
1582
|
+
box=None,
|
|
1583
|
+
padding=(0, 1),
|
|
1584
|
+
width=88,
|
|
1585
|
+
)
|
|
1586
|
+
configs_table.add_column("#", style="dim", width=5, justify="right")
|
|
1587
|
+
configs_table.add_column("Configuration", style="bright_cyan")
|
|
1588
|
+
|
|
1589
|
+
# Build config choices and table
|
|
1590
|
+
config_choices = []
|
|
1591
|
+
for idx, (display_name, path) in enumerate(sorted(categories[selected_category]), 1):
|
|
1592
|
+
# Show relative path within category
|
|
1593
|
+
short_name = display_name.replace(f"{selected_category}/", "")
|
|
1594
|
+
configs_table.add_row(str(idx), short_name)
|
|
1595
|
+
config_choices.append(
|
|
1596
|
+
questionary.Choice(
|
|
1597
|
+
title=f" {idx:2d}. {short_name}",
|
|
1598
|
+
value=str(path),
|
|
1599
|
+
),
|
|
1600
|
+
)
|
|
1601
|
+
|
|
1602
|
+
# Display configs in a panel
|
|
1603
|
+
console.print()
|
|
1604
|
+
console.print(
|
|
1605
|
+
Panel(
|
|
1606
|
+
configs_table,
|
|
1607
|
+
title=f"[bold bright_green]{emoji} {selected_category.title()} Configurations[/bold bright_green]",
|
|
1608
|
+
border_style="bright_green",
|
|
1609
|
+
padding=(0, 1),
|
|
1610
|
+
width=90,
|
|
1611
|
+
),
|
|
1612
|
+
)
|
|
1613
|
+
|
|
1614
|
+
# Add back option
|
|
1615
|
+
config_choices.append(questionary.Separator("\n─────────────────────────────────"))
|
|
1616
|
+
config_choices.append(questionary.Choice(title=" ← Back to categories", value="__back__"))
|
|
1617
|
+
|
|
1618
|
+
# Step 2: Select config
|
|
1619
|
+
# For large lists: disable shortcuts (max 36) and enable search filter for better UX
|
|
1620
|
+
# Note: When search filter is enabled, j/k keys must be disabled (they conflict with search)
|
|
1621
|
+
use_shortcuts = len(config_choices) <= 36
|
|
1622
|
+
use_search_filter = len(config_choices) > 36
|
|
1623
|
+
console.print()
|
|
1624
|
+
selected_config = questionary.select(
|
|
1625
|
+
"Select a configuration:",
|
|
1626
|
+
choices=config_choices,
|
|
1627
|
+
use_shortcuts=use_shortcuts,
|
|
1628
|
+
use_arrow_keys=True,
|
|
1629
|
+
use_search_filter=use_search_filter,
|
|
1630
|
+
use_jk_keys=not use_search_filter,
|
|
1631
|
+
style=MASSGEN_QUESTIONARY_STYLE,
|
|
1632
|
+
pointer="▸",
|
|
1633
|
+
).ask()
|
|
1634
|
+
|
|
1635
|
+
if selected_config is None or selected_config == "__cancel__":
|
|
1636
|
+
console.print("\n[yellow]⚠️ Selection cancelled[/yellow]\n")
|
|
1637
|
+
return None
|
|
1638
|
+
|
|
1639
|
+
if selected_config == "__back__":
|
|
1640
|
+
# Recursively call to go back to category selection
|
|
1641
|
+
return _select_package_example(examples, console)
|
|
1642
|
+
|
|
1643
|
+
# Return the selected config path
|
|
1644
|
+
console.print(f"\n[bold green]✓ Selected:[/bold green] [cyan]{selected_config}[/cyan]\n")
|
|
1645
|
+
return selected_config
|
|
1646
|
+
|
|
1647
|
+
|
|
1222
1648
|
def should_run_builder() -> bool:
|
|
1223
1649
|
"""Check if config builder should run automatically.
|
|
1224
1650
|
|
|
@@ -1511,6 +1937,14 @@ async def run_interactive_mode(
|
|
|
1511
1937
|
setup_logging(debug=_DEBUG_MODE, turn=next_turn)
|
|
1512
1938
|
logger.info(f"Starting turn {next_turn}")
|
|
1513
1939
|
|
|
1940
|
+
# Save execution metadata for this turn (original_config already has pre-relocation paths)
|
|
1941
|
+
save_execution_metadata(
|
|
1942
|
+
query=question,
|
|
1943
|
+
config_path=config_path,
|
|
1944
|
+
config_content=original_config, # This is the pre-relocation config passed from main()
|
|
1945
|
+
cli_args={"mode": "interactive", "turn": next_turn, "session_id": session_id},
|
|
1946
|
+
)
|
|
1947
|
+
|
|
1514
1948
|
# Pass session state for multi-turn filesystem support
|
|
1515
1949
|
session_info = {
|
|
1516
1950
|
"session_id": session_id,
|
|
@@ -1612,6 +2046,9 @@ async def main(args):
|
|
|
1612
2046
|
logger.debug(f"Created simple config with backend: {backend}, model: {model}")
|
|
1613
2047
|
logger.debug(f"Config content: {json.dumps(config, indent=2)}")
|
|
1614
2048
|
|
|
2049
|
+
# Save original config before relocation (for execution_metadata.yaml)
|
|
2050
|
+
original_config_for_metadata = copy.deepcopy(config)
|
|
2051
|
+
|
|
1615
2052
|
# Validate that all context paths exist before proceeding
|
|
1616
2053
|
validate_context_paths(config)
|
|
1617
2054
|
|
|
@@ -1695,6 +2132,16 @@ async def main(args):
|
|
|
1695
2132
|
if "orchestrator" in config:
|
|
1696
2133
|
kwargs["orchestrator"] = config["orchestrator"]
|
|
1697
2134
|
|
|
2135
|
+
# Save execution metadata for debugging and reconstruction
|
|
2136
|
+
if args.question:
|
|
2137
|
+
# For single question mode, save metadata now (use original config before .massgen/ relocation)
|
|
2138
|
+
save_execution_metadata(
|
|
2139
|
+
query=args.question,
|
|
2140
|
+
config_path=str(resolved_path) if args.config and "resolved_path" in locals() else None,
|
|
2141
|
+
config_content=original_config_for_metadata,
|
|
2142
|
+
cli_args=vars(args),
|
|
2143
|
+
)
|
|
2144
|
+
|
|
1698
2145
|
# Run mode based on whether question was provided
|
|
1699
2146
|
try:
|
|
1700
2147
|
if args.question:
|
|
@@ -1734,23 +2181,27 @@ def cli_main():
|
|
|
1734
2181
|
epilog="""
|
|
1735
2182
|
Examples:
|
|
1736
2183
|
# Use configuration file
|
|
1737
|
-
|
|
2184
|
+
massgen --config config.yaml "What is machine learning?"
|
|
1738
2185
|
|
|
1739
2186
|
# Quick single agent setup
|
|
1740
|
-
|
|
1741
|
-
|
|
2187
|
+
massgen --backend openai --model gpt-4o-mini "Explain quantum computing"
|
|
2188
|
+
massgen --backend claude --model claude-sonnet-4-20250514 "Analyze this data"
|
|
1742
2189
|
|
|
1743
2190
|
# Use ChatCompletion backend with custom base URL
|
|
1744
|
-
|
|
2191
|
+
massgen --backend chatcompletion --model gpt-oss-120b --base-url https://api.cerebras.ai/v1/chat/completions "What is 2+2?"
|
|
1745
2192
|
|
|
1746
2193
|
# Interactive mode
|
|
1747
|
-
|
|
2194
|
+
massgen --config config.yaml
|
|
2195
|
+
massgen # Uses default config if available
|
|
1748
2196
|
|
|
1749
2197
|
# Timeout control examples
|
|
1750
|
-
|
|
2198
|
+
massgen --config config.yaml --orchestrator-timeout 600 "Complex task"
|
|
1751
2199
|
|
|
1752
|
-
#
|
|
1753
|
-
|
|
2200
|
+
# Configuration management
|
|
2201
|
+
massgen --init # Create new configuration interactively
|
|
2202
|
+
massgen --select # Choose from available configurations
|
|
2203
|
+
massgen --setup # Set up API keys
|
|
2204
|
+
massgen --list-examples # View example configurations
|
|
1754
2205
|
|
|
1755
2206
|
Environment Variables:
|
|
1756
2207
|
OPENAI_API_KEY - Required for OpenAI backend
|
|
@@ -1782,6 +2233,11 @@ Environment Variables:
|
|
|
1782
2233
|
# Configuration options
|
|
1783
2234
|
config_group = parser.add_mutually_exclusive_group()
|
|
1784
2235
|
config_group.add_argument("--config", type=str, help="Path to YAML/JSON configuration file or @examples/NAME")
|
|
2236
|
+
config_group.add_argument(
|
|
2237
|
+
"--select",
|
|
2238
|
+
action="store_true",
|
|
2239
|
+
help="Interactively select from available configurations",
|
|
2240
|
+
)
|
|
1785
2241
|
config_group.add_argument(
|
|
1786
2242
|
"--backend",
|
|
1787
2243
|
type=str,
|
|
@@ -1825,7 +2281,7 @@ Environment Variables:
|
|
|
1825
2281
|
help="Launch interactive configuration builder to create config file",
|
|
1826
2282
|
)
|
|
1827
2283
|
parser.add_argument(
|
|
1828
|
-
"--setup
|
|
2284
|
+
"--setup",
|
|
1829
2285
|
action="store_true",
|
|
1830
2286
|
help="Launch interactive API key setup wizard to configure credentials",
|
|
1831
2287
|
)
|
|
@@ -1888,7 +2344,7 @@ Environment Variables:
|
|
|
1888
2344
|
return
|
|
1889
2345
|
|
|
1890
2346
|
# Launch interactive API key setup if requested
|
|
1891
|
-
if args.
|
|
2347
|
+
if args.setup:
|
|
1892
2348
|
from .config_builder import ConfigBuilder
|
|
1893
2349
|
|
|
1894
2350
|
builder = ConfigBuilder()
|
|
@@ -1899,9 +2355,20 @@ Environment Variables:
|
|
|
1899
2355
|
print(f"{BRIGHT_CYAN}💡 You can now use MassGen with these providers{RESET}\n")
|
|
1900
2356
|
else:
|
|
1901
2357
|
print(f"\n{BRIGHT_YELLOW}⚠️ No API keys configured{RESET}")
|
|
1902
|
-
print(f"{BRIGHT_CYAN}💡 You can run 'massgen --setup
|
|
2358
|
+
print(f"{BRIGHT_CYAN}💡 You can run 'massgen --setup' anytime to set them up{RESET}\n")
|
|
1903
2359
|
return
|
|
1904
2360
|
|
|
2361
|
+
# Launch interactive config selector if requested
|
|
2362
|
+
if args.select:
|
|
2363
|
+
selected_config = interactive_config_selector()
|
|
2364
|
+
if selected_config:
|
|
2365
|
+
# Update args to use the selected config
|
|
2366
|
+
args.config = selected_config
|
|
2367
|
+
# Continue to main() with the selected config
|
|
2368
|
+
else:
|
|
2369
|
+
# User cancelled selection
|
|
2370
|
+
return
|
|
2371
|
+
|
|
1905
2372
|
# Launch interactive config builder if requested
|
|
1906
2373
|
if args.init:
|
|
1907
2374
|
from .config_builder import ConfigBuilder
|
|
@@ -1918,7 +2385,7 @@ Environment Variables:
|
|
|
1918
2385
|
elif filepath:
|
|
1919
2386
|
# Config created but user chose not to run
|
|
1920
2387
|
print(f"\n✅ Configuration saved to: {filepath}")
|
|
1921
|
-
print(f'Run with:
|
|
2388
|
+
print(f'Run with: massgen --config {filepath} "Your question"')
|
|
1922
2389
|
return
|
|
1923
2390
|
else:
|
|
1924
2391
|
# User cancelled
|