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.

Files changed (76) hide show
  1. massgen/__init__.py +94 -0
  2. massgen/agent_config.py +507 -0
  3. massgen/backend/CLAUDE_API_RESEARCH.md +266 -0
  4. massgen/backend/Function calling openai responses.md +1161 -0
  5. massgen/backend/GEMINI_API_DOCUMENTATION.md +410 -0
  6. massgen/backend/OPENAI_RESPONSES_API_FORMAT.md +65 -0
  7. massgen/backend/__init__.py +25 -0
  8. massgen/backend/base.py +180 -0
  9. massgen/backend/chat_completions.py +228 -0
  10. massgen/backend/claude.py +661 -0
  11. massgen/backend/gemini.py +652 -0
  12. massgen/backend/grok.py +187 -0
  13. massgen/backend/response.py +397 -0
  14. massgen/chat_agent.py +440 -0
  15. massgen/cli.py +686 -0
  16. massgen/configs/README.md +293 -0
  17. massgen/configs/creative_team.yaml +53 -0
  18. massgen/configs/gemini_4o_claude.yaml +31 -0
  19. massgen/configs/news_analysis.yaml +51 -0
  20. massgen/configs/research_team.yaml +51 -0
  21. massgen/configs/single_agent.yaml +18 -0
  22. massgen/configs/single_flash2.5.yaml +44 -0
  23. massgen/configs/technical_analysis.yaml +51 -0
  24. massgen/configs/three_agents_default.yaml +31 -0
  25. massgen/configs/travel_planning.yaml +51 -0
  26. massgen/configs/two_agents.yaml +39 -0
  27. massgen/frontend/__init__.py +20 -0
  28. massgen/frontend/coordination_ui.py +945 -0
  29. massgen/frontend/displays/__init__.py +24 -0
  30. massgen/frontend/displays/base_display.py +83 -0
  31. massgen/frontend/displays/rich_terminal_display.py +3497 -0
  32. massgen/frontend/displays/simple_display.py +93 -0
  33. massgen/frontend/displays/terminal_display.py +381 -0
  34. massgen/frontend/logging/__init__.py +9 -0
  35. massgen/frontend/logging/realtime_logger.py +197 -0
  36. massgen/message_templates.py +431 -0
  37. massgen/orchestrator.py +1222 -0
  38. massgen/tests/__init__.py +10 -0
  39. massgen/tests/multi_turn_conversation_design.md +214 -0
  40. massgen/tests/multiturn_llm_input_analysis.md +189 -0
  41. massgen/tests/test_case_studies.md +113 -0
  42. massgen/tests/test_claude_backend.py +310 -0
  43. massgen/tests/test_grok_backend.py +160 -0
  44. massgen/tests/test_message_context_building.py +293 -0
  45. massgen/tests/test_rich_terminal_display.py +378 -0
  46. massgen/tests/test_v3_3agents.py +117 -0
  47. massgen/tests/test_v3_simple.py +216 -0
  48. massgen/tests/test_v3_three_agents.py +272 -0
  49. massgen/tests/test_v3_two_agents.py +176 -0
  50. massgen/utils.py +79 -0
  51. massgen/v1/README.md +330 -0
  52. massgen/v1/__init__.py +91 -0
  53. massgen/v1/agent.py +605 -0
  54. massgen/v1/agents.py +330 -0
  55. massgen/v1/backends/gemini.py +584 -0
  56. massgen/v1/backends/grok.py +410 -0
  57. massgen/v1/backends/oai.py +571 -0
  58. massgen/v1/cli.py +351 -0
  59. massgen/v1/config.py +169 -0
  60. massgen/v1/examples/fast-4o-mini-config.yaml +44 -0
  61. massgen/v1/examples/fast_config.yaml +44 -0
  62. massgen/v1/examples/production.yaml +70 -0
  63. massgen/v1/examples/single_agent.yaml +39 -0
  64. massgen/v1/logging.py +974 -0
  65. massgen/v1/main.py +368 -0
  66. massgen/v1/orchestrator.py +1138 -0
  67. massgen/v1/streaming_display.py +1190 -0
  68. massgen/v1/tools.py +160 -0
  69. massgen/v1/types.py +245 -0
  70. massgen/v1/utils.py +199 -0
  71. massgen-0.0.3.dist-info/METADATA +568 -0
  72. massgen-0.0.3.dist-info/RECORD +76 -0
  73. massgen-0.0.3.dist-info/WHEEL +5 -0
  74. massgen-0.0.3.dist-info/entry_points.txt +2 -0
  75. massgen-0.0.3.dist-info/licenses/LICENSE +204 -0
  76. 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)