massgen 0.0.3__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 +94 -0
- massgen/agent_config.py +507 -0
- massgen/backend/CLAUDE_API_RESEARCH.md +266 -0
- massgen/backend/Function calling openai responses.md +1161 -0
- massgen/backend/GEMINI_API_DOCUMENTATION.md +410 -0
- massgen/backend/OPENAI_RESPONSES_API_FORMAT.md +65 -0
- massgen/backend/__init__.py +25 -0
- massgen/backend/base.py +180 -0
- massgen/backend/chat_completions.py +228 -0
- massgen/backend/claude.py +661 -0
- massgen/backend/gemini.py +652 -0
- massgen/backend/grok.py +187 -0
- massgen/backend/response.py +397 -0
- massgen/chat_agent.py +440 -0
- massgen/cli.py +686 -0
- massgen/configs/README.md +293 -0
- massgen/configs/creative_team.yaml +53 -0
- massgen/configs/gemini_4o_claude.yaml +31 -0
- massgen/configs/news_analysis.yaml +51 -0
- massgen/configs/research_team.yaml +51 -0
- massgen/configs/single_agent.yaml +18 -0
- massgen/configs/single_flash2.5.yaml +44 -0
- massgen/configs/technical_analysis.yaml +51 -0
- massgen/configs/three_agents_default.yaml +31 -0
- massgen/configs/travel_planning.yaml +51 -0
- massgen/configs/two_agents.yaml +39 -0
- massgen/frontend/__init__.py +20 -0
- massgen/frontend/coordination_ui.py +945 -0
- massgen/frontend/displays/__init__.py +24 -0
- massgen/frontend/displays/base_display.py +83 -0
- massgen/frontend/displays/rich_terminal_display.py +3497 -0
- massgen/frontend/displays/simple_display.py +93 -0
- massgen/frontend/displays/terminal_display.py +381 -0
- massgen/frontend/logging/__init__.py +9 -0
- massgen/frontend/logging/realtime_logger.py +197 -0
- massgen/message_templates.py +431 -0
- massgen/orchestrator.py +1222 -0
- massgen/tests/__init__.py +10 -0
- massgen/tests/multi_turn_conversation_design.md +214 -0
- massgen/tests/multiturn_llm_input_analysis.md +189 -0
- massgen/tests/test_case_studies.md +113 -0
- massgen/tests/test_claude_backend.py +310 -0
- massgen/tests/test_grok_backend.py +160 -0
- massgen/tests/test_message_context_building.py +293 -0
- massgen/tests/test_rich_terminal_display.py +378 -0
- massgen/tests/test_v3_3agents.py +117 -0
- massgen/tests/test_v3_simple.py +216 -0
- massgen/tests/test_v3_three_agents.py +272 -0
- massgen/tests/test_v3_two_agents.py +176 -0
- massgen/utils.py +79 -0
- massgen/v1/README.md +330 -0
- massgen/v1/__init__.py +91 -0
- massgen/v1/agent.py +605 -0
- massgen/v1/agents.py +330 -0
- massgen/v1/backends/gemini.py +584 -0
- massgen/v1/backends/grok.py +410 -0
- massgen/v1/backends/oai.py +571 -0
- massgen/v1/cli.py +351 -0
- massgen/v1/config.py +169 -0
- massgen/v1/examples/fast-4o-mini-config.yaml +44 -0
- massgen/v1/examples/fast_config.yaml +44 -0
- massgen/v1/examples/production.yaml +70 -0
- massgen/v1/examples/single_agent.yaml +39 -0
- massgen/v1/logging.py +974 -0
- massgen/v1/main.py +368 -0
- massgen/v1/orchestrator.py +1138 -0
- massgen/v1/streaming_display.py +1190 -0
- massgen/v1/tools.py +160 -0
- massgen/v1/types.py +245 -0
- massgen/v1/utils.py +199 -0
- massgen-0.0.3.dist-info/METADATA +568 -0
- massgen-0.0.3.dist-info/RECORD +76 -0
- massgen-0.0.3.dist-info/WHEEL +5 -0
- massgen-0.0.3.dist-info/entry_points.txt +2 -0
- massgen-0.0.3.dist-info/licenses/LICENSE +204 -0
- massgen-0.0.3.dist-info/top_level.txt +1 -0
massgen/v1/main.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MassGen (Multi-Agent Scaling System) - Programmatic Interface
|
|
4
|
+
|
|
5
|
+
This module provides programmatic interfaces for running the MassGen system.
|
|
6
|
+
For command-line usage, use: uv run python -m massgen.v1.cli
|
|
7
|
+
|
|
8
|
+
Programmatic usage examples:
|
|
9
|
+
# Using YAML configuration
|
|
10
|
+
from mass import run_mass_with_config, load_config_from_yaml
|
|
11
|
+
config = load_config_from_yaml("config.yaml")
|
|
12
|
+
result = run_mass_with_config("Your question here", config)
|
|
13
|
+
|
|
14
|
+
# Using simple model list
|
|
15
|
+
from mass import run_mass_agents
|
|
16
|
+
result = run_mass_agents("What is 2+2?", ["gpt-4o", "gemini-2.5-flash"])
|
|
17
|
+
|
|
18
|
+
# Using configuration objects
|
|
19
|
+
from mass import MassSystem, create_config_from_models
|
|
20
|
+
config = create_config_from_models(["gpt-4o", "grok-3"])
|
|
21
|
+
system = MassSystem(config)
|
|
22
|
+
result = system.run("Complex question here")
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import sys
|
|
26
|
+
import os
|
|
27
|
+
import logging
|
|
28
|
+
import time
|
|
29
|
+
import json
|
|
30
|
+
from typing import List, Dict, Any, Optional, Union
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
# Add current directory to path for imports
|
|
34
|
+
sys.path.append(os.path.dirname(__file__))
|
|
35
|
+
|
|
36
|
+
from .types import TaskInput, MassConfig, ModelConfig, AgentConfig
|
|
37
|
+
from .config import create_config_from_models
|
|
38
|
+
from .orchestrator import MassOrchestrator
|
|
39
|
+
from .agents import create_agent
|
|
40
|
+
from .streaming_display import create_streaming_display
|
|
41
|
+
from .logging import MassLogManager
|
|
42
|
+
|
|
43
|
+
# Initialize logging
|
|
44
|
+
logging.basicConfig(
|
|
45
|
+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
|
46
|
+
)
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _run_single_agent_simple(question: str, config: MassConfig) -> Dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Simple single-agent processing that bypasses the multi-agent orchestration system.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
question: The question to solve
|
|
56
|
+
config: MassConfig object with exactly one agent
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dict containing the answer and detailed results
|
|
60
|
+
"""
|
|
61
|
+
start_time = time.time()
|
|
62
|
+
agent_config = config.agents[0]
|
|
63
|
+
|
|
64
|
+
logger.info(f"🤖 Running single agent mode with {agent_config.model_config.model}")
|
|
65
|
+
logger.info(f" Question: {question}")
|
|
66
|
+
|
|
67
|
+
# Create log manager for single agent mode to ensure result.json is saved
|
|
68
|
+
log_manager = MassLogManager(
|
|
69
|
+
log_dir=config.logging.log_dir,
|
|
70
|
+
session_id=config.logging.session_id,
|
|
71
|
+
non_blocking=config.logging.non_blocking,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
# Create the single agent without orchestrator (None)
|
|
76
|
+
agent = create_agent(
|
|
77
|
+
agent_type=agent_config.agent_type,
|
|
78
|
+
agent_id=agent_config.agent_id,
|
|
79
|
+
orchestrator=None, # No orchestrator needed for single agent
|
|
80
|
+
model_config=agent_config.model_config,
|
|
81
|
+
stream_callback=None, # Simple mode without streaming
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Create simple conversation format
|
|
85
|
+
messages = [
|
|
86
|
+
{
|
|
87
|
+
"role": "system",
|
|
88
|
+
"content": f"You are an expert agent equipped with tools to solve complex tasks. Please provide a comprehensive answer to the user's question.",
|
|
89
|
+
},
|
|
90
|
+
{"role": "user", "content": question},
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
# Get available tools from agent configuration
|
|
94
|
+
tools = (
|
|
95
|
+
agent_config.model_config.tools if agent_config.model_config.tools else []
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Call process_message directly
|
|
99
|
+
result = agent.process_message(messages=messages, tools=tools)
|
|
100
|
+
|
|
101
|
+
# Calculate duration
|
|
102
|
+
session_duration = time.time() - start_time
|
|
103
|
+
|
|
104
|
+
# Format response to match multi-agent system format
|
|
105
|
+
response = {
|
|
106
|
+
"answer": result.text if result.text else "No response generated",
|
|
107
|
+
"consensus_reached": True, # Trivially true for single agent
|
|
108
|
+
"representative_agent_id": agent_config.agent_id,
|
|
109
|
+
"session_duration": session_duration,
|
|
110
|
+
"summary": {
|
|
111
|
+
"total_agents": 1,
|
|
112
|
+
"failed_agents": 0,
|
|
113
|
+
"total_votes": 1,
|
|
114
|
+
"final_vote_distribution": {
|
|
115
|
+
agent_config.agent_id: 1
|
|
116
|
+
}, # Single agent votes for itself
|
|
117
|
+
},
|
|
118
|
+
"model_used": agent_config.model_config.model,
|
|
119
|
+
"citations": result.citations if hasattr(result, "citations") else [],
|
|
120
|
+
"code": result.code if hasattr(result, "code") else [],
|
|
121
|
+
"single_agent_mode": True,
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Save result to result.json in the session directory
|
|
125
|
+
if log_manager and not log_manager.non_blocking:
|
|
126
|
+
try:
|
|
127
|
+
result_file = log_manager.session_dir / "result.json"
|
|
128
|
+
with open(result_file, "w", encoding="utf-8") as f:
|
|
129
|
+
json.dump(response, f, indent=2, ensure_ascii=False, default=str)
|
|
130
|
+
logger.info(f"💾 Single agent result saved to {result_file}")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.warning(f"⚠️ Failed to save result.json: {e}")
|
|
133
|
+
|
|
134
|
+
logger.info(f"✅ Single agent completed in {session_duration:.1f}s")
|
|
135
|
+
return response
|
|
136
|
+
|
|
137
|
+
except Exception as e:
|
|
138
|
+
session_duration = time.time() - start_time
|
|
139
|
+
logger.error(f"❌ Single agent failed: {e}")
|
|
140
|
+
|
|
141
|
+
# Return error response in same format
|
|
142
|
+
error_response = {
|
|
143
|
+
"answer": f"Error in single agent processing: {str(e)}",
|
|
144
|
+
"consensus_reached": False,
|
|
145
|
+
"representative_agent_id": None,
|
|
146
|
+
"session_duration": session_duration,
|
|
147
|
+
"summary": {
|
|
148
|
+
"total_agents": 1,
|
|
149
|
+
"failed_agents": 1,
|
|
150
|
+
"total_votes": 0,
|
|
151
|
+
"final_vote_distribution": {},
|
|
152
|
+
},
|
|
153
|
+
"model_used": agent_config.model_config.model,
|
|
154
|
+
"citations": [],
|
|
155
|
+
"code": [],
|
|
156
|
+
"single_agent_mode": True,
|
|
157
|
+
"error": str(e),
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Save error result to result.json in the session directory
|
|
161
|
+
if log_manager and not log_manager.non_blocking:
|
|
162
|
+
try:
|
|
163
|
+
result_file = log_manager.session_dir / "result.json"
|
|
164
|
+
with open(result_file, "w", encoding="utf-8") as f:
|
|
165
|
+
json.dump(
|
|
166
|
+
error_response, f, indent=2, ensure_ascii=False, default=str
|
|
167
|
+
)
|
|
168
|
+
logger.info(f"💾 Single agent error result saved to {result_file}")
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.warning(f"⚠️ Failed to save result.json: {e}")
|
|
171
|
+
|
|
172
|
+
return error_response
|
|
173
|
+
finally:
|
|
174
|
+
# Cleanup log manager
|
|
175
|
+
if log_manager:
|
|
176
|
+
try:
|
|
177
|
+
log_manager.cleanup()
|
|
178
|
+
except Exception as e:
|
|
179
|
+
logger.warning(f"⚠️ Error cleaning up log manager: {e}")
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def run_mass_with_config(question: str, config: MassConfig) -> Dict[str, Any]:
|
|
183
|
+
"""
|
|
184
|
+
Run MassGen system with a complete configuration object.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
question: The question to solve
|
|
188
|
+
config: Complete MassConfig object
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Dict containing the answer and detailed results
|
|
192
|
+
"""
|
|
193
|
+
# Validate configuration
|
|
194
|
+
config.validate()
|
|
195
|
+
|
|
196
|
+
# Check for single agent case
|
|
197
|
+
if len(config.agents) == 1:
|
|
198
|
+
logger.info("🔄 Single agent detected - using simple processing mode")
|
|
199
|
+
return _run_single_agent_simple(question, config)
|
|
200
|
+
|
|
201
|
+
# Continue with multi-agent orchestration for multiple agents
|
|
202
|
+
logger.info("🔄 Multiple agents detected - using multi-agent orchestration")
|
|
203
|
+
|
|
204
|
+
# Create task input
|
|
205
|
+
task = TaskInput(question=question)
|
|
206
|
+
|
|
207
|
+
# Create log manager first to get answers directory
|
|
208
|
+
log_manager = MassLogManager(
|
|
209
|
+
log_dir=config.logging.log_dir,
|
|
210
|
+
session_id=config.logging.session_id,
|
|
211
|
+
non_blocking=config.logging.non_blocking,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Create streaming display with answers directory from log manager
|
|
215
|
+
streaming_orchestrator = None
|
|
216
|
+
if config.streaming_display.display_enabled:
|
|
217
|
+
streaming_orchestrator = create_streaming_display(
|
|
218
|
+
display_enabled=config.streaming_display.display_enabled,
|
|
219
|
+
max_lines=config.streaming_display.max_lines,
|
|
220
|
+
save_logs=config.streaming_display.save_logs,
|
|
221
|
+
stream_callback=config.streaming_display.stream_callback,
|
|
222
|
+
answers_dir=(
|
|
223
|
+
str(log_manager.answers_dir) if not log_manager.non_blocking else None
|
|
224
|
+
),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Create orchestrator with full configuration
|
|
228
|
+
orchestrator = MassOrchestrator(
|
|
229
|
+
max_duration=config.orchestrator.max_duration,
|
|
230
|
+
consensus_threshold=config.orchestrator.consensus_threshold,
|
|
231
|
+
max_debate_rounds=config.orchestrator.max_debate_rounds,
|
|
232
|
+
status_check_interval=config.orchestrator.status_check_interval,
|
|
233
|
+
thread_pool_timeout=config.orchestrator.thread_pool_timeout,
|
|
234
|
+
streaming_orchestrator=streaming_orchestrator,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Set log manager
|
|
238
|
+
orchestrator.log_manager = log_manager
|
|
239
|
+
|
|
240
|
+
# Register agents
|
|
241
|
+
for agent_config in config.agents:
|
|
242
|
+
# Create stream callback that connects agent to streaming display
|
|
243
|
+
stream_callback = None
|
|
244
|
+
if streaming_orchestrator:
|
|
245
|
+
# Create a proper closure that captures the agent_id
|
|
246
|
+
def create_stream_callback(agent_id):
|
|
247
|
+
def callback(content):
|
|
248
|
+
streaming_orchestrator.stream_output(agent_id, content)
|
|
249
|
+
|
|
250
|
+
return callback
|
|
251
|
+
|
|
252
|
+
stream_callback = create_stream_callback(agent_config.agent_id)
|
|
253
|
+
|
|
254
|
+
agent = create_agent(
|
|
255
|
+
agent_type=agent_config.agent_type,
|
|
256
|
+
agent_id=agent_config.agent_id,
|
|
257
|
+
orchestrator=orchestrator,
|
|
258
|
+
model_config=agent_config.model_config,
|
|
259
|
+
stream_callback=stream_callback,
|
|
260
|
+
)
|
|
261
|
+
orchestrator.register_agent(agent)
|
|
262
|
+
|
|
263
|
+
logger.info(f"🚀 Starting MassGen with {len(config.agents)} agents")
|
|
264
|
+
logger.info(f" Question: {question}")
|
|
265
|
+
logger.info(f" Models: {[agent.model_config.model for agent in config.agents]}")
|
|
266
|
+
logger.info(f" Max duration: {config.orchestrator.max_duration}s")
|
|
267
|
+
logger.info(f" Consensus threshold: {config.orchestrator.consensus_threshold}")
|
|
268
|
+
|
|
269
|
+
# Start the task and get results
|
|
270
|
+
try:
|
|
271
|
+
result = orchestrator.start_task(task)
|
|
272
|
+
logger.info("✅ MassGen completed successfully")
|
|
273
|
+
return result
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
logger.error(f"❌ MassGen failed: {e}")
|
|
277
|
+
raise
|
|
278
|
+
finally:
|
|
279
|
+
# Cleanup
|
|
280
|
+
orchestrator.cleanup()
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class MassSystem:
|
|
284
|
+
"""
|
|
285
|
+
Enhanced MassGen system interface with configuration support.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
def __init__(self, config: MassConfig):
|
|
289
|
+
"""
|
|
290
|
+
Initialize the MassGen system.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
config: MassConfig object with complete configuration.
|
|
294
|
+
"""
|
|
295
|
+
self.config = config
|
|
296
|
+
|
|
297
|
+
def run(self, question: str) -> Dict[str, Any]:
|
|
298
|
+
"""
|
|
299
|
+
Run MassGen system on a question using the configured setup.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
question: The question to solve
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Dict containing the answer and detailed results
|
|
306
|
+
"""
|
|
307
|
+
return run_mass_with_config(question, self.config)
|
|
308
|
+
|
|
309
|
+
def update_config(self, **kwargs) -> None:
|
|
310
|
+
"""
|
|
311
|
+
Update configuration parameters.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
**kwargs: Configuration parameters to update
|
|
315
|
+
"""
|
|
316
|
+
# Update orchestrator config
|
|
317
|
+
if "max_duration" in kwargs:
|
|
318
|
+
self.config.orchestrator.max_duration = kwargs["max_duration"]
|
|
319
|
+
if "consensus_threshold" in kwargs:
|
|
320
|
+
self.config.orchestrator.consensus_threshold = kwargs["consensus_threshold"]
|
|
321
|
+
if "max_debate_rounds" in kwargs:
|
|
322
|
+
self.config.orchestrator.max_debate_rounds = kwargs["max_debate_rounds"]
|
|
323
|
+
|
|
324
|
+
# Validate updated configuration
|
|
325
|
+
self.config.validate()
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def run_mass_agents(
|
|
329
|
+
question: str,
|
|
330
|
+
models: List[str],
|
|
331
|
+
max_duration: int = 600,
|
|
332
|
+
consensus_threshold: float = 0.0,
|
|
333
|
+
streaming_display: bool = True,
|
|
334
|
+
**kwargs,
|
|
335
|
+
) -> Dict[str, Any]:
|
|
336
|
+
"""
|
|
337
|
+
Simple function to run MassGen agents on a question (backward compatibility).
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
question: The question to solve
|
|
341
|
+
models: List of model names (e.g., ["gpt-4o", "gemini-2.5-flash"])
|
|
342
|
+
max_duration: Maximum duration in seconds
|
|
343
|
+
consensus_threshold: Consensus threshold
|
|
344
|
+
streaming_display: Whether to show real-time progress
|
|
345
|
+
**kwargs: Additional configuration parameters
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Dict containing the answer and detailed results
|
|
349
|
+
"""
|
|
350
|
+
# Create configuration from models
|
|
351
|
+
config = create_config_from_models(
|
|
352
|
+
models=models,
|
|
353
|
+
orchestrator_config={
|
|
354
|
+
"max_duration": max_duration,
|
|
355
|
+
"consensus_threshold": consensus_threshold,
|
|
356
|
+
**{
|
|
357
|
+
k: v
|
|
358
|
+
for k, v in kwargs.items()
|
|
359
|
+
if k in ["max_debate_rounds", "status_check_interval"]
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
streaming_config={
|
|
363
|
+
"display_enabled": streaming_display,
|
|
364
|
+
**{k: v for k, v in kwargs.items() if k in ["max_lines", "save_logs"]},
|
|
365
|
+
},
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
return run_mass_with_config(question, config)
|