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
@@ -0,0 +1,431 @@
1
+ """
2
+ Message templates for MassGen framework following input_cases_reference.md
3
+ Implements proven binary decision framework that eliminates perfectionism loops.
4
+ """
5
+
6
+ from typing import Dict, Any, Optional, List
7
+
8
+
9
+ class MessageTemplates:
10
+ """Message templates implementing the proven MassGen approach."""
11
+
12
+ def __init__(self, **template_overrides):
13
+ """Initialize with optional template overrides."""
14
+ self._template_overrides = template_overrides
15
+
16
+ # =============================================================================
17
+ # SYSTEM MESSAGE TEMPLATES
18
+ # =============================================================================
19
+
20
+ def evaluation_system_message(self) -> str:
21
+ """Standard evaluation system message for all cases."""
22
+ if "evaluation_system_message" in self._template_overrides:
23
+ return str(self._template_overrides["evaluation_system_message"])
24
+
25
+ import time
26
+
27
+ # return f"""You are evaluating answers from multiple agents for final response to a message.
28
+
29
+ # For every aspect, claim, reasoning steps in the CURRENT ANSWERS, verify correctness, factual accuracy, and completeness using your expertise, reasoning, and available tools.
30
+
31
+ # If the CURRENT ANSWERS fully address the ORIGINAL MESSAGE, use the `vote` tool to record your vote and skip the `new_answer` tool.
32
+
33
+ # If the CURRENT ANSWERS are incomplete, incorrect, or not fully address the ORIGINAL MESSAGE, conduct any necessary reasoning or research. Then, use the `new_answer` tool to submit a new response.
34
+
35
+ # Your new answer must be self-contained, process-complete, well-sourced, and compelling—ready to serve as the final reply.
36
+
37
+ # **Important**: Be sure to actually call the `new_answer` tool to submit your new answer (use native tool call format).
38
+
39
+ # *Note*: The CURRENT TIME is **{time.strftime("%Y-%m-%d %H:%M:%S")}**.
40
+ # For any time-sensitive requests, use the search tool (if available) rather than relying on prior knowledge."""
41
+
42
+ return f"""You are evaluating answers from multiple agents for final response to a message. Does the best CURRENT ANSWER address the ORIGINAL MESSAGE?
43
+
44
+ If YES, use the `vote` tool to record your vote and skip the `new_answer` tool.
45
+ Otherwise, do additional work first, then use the `new_answer` tool to record a better answer to the ORIGINAL MESSAGE. Make sure you actually call `vote` or `new_answer` (in tool call format).
46
+
47
+ *Note*: The CURRENT TIME is **{time.strftime("%Y-%m-%d %H:%M:%S")}**.
48
+ """
49
+
50
+ # =============================================================================
51
+ # USER MESSAGE TEMPLATES
52
+ # =============================================================================
53
+
54
+ def format_original_message(self, task: str) -> str:
55
+ """Format the original message section."""
56
+ if "format_original_message" in self._template_overrides:
57
+ override = self._template_overrides["format_original_message"]
58
+ if callable(override):
59
+ return override(task)
60
+ return str(override).format(task=task)
61
+
62
+ return f"<ORIGINAL MESSAGE> {task} <END OF ORIGINAL MESSAGE>"
63
+
64
+ def format_conversation_history(
65
+ self, conversation_history: List[Dict[str, str]]
66
+ ) -> str:
67
+ """Format conversation history for agent context."""
68
+ if "format_conversation_history" in self._template_overrides:
69
+ override = self._template_overrides["format_conversation_history"]
70
+ if callable(override):
71
+ return override(conversation_history)
72
+ return str(override)
73
+
74
+ if not conversation_history:
75
+ return ""
76
+
77
+ lines = ["<CONVERSATION_HISTORY>"]
78
+ for message in conversation_history:
79
+ role = message.get("role", "unknown")
80
+ content = message.get("content", "")
81
+ if role == "user":
82
+ lines.append(f"User: {content}")
83
+ elif role == "assistant":
84
+ lines.append(f"Assistant: {content}")
85
+ elif role == "system":
86
+ # Skip system messages in history display
87
+ continue
88
+ lines.append("<END OF CONVERSATION_HISTORY>")
89
+ return "\n".join(lines)
90
+
91
+ def system_message_with_context(
92
+ self, conversation_history: Optional[List[Dict[str, str]]] = None
93
+ ) -> str:
94
+ """Evaluation system message with conversation context awareness."""
95
+ if "system_message_with_context" in self._template_overrides:
96
+ override = self._template_overrides["system_message_with_context"]
97
+ if callable(override):
98
+ return override(conversation_history)
99
+ return str(override)
100
+
101
+ base_message = self.evaluation_system_message()
102
+
103
+ if conversation_history and len(conversation_history) > 0:
104
+ context_note = """
105
+
106
+ IMPORTANT: You are responding to the latest message in an ongoing conversation. Consider the full conversation context when evaluating answers and providing your response."""
107
+ return base_message + context_note
108
+
109
+ return base_message
110
+
111
+ def format_current_answers_empty(self) -> str:
112
+ """Format current answers section when no answers exist (Case 1)."""
113
+ if "format_current_answers_empty" in self._template_overrides:
114
+ return str(self._template_overrides["format_current_answers_empty"])
115
+
116
+ return """<CURRENT ANSWERS from the agents>
117
+ (no answers available yet)
118
+ <END OF CURRENT ANSWERS>"""
119
+
120
+ def format_current_answers_with_summaries(
121
+ self, agent_summaries: Dict[str, str]
122
+ ) -> str:
123
+ """Format current answers section with agent summaries (Case 2) using anonymous agent IDs."""
124
+ if "format_current_answers_with_summaries" in self._template_overrides:
125
+ override = self._template_overrides["format_current_answers_with_summaries"]
126
+ if callable(override):
127
+ return override(agent_summaries)
128
+
129
+ lines = ["<CURRENT ANSWERS from the agents>"]
130
+
131
+ # Create anonymous mapping: agent1, agent2, etc.
132
+ agent_mapping = {}
133
+ for i, agent_id in enumerate(sorted(agent_summaries.keys()), 1):
134
+ agent_mapping[agent_id] = f"agent{i}"
135
+
136
+ for agent_id, summary in agent_summaries.items():
137
+ anon_id = agent_mapping[agent_id]
138
+ lines.append(f"<{anon_id}> {summary} <end of {anon_id}>")
139
+
140
+ lines.append("<END OF CURRENT ANSWERS>")
141
+ return "\n".join(lines)
142
+
143
+ def enforcement_message(self) -> str:
144
+ """Enforcement message for Case 3 (non-workflow responses)."""
145
+ if "enforcement_message" in self._template_overrides:
146
+ return str(self._template_overrides["enforcement_message"])
147
+
148
+ return "Finish your work above by making a tool call of `vote` or `new_answer`. Make sure you actually call the tool."
149
+
150
+ def tool_error_message(self, error_msg: str) -> Dict[str, str]:
151
+ """Create a tool role message for tool usage errors."""
152
+ return {"role": "tool", "content": error_msg}
153
+
154
+ def enforcement_user_message(self) -> Dict[str, str]:
155
+ """Create a user role message for enforcement."""
156
+ return {"role": "user", "content": self.enforcement_message()}
157
+
158
+ # =============================================================================
159
+ # TOOL DEFINITIONS
160
+ # =============================================================================
161
+
162
+ def get_new_answer_tool(self) -> Dict[str, Any]:
163
+ """Get new_answer tool definition."""
164
+ if "new_answer_tool" in self._template_overrides:
165
+ return self._template_overrides["new_answer_tool"]
166
+
167
+ return {
168
+ "type": "function",
169
+ "function": {
170
+ "name": "new_answer",
171
+ "description": "Provide an improved answer to the ORIGINAL MESSAGE",
172
+ "parameters": {
173
+ "type": "object",
174
+ "properties": {
175
+ "content": {
176
+ "type": "string",
177
+ "description": "Your improved answer. If any builtin tools like search or code execution were used, include how they are used here.",
178
+ }
179
+ },
180
+ "required": ["content"],
181
+ },
182
+ },
183
+ }
184
+
185
+ def get_vote_tool(
186
+ self, valid_agent_ids: Optional[List[str]] = None
187
+ ) -> Dict[str, Any]:
188
+ """Get vote tool definition with anonymous agent IDs."""
189
+ if "vote_tool" in self._template_overrides:
190
+ override = self._template_overrides["vote_tool"]
191
+ if callable(override):
192
+ return override(valid_agent_ids)
193
+ return override
194
+
195
+ tool_def = {
196
+ "type": "function",
197
+ "function": {
198
+ "name": "vote",
199
+ "description": "Vote for the best agent to present final answer",
200
+ "parameters": {
201
+ "type": "object",
202
+ "properties": {
203
+ "agent_id": {
204
+ "type": "string",
205
+ "description": "Anonymous agent ID to vote for (e.g., 'agent1', 'agent2')",
206
+ },
207
+ "reason": {
208
+ "type": "string",
209
+ "description": "Brief reason why this agent has the best answer",
210
+ },
211
+ },
212
+ "required": ["agent_id", "reason"],
213
+ },
214
+ },
215
+ }
216
+
217
+ # Create anonymous mapping for enum constraint
218
+ if valid_agent_ids:
219
+ anon_agent_ids = [f"agent{i}" for i in range(1, len(valid_agent_ids) + 1)]
220
+ tool_def["function"]["parameters"]["properties"]["agent_id"][
221
+ "enum"
222
+ ] = anon_agent_ids
223
+
224
+ return tool_def
225
+
226
+ def get_standard_tools(
227
+ self, valid_agent_ids: Optional[List[str]] = None
228
+ ) -> List[Dict[str, Any]]:
229
+ """Get standard tools for MassGen framework."""
230
+ return [self.get_new_answer_tool(), self.get_vote_tool(valid_agent_ids)]
231
+
232
+ def final_presentation_system_message(
233
+ self, original_system_message: Optional[str] = None
234
+ ) -> str:
235
+ """System message for final answer presentation by winning agent.
236
+
237
+ Args:
238
+ original_system_message: The agent's original system message to preserve
239
+ """
240
+ if "final_presentation_system_message" in self._template_overrides:
241
+ return str(self._template_overrides["final_presentation_system_message"])
242
+
243
+ presentation_instructions = """You have been selected as the winning answer in a coordination process. Your task is to present a polished, comprehensive final answer that incorporates the best insights from all participants.
244
+
245
+ Consider:
246
+ 1. Your original response and how it can be refined
247
+ 2. Valuable insights from other agents' answers that should be incorporated
248
+ 3. Feedback received through the voting process
249
+ 4. Ensuring clarity, completeness, and comprehensiveness for the final audience
250
+
251
+ Present your final coordinated answer in the most helpful and complete way possible."""
252
+
253
+ # Combine with original system message if provided
254
+ if original_system_message:
255
+ return f"""{original_system_message}
256
+
257
+ COORDINATION CONTEXT:
258
+ {presentation_instructions}"""
259
+ else:
260
+ return presentation_instructions
261
+
262
+ # =============================================================================
263
+ # COMPLETE MESSAGE BUILDERS
264
+ # =============================================================================
265
+
266
+ def build_case1_user_message(self, task: str) -> str:
267
+ """Build Case 1 user message (no summaries exist)."""
268
+ return f"""{self.format_original_message(task)}
269
+
270
+ {self.format_current_answers_empty()}"""
271
+
272
+ def build_case2_user_message(
273
+ self, task: str, agent_summaries: Dict[str, str]
274
+ ) -> str:
275
+ """Build Case 2 user message (summaries exist)."""
276
+ return f"""{self.format_original_message(task)}
277
+
278
+ {self.format_current_answers_with_summaries(agent_summaries)}"""
279
+
280
+ def build_evaluation_message(
281
+ self, task: str, agent_answers: Optional[Dict[str, str]] = None
282
+ ) -> str:
283
+ """Build evaluation user message for any case."""
284
+ if agent_answers:
285
+ return self.build_case2_user_message(task, agent_answers)
286
+ else:
287
+ return self.build_case1_user_message(task)
288
+
289
+ def build_coordination_context(
290
+ self,
291
+ current_task: str,
292
+ conversation_history: Optional[List[Dict[str, str]]] = None,
293
+ agent_answers: Optional[Dict[str, str]] = None,
294
+ ) -> str:
295
+ """Build coordination context including conversation history and current state."""
296
+ if "build_coordination_context" in self._template_overrides:
297
+ override = self._template_overrides["build_coordination_context"]
298
+ if callable(override):
299
+ return override(current_task, conversation_history, agent_answers)
300
+ return str(override)
301
+
302
+ context_parts = []
303
+
304
+ # Add conversation history if present
305
+ if conversation_history and len(conversation_history) > 0:
306
+ history_formatted = self.format_conversation_history(conversation_history)
307
+ if history_formatted:
308
+ context_parts.append(history_formatted)
309
+ context_parts.append("") # Empty line for spacing
310
+
311
+ # Add current task
312
+ context_parts.append(self.format_original_message(current_task))
313
+ context_parts.append("") # Empty line for spacing
314
+
315
+ # Add agent answers
316
+ if agent_answers:
317
+ context_parts.append(
318
+ self.format_current_answers_with_summaries(agent_answers)
319
+ )
320
+ else:
321
+ context_parts.append(self.format_current_answers_empty())
322
+
323
+ return "\n".join(context_parts)
324
+
325
+ # =============================================================================
326
+ # CONVERSATION BUILDERS
327
+ # =============================================================================
328
+
329
+ def build_initial_conversation(
330
+ self,
331
+ task: str,
332
+ agent_summaries: Optional[Dict[str, str]] = None,
333
+ valid_agent_ids: Optional[List[str]] = None,
334
+ ) -> Dict[str, Any]:
335
+ """Build complete initial conversation for MassGen evaluation."""
336
+ return {
337
+ "system_message": self.evaluation_system_message(),
338
+ "user_message": self.build_evaluation_message(task, agent_summaries),
339
+ "tools": self.get_standard_tools(valid_agent_ids),
340
+ }
341
+
342
+ def build_conversation_with_context(
343
+ self,
344
+ current_task: str,
345
+ conversation_history: Optional[List[Dict[str, str]]] = None,
346
+ agent_summaries: Optional[Dict[str, str]] = None,
347
+ valid_agent_ids: Optional[List[str]] = None,
348
+ ) -> Dict[str, Any]:
349
+ """Build complete conversation with conversation history context for MassGen evaluation."""
350
+ return {
351
+ "system_message": self.system_message_with_context(conversation_history),
352
+ "user_message": self.build_coordination_context(
353
+ current_task, conversation_history, agent_summaries
354
+ ),
355
+ "tools": self.get_standard_tools(valid_agent_ids),
356
+ }
357
+
358
+ def build_final_presentation_message(
359
+ self,
360
+ original_task: str,
361
+ vote_summary: str,
362
+ all_answers: Dict[str, str],
363
+ selected_agent_id: str,
364
+ ) -> str:
365
+ """Build final presentation message for winning agent."""
366
+ # Format all answers with clear marking
367
+ answers_section = "All answers provided during coordination:\n"
368
+ for agent_id, answer in all_answers.items():
369
+ marker = " (YOUR ANSWER)" if agent_id == selected_agent_id else ""
370
+ answers_section += f'\n{agent_id}{marker}: "{answer}"\n'
371
+
372
+ return f"""{self.format_original_message(original_task)}
373
+
374
+ VOTING RESULTS:
375
+ {vote_summary}
376
+
377
+ {answers_section}
378
+
379
+ Based on the coordination process above, present your final answer:"""
380
+
381
+ def add_enforcement_message(
382
+ self, conversation_messages: List[Dict[str, str]]
383
+ ) -> List[Dict[str, str]]:
384
+ """Add enforcement message to existing conversation (Case 3)."""
385
+ messages = conversation_messages.copy()
386
+ messages.append({"role": "user", "content": self.enforcement_message()})
387
+ return messages
388
+
389
+
390
+ # Global template instance
391
+ _templates = MessageTemplates()
392
+
393
+
394
+ def get_templates() -> MessageTemplates:
395
+ """Get global message templates instance."""
396
+ return _templates
397
+
398
+
399
+ def set_templates(templates: MessageTemplates) -> None:
400
+ """Set global message templates instance."""
401
+ global _templates
402
+ _templates = templates
403
+
404
+
405
+ # Convenience functions for common operations
406
+ def build_case1_conversation(task: str) -> Dict[str, Any]:
407
+ """Build Case 1 conversation (no summaries exist)."""
408
+ return get_templates().build_initial_conversation(task)
409
+
410
+
411
+ def build_case2_conversation(
412
+ task: str,
413
+ agent_summaries: Dict[str, str],
414
+ valid_agent_ids: Optional[List[str]] = None,
415
+ ) -> Dict[str, Any]:
416
+ """Build Case 2 conversation (summaries exist)."""
417
+ return get_templates().build_initial_conversation(
418
+ task, agent_summaries, valid_agent_ids
419
+ )
420
+
421
+
422
+ def get_standard_tools(
423
+ valid_agent_ids: Optional[List[str]] = None,
424
+ ) -> List[Dict[str, Any]]:
425
+ """Get standard MassGen tools."""
426
+ return get_templates().get_standard_tools(valid_agent_ids)
427
+
428
+
429
+ def get_enforcement_message() -> str:
430
+ """Get enforcement message for Case 3."""
431
+ return get_templates().enforcement_message()