massgen 0.1.4__py3-none-any.whl → 0.1.6__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 (84) hide show
  1. massgen/__init__.py +1 -1
  2. massgen/backend/base_with_custom_tool_and_mcp.py +453 -23
  3. massgen/backend/capabilities.py +39 -0
  4. massgen/backend/chat_completions.py +111 -197
  5. massgen/backend/claude.py +210 -181
  6. massgen/backend/gemini.py +1015 -1559
  7. massgen/backend/grok.py +3 -2
  8. massgen/backend/response.py +160 -220
  9. massgen/chat_agent.py +340 -20
  10. massgen/cli.py +399 -25
  11. massgen/config_builder.py +20 -54
  12. massgen/config_validator.py +931 -0
  13. massgen/configs/README.md +95 -10
  14. massgen/configs/memory/gpt5mini_gemini_baseline_research_to_implementation.yaml +94 -0
  15. massgen/configs/memory/gpt5mini_gemini_context_window_management.yaml +187 -0
  16. massgen/configs/memory/gpt5mini_gemini_research_to_implementation.yaml +127 -0
  17. massgen/configs/memory/gpt5mini_high_reasoning_gemini.yaml +107 -0
  18. massgen/configs/memory/single_agent_compression_test.yaml +64 -0
  19. massgen/configs/tools/custom_tools/claude_code_custom_tool_with_mcp_example.yaml +1 -0
  20. massgen/configs/tools/custom_tools/claude_custom_tool_example_no_path.yaml +1 -1
  21. massgen/configs/tools/custom_tools/claude_custom_tool_with_mcp_example.yaml +1 -0
  22. massgen/configs/tools/custom_tools/computer_use_browser_example.yaml +1 -1
  23. massgen/configs/tools/custom_tools/computer_use_docker_example.yaml +1 -1
  24. massgen/configs/tools/custom_tools/gemini_custom_tool_with_mcp_example.yaml +1 -0
  25. massgen/configs/tools/custom_tools/gpt5_nano_custom_tool_with_mcp_example.yaml +1 -0
  26. massgen/configs/tools/custom_tools/gpt_oss_custom_tool_with_mcp_example.yaml +1 -0
  27. massgen/configs/tools/custom_tools/grok3_mini_custom_tool_with_mcp_example.yaml +1 -0
  28. massgen/configs/tools/custom_tools/interop/ag2_and_langgraph_lesson_planner.yaml +65 -0
  29. massgen/configs/tools/custom_tools/interop/ag2_and_openai_assistant_lesson_planner.yaml +65 -0
  30. massgen/configs/tools/custom_tools/interop/ag2_lesson_planner_example.yaml +48 -0
  31. massgen/configs/tools/custom_tools/interop/agentscope_lesson_planner_example.yaml +48 -0
  32. massgen/configs/tools/custom_tools/interop/langgraph_lesson_planner_example.yaml +49 -0
  33. massgen/configs/tools/custom_tools/interop/openai_assistant_lesson_planner_example.yaml +50 -0
  34. massgen/configs/tools/custom_tools/interop/smolagent_lesson_planner_example.yaml +49 -0
  35. massgen/configs/tools/custom_tools/qwen_api_custom_tool_with_mcp_example.yaml +1 -0
  36. massgen/configs/tools/custom_tools/two_models_with_tools_example.yaml +44 -0
  37. massgen/formatter/_gemini_formatter.py +61 -15
  38. massgen/memory/README.md +277 -0
  39. massgen/memory/__init__.py +26 -0
  40. massgen/memory/_base.py +193 -0
  41. massgen/memory/_compression.py +237 -0
  42. massgen/memory/_context_monitor.py +211 -0
  43. massgen/memory/_conversation.py +255 -0
  44. massgen/memory/_fact_extraction_prompts.py +333 -0
  45. massgen/memory/_mem0_adapters.py +257 -0
  46. massgen/memory/_persistent.py +687 -0
  47. massgen/memory/docker-compose.qdrant.yml +36 -0
  48. massgen/memory/docs/DESIGN.md +388 -0
  49. massgen/memory/docs/QUICKSTART.md +409 -0
  50. massgen/memory/docs/SUMMARY.md +319 -0
  51. massgen/memory/docs/agent_use_memory.md +408 -0
  52. massgen/memory/docs/orchestrator_use_memory.md +586 -0
  53. massgen/memory/examples.py +237 -0
  54. massgen/orchestrator.py +207 -7
  55. massgen/tests/memory/test_agent_compression.py +174 -0
  56. massgen/tests/memory/test_context_window_management.py +286 -0
  57. massgen/tests/memory/test_force_compression.py +154 -0
  58. massgen/tests/memory/test_simple_compression.py +147 -0
  59. massgen/tests/test_ag2_lesson_planner.py +223 -0
  60. massgen/tests/test_agent_memory.py +534 -0
  61. massgen/tests/test_config_validator.py +1156 -0
  62. massgen/tests/test_conversation_memory.py +382 -0
  63. massgen/tests/test_langgraph_lesson_planner.py +223 -0
  64. massgen/tests/test_orchestrator_memory.py +620 -0
  65. massgen/tests/test_persistent_memory.py +435 -0
  66. massgen/token_manager/token_manager.py +6 -0
  67. massgen/tool/__init__.py +2 -9
  68. massgen/tool/_decorators.py +52 -0
  69. massgen/tool/_extraframework_agents/ag2_lesson_planner_tool.py +251 -0
  70. massgen/tool/_extraframework_agents/agentscope_lesson_planner_tool.py +303 -0
  71. massgen/tool/_extraframework_agents/langgraph_lesson_planner_tool.py +275 -0
  72. massgen/tool/_extraframework_agents/openai_assistant_lesson_planner_tool.py +247 -0
  73. massgen/tool/_extraframework_agents/smolagent_lesson_planner_tool.py +180 -0
  74. massgen/tool/_manager.py +102 -16
  75. massgen/tool/_registered_tool.py +3 -0
  76. massgen/tool/_result.py +3 -0
  77. {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/METADATA +138 -77
  78. {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/RECORD +82 -37
  79. massgen/backend/gemini_mcp_manager.py +0 -545
  80. massgen/backend/gemini_trackers.py +0 -344
  81. {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/WHEEL +0 -0
  82. {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/entry_points.txt +0 -0
  83. {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/licenses/LICENSE +0 -0
  84. {massgen-0.1.4.dist-info → massgen-0.1.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,251 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AG2 (AutoGen) Nested Chat Lesson Planner Tool
4
+ This tool demonstrates interoperability by wrapping AutoGen's nested chat functionality as a MassGen custom tool.
5
+ """
6
+
7
+ import os
8
+ from typing import Any, AsyncGenerator, Dict, List
9
+
10
+ from autogen import ConversableAgent, GroupChat, GroupChatManager
11
+
12
+ from massgen.tool import context_params
13
+ from massgen.tool._result import ExecutionResult, TextContent
14
+
15
+
16
+ def run_ag2_lesson_planner_agent(
17
+ messages: List[Dict[str, Any]],
18
+ api_key: str,
19
+ ):
20
+ """
21
+ Core AG2 lesson planner agent - pure AutoGen implementation.
22
+
23
+ This function contains the pure AG2/AutoGen logic for creating lesson plans
24
+ using nested chats and multiple specialized agents.
25
+
26
+ Args:
27
+ messages: messages for the agent to execute
28
+ api_key: OpenAI API key for the agents
29
+
30
+ Returns:
31
+ The formatted lesson plan as a string
32
+
33
+ Raises:
34
+ Exception: Any errors during agent execution
35
+ """
36
+ if not messages:
37
+ raise ValueError("No messages provided for lesson planning.")
38
+ # Configure LLM
39
+ llm_config = {
40
+ "config_list": [
41
+ {
42
+ "api_type": "openai",
43
+ "model": "gpt-4o",
44
+ "api_key": api_key,
45
+ },
46
+ ],
47
+ }
48
+
49
+ # Curriculum Standards Agent
50
+ curriculum_agent = ConversableAgent(
51
+ name="Curriculum_Agent",
52
+ system_message="""You are a curriculum standards expert for fourth grade education.
53
+ When given a topic, you provide relevant grade-level standards and learning objectives.
54
+ Format every response as:
55
+ STANDARDS:
56
+ - [Standard 1]
57
+ - [Standard 2]
58
+ OBJECTIVES:
59
+ - By the end of this lesson, students will be able to [objective 1]
60
+ - By the end of this lesson, students will be able to [objective 2]""",
61
+ human_input_mode="NEVER",
62
+ llm_config=llm_config,
63
+ )
64
+
65
+ # Lesson Planner Agent
66
+ lesson_planner_agent = ConversableAgent(
67
+ name="Lesson_Planner_Agent",
68
+ system_message="""You are a lesson planning specialist.
69
+ Given standards and objectives, you create detailed lesson plans including:
70
+ - Opening/Hook (5-10 minutes)
71
+ - Main Activity (20-30 minutes)
72
+ - Practice Activity (15-20 minutes)
73
+ - Assessment/Closure (5-10 minutes)
74
+ Format as a structured lesson plan with clear timing and materials needed.""",
75
+ human_input_mode="NEVER",
76
+ llm_config=llm_config,
77
+ )
78
+
79
+ # Lesson Reviewer Agent
80
+ lesson_reviewer_agent = ConversableAgent(
81
+ name="Lesson_Reviewer_Agent",
82
+ system_message="""You are a lesson plan reviewer who ensures:
83
+ 1. Age-appropriate content and activities
84
+ 2. Alignment with provided standards
85
+ 3. Realistic timing
86
+ 4. Clear instructions
87
+ 5. Differentiation opportunities
88
+ Provide specific feedback in these areas and suggest improvements if needed.""",
89
+ human_input_mode="NEVER",
90
+ llm_config=llm_config,
91
+ )
92
+
93
+ # Lead Teacher Agent
94
+ lead_teacher_agent = ConversableAgent(
95
+ name="Lead_Teacher_Agent",
96
+ system_message="""You are an experienced fourth grade teacher who oversees the lesson planning process.
97
+ Your role is to:
98
+ 1. Initiate the planning process with a clear topic
99
+ 2. Review and integrate feedback from other agents
100
+ 3. Ensure the final lesson plan is practical and engaging
101
+ 4. Make final adjustments based on classroom experience""",
102
+ human_input_mode="NEVER",
103
+ llm_config=llm_config,
104
+ )
105
+
106
+ # Create the group chat for collaborative lesson planning
107
+ planning_chat = GroupChat(
108
+ agents=[curriculum_agent, lesson_planner_agent, lesson_reviewer_agent],
109
+ messages=[],
110
+ max_round=2,
111
+ send_introductions=True,
112
+ )
113
+
114
+ planning_manager = GroupChatManager(
115
+ groupchat=planning_chat,
116
+ llm_config=llm_config,
117
+ )
118
+
119
+ # Formatter of the final lesson plan to a standard format
120
+ formatter_message = """You are a lesson plan formatter. Format the complete plan as follows:
121
+ <title>Lesson plan title</title>
122
+ <standards>Standards covered</standards>
123
+ <learning_objectives>Key learning objectives</learning_objectives>
124
+ <materials>Materials required</materials>
125
+ <activities>Lesson plan activities</activities>
126
+ <assessment>Assessment details</assessment>
127
+ """
128
+
129
+ lesson_formatter = ConversableAgent(
130
+ name="formatter_agent",
131
+ system_message=formatter_message,
132
+ llm_config=llm_config,
133
+ )
134
+
135
+ # Create nested chats configuration
136
+ nested_chats = [
137
+ {
138
+ # The first internal chat determines the standards and objectives
139
+ "recipient": curriculum_agent,
140
+ "message": f"Please provide fourth grade standards and objectives for: {messages}",
141
+ "max_turns": 2,
142
+ "summary_method": "last_msg",
143
+ },
144
+ {
145
+ # A group chat follows, where the lesson plan is created
146
+ "recipient": planning_manager,
147
+ "message": "Based on these standards and objectives, create a detailed lesson plan.",
148
+ "max_turns": 1,
149
+ "summary_method": "last_msg",
150
+ },
151
+ {
152
+ # Finally, a two-agent chat formats the lesson plan
153
+ "recipient": lesson_formatter,
154
+ "message": "Format the lesson plan.",
155
+ "max_turns": 1,
156
+ "summary_method": "last_msg",
157
+ },
158
+ ]
159
+
160
+ # Register nested chats with the lead teacher
161
+ lead_teacher_agent.register_nested_chats(
162
+ chat_queue=nested_chats,
163
+ trigger=lambda sender: sender.name == "Assistant_Agent",
164
+ )
165
+
166
+ # Create a simple assistant agent to trigger the nested chat
167
+ assistant_agent = ConversableAgent(
168
+ name="Assistant_Agent",
169
+ system_message="You are a helpful assistant.",
170
+ human_input_mode="NEVER",
171
+ llm_config=llm_config,
172
+ )
173
+
174
+ # Initiate the chat and get the result
175
+ response = assistant_agent.run(
176
+ recipient=lead_teacher_agent,
177
+ message=str(messages),
178
+ max_turns=1,
179
+ )
180
+
181
+ # Extract the lesson plan from the result
182
+
183
+ return response
184
+
185
+
186
+ @context_params("prompt")
187
+ async def ag2_lesson_planner(
188
+ prompt: List[Dict[str, Any]],
189
+ ) -> AsyncGenerator[ExecutionResult, None]:
190
+ """
191
+ MassGen custom tool wrapper for AG2 lesson planner.
192
+
193
+ This is the interface exposed to MassGen's backend. It handles environment setup,
194
+ error handling, and wraps the core agent logic in ExecutionResult.
195
+
196
+ Args:
197
+ prompt: processed message list from orchestrator (auto-injected via execution_context)
198
+
199
+ Returns:
200
+ ExecutionResult containing the formatted lesson plan or error message
201
+ """
202
+ # Get API key from environment
203
+ api_key = os.getenv("OPENAI_API_KEY")
204
+
205
+ if not api_key:
206
+ yield ExecutionResult(
207
+ output_blocks=[
208
+ TextContent(data="Error: OPENAI_API_KEY not found. Please set the environment variable."),
209
+ ],
210
+ )
211
+
212
+ try:
213
+ # Call the core agent function with processed messages
214
+ response = run_ag2_lesson_planner_agent(
215
+ messages=prompt,
216
+ api_key=api_key,
217
+ )
218
+
219
+ last_nested_chat_event_msgs = []
220
+
221
+ def process_and_log_event(*args, **kwargs) -> None:
222
+ """Process and log AG2 event, returning string representation."""
223
+ line = " ".join(str(arg) for arg in args)
224
+ last_nested_chat_event_msgs.append(line)
225
+
226
+ for event in response.events:
227
+ last_nested_chat_event_msgs.clear()
228
+ event.print(f=process_and_log_event)
229
+ formatted_message = "\n".join(last_nested_chat_event_msgs)
230
+
231
+ if event.type == "run_completion":
232
+ # Final output
233
+ yield ExecutionResult(
234
+ output_blocks=[
235
+ TextContent(data=event.content.summary),
236
+ ],
237
+ )
238
+ else:
239
+ yield ExecutionResult(
240
+ output_blocks=[
241
+ TextContent(data=formatted_message),
242
+ ],
243
+ is_log=True,
244
+ )
245
+
246
+ except Exception as e:
247
+ yield ExecutionResult(
248
+ output_blocks=[
249
+ TextContent(data=f"Error creating lesson plan: {str(e)}"),
250
+ ],
251
+ )
@@ -0,0 +1,303 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ AgentScope Lesson Planner Tool
4
+ This tool demonstrates interoperability by wrapping AgentScope's multi-agent framework as a MassGen custom tool.
5
+ Compatible with AgentScope 1.0.6+
6
+ """
7
+
8
+ import os
9
+ from typing import Any, AsyncGenerator, Dict, List
10
+
11
+ import agentscope
12
+ from agentscope.agent import AgentBase
13
+ from agentscope.formatter import OpenAIChatFormatter
14
+ from agentscope.memory import InMemoryMemory
15
+ from agentscope.message import Msg
16
+ from agentscope.model import OpenAIChatModel
17
+
18
+ from massgen.tool import context_params
19
+ from massgen.tool._result import ExecutionResult, TextContent
20
+
21
+
22
+ class SimpleDialogAgent(AgentBase):
23
+ """
24
+ A simple dialog agent for AgentScope 1.0.6+
25
+ This is a lightweight replacement for the deprecated DialogAgent.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ name: str,
31
+ sys_prompt: str,
32
+ model: OpenAIChatModel,
33
+ ):
34
+ """
35
+ Initialize the dialog agent.
36
+
37
+ Args:
38
+ name: Agent name
39
+ sys_prompt: System prompt for the agent
40
+ model: OpenAI chat model instance
41
+ """
42
+ super().__init__()
43
+ self.name = name
44
+ self.sys_prompt = sys_prompt
45
+ self.model = model
46
+ self.formatter = OpenAIChatFormatter()
47
+ self.memory = InMemoryMemory()
48
+
49
+ # Store system prompt to add later (memory operations are async)
50
+ self.pending_sys_msg = None
51
+ if sys_prompt:
52
+ self.pending_sys_msg = Msg(name="system", content=sys_prompt, role="system")
53
+
54
+ async def reply(self, x: Msg = None) -> Msg:
55
+ """
56
+ Generate a reply to the input message.
57
+
58
+ Args:
59
+ x: Input message
60
+
61
+ Returns:
62
+ Response message
63
+ """
64
+ # Add system prompt on first call
65
+ if self.pending_sys_msg is not None:
66
+ await self.memory.add(self.pending_sys_msg)
67
+ self.pending_sys_msg = None
68
+
69
+ # Add user message to memory
70
+ if x is not None:
71
+ await self.memory.add(x)
72
+
73
+ # Get conversation history
74
+ history = await self.memory.get_memory()
75
+
76
+ # Format messages for the model
77
+ formatted_msgs = []
78
+ for msg in history:
79
+ formatted_msgs.append(
80
+ {
81
+ "role": msg.role,
82
+ "content": msg.content if isinstance(msg.content, str) else str(msg.content),
83
+ },
84
+ )
85
+
86
+ # Generate response using the model
87
+ response = await self.model(formatted_msgs)
88
+
89
+ # Extract content from ChatResponse
90
+ # response.content is a list like [{'type': 'text', 'text': '...'}]
91
+ content = ""
92
+ if hasattr(response, "content") and isinstance(response.content, list):
93
+ for item in response.content:
94
+ if isinstance(item, dict) and item.get("type") == "text":
95
+ content += item.get("text", "")
96
+ elif hasattr(response, "content"):
97
+ content = str(response.content)
98
+ else:
99
+ content = str(response)
100
+
101
+ # Create response message
102
+ response_msg = Msg(
103
+ name=self.name,
104
+ content=content,
105
+ role="assistant",
106
+ )
107
+
108
+ # Add response to memory
109
+ await self.memory.add(response_msg)
110
+
111
+ return response_msg
112
+
113
+
114
+ async def run_agentscope_lesson_planner_agent(
115
+ messages: List[Dict[str, Any]],
116
+ api_key: str,
117
+ ) -> str:
118
+ """
119
+ Core AgentScope lesson planner agent - pure AgentScope implementation.
120
+
121
+ This function contains the pure AgentScope logic for creating lesson plans
122
+ using multiple specialized DialogAgents in a sequential pipeline.
123
+
124
+ Args:
125
+ messages: Complete message history from orchestrator
126
+ api_key: OpenAI API key for the agents
127
+
128
+ Returns:
129
+ The formatted lesson plan as a string
130
+
131
+ Raises:
132
+ Exception: Any errors during agent execution
133
+ """
134
+ if not messages:
135
+ raise ValueError("No messages provided for lesson planning.")
136
+
137
+ # Extract the user's topic/request from messages
138
+ # Messages is typically a list of dicts with 'role' and 'content'
139
+ user_prompt = ""
140
+ for msg in messages:
141
+ if isinstance(msg, dict) and msg.get("role") == "user":
142
+ user_prompt = msg.get("content", "")
143
+ break
144
+
145
+ if not user_prompt:
146
+ # Fallback: use the entire messages as string
147
+ user_prompt = str(messages)
148
+
149
+ # Initialize AgentScope (simplified for 1.0.6)
150
+ agentscope.init(
151
+ project="massgen_lesson_planner",
152
+ name="agentscope_lesson_planner_run",
153
+ logging_level="WARNING",
154
+ )
155
+
156
+ # Create shared model instance
157
+ model = OpenAIChatModel(
158
+ model_name="gpt-4o",
159
+ api_key=api_key,
160
+ stream=False,
161
+ generate_kwargs={
162
+ "temperature": 0.7,
163
+ },
164
+ )
165
+
166
+ # Create specialized agents for each step
167
+ curriculum_agent = SimpleDialogAgent(
168
+ name="Curriculum_Standards_Expert",
169
+ sys_prompt="""You are a curriculum standards expert for fourth grade education.
170
+ When given a topic, you provide relevant grade-level standards and learning objectives.
171
+ Format every response as:
172
+ STANDARDS:
173
+ - [Standard 1]
174
+ - [Standard 2]
175
+ OBJECTIVES:
176
+ - By the end of this lesson, students will be able to [objective 1]
177
+ - By the end of this lesson, students will be able to [objective 2]""",
178
+ model=model,
179
+ )
180
+
181
+ lesson_planner_agent = SimpleDialogAgent(
182
+ name="Lesson_Planning_Specialist",
183
+ sys_prompt="""You are a lesson planning specialist.
184
+ Given standards and objectives, you create detailed lesson plans including:
185
+ - Opening/Hook (5-10 minutes)
186
+ - Main Activity (20-30 minutes)
187
+ - Practice Activity (15-20 minutes)
188
+ - Assessment/Closure (5-10 minutes)
189
+ Format as a structured lesson plan with clear timing and materials needed.""",
190
+ model=model,
191
+ )
192
+
193
+ reviewer_agent = SimpleDialogAgent(
194
+ name="Lesson_Plan_Reviewer",
195
+ sys_prompt="""You are a lesson plan reviewer who ensures:
196
+ 1. Age-appropriate content and activities
197
+ 2. Alignment with provided standards
198
+ 3. Realistic timing
199
+ 4. Clear instructions
200
+ 5. Differentiation opportunities
201
+ Provide an improved version of the lesson plan incorporating your feedback.""",
202
+ model=model,
203
+ )
204
+
205
+ formatter_agent = SimpleDialogAgent(
206
+ name="Lesson_Plan_Formatter",
207
+ sys_prompt="""You are a lesson plan formatter. Format the complete plan as follows:
208
+ <title>Lesson plan title</title>
209
+ <standards>Standards covered</standards>
210
+ <learning_objectives>Key learning objectives</learning_objectives>
211
+ <materials>Materials required</materials>
212
+ <activities>Detailed lesson plan activities with timing</activities>
213
+ <assessment>Assessment details</assessment>""",
214
+ model=model,
215
+ )
216
+
217
+ # Build the initial message
218
+ initial_message = f"Please provide fourth grade standards and objectives for: {user_prompt}"
219
+
220
+ # Create the sequential pipeline
221
+ # Step 1: Get curriculum standards
222
+ msg = Msg(name="User", content=initial_message, role="user")
223
+ standards_response = await curriculum_agent.reply(msg)
224
+
225
+ # Step 2: Create lesson plan based on standards
226
+ lesson_msg = Msg(
227
+ name="User",
228
+ content=f"Based on these standards and objectives, create a detailed lesson plan:\n\n{standards_response.content}",
229
+ role="user",
230
+ )
231
+ lesson_response = await lesson_planner_agent.reply(lesson_msg)
232
+
233
+ # Step 3: Review and improve the lesson plan
234
+ review_msg = Msg(
235
+ name="User",
236
+ content=f"Please review and improve this lesson plan:\n\n{lesson_response.content}",
237
+ role="user",
238
+ )
239
+ reviewed_response = await reviewer_agent.reply(review_msg)
240
+
241
+ # Step 4: Format the final lesson plan
242
+ format_msg = Msg(
243
+ name="User",
244
+ content=f"Format this reviewed lesson plan:\n\n{reviewed_response.content}",
245
+ role="user",
246
+ )
247
+ final_response = await formatter_agent.reply(format_msg)
248
+
249
+ # Extract the final lesson plan
250
+ lesson_plan = final_response.content if isinstance(final_response.content, str) else str(final_response.content)
251
+
252
+ return lesson_plan
253
+
254
+
255
+ @context_params("prompt")
256
+ async def agentscope_lesson_planner(
257
+ prompt: List[Dict[str, Any]],
258
+ ) -> AsyncGenerator[ExecutionResult, None]:
259
+ """
260
+ MassGen custom tool wrapper for AgentScope lesson planner.
261
+
262
+ This is the interface exposed to MassGen's backend. It handles environment setup,
263
+ error handling, and wraps the core agent logic in ExecutionResult.
264
+
265
+ Args:
266
+ prompt: processed message list from orchestrator (auto-injected via execution_context)
267
+
268
+ Returns:
269
+ ExecutionResult containing the formatted lesson plan or error message
270
+ """
271
+ # Get API key from environment
272
+ api_key = os.getenv("OPENAI_API_KEY")
273
+
274
+ if not api_key:
275
+ yield ExecutionResult(
276
+ output_blocks=[
277
+ TextContent(data="Error: OPENAI_API_KEY not found. Please set the environment variable."),
278
+ ],
279
+ )
280
+ return
281
+
282
+ try:
283
+ # Call the core agent function with processed messages
284
+ lesson_plan = await run_agentscope_lesson_planner_agent(
285
+ messages=prompt,
286
+ api_key=api_key,
287
+ )
288
+
289
+ yield ExecutionResult(
290
+ output_blocks=[
291
+ TextContent(data=f"AgentScope Lesson Planner Result:\n\n{lesson_plan}"),
292
+ ],
293
+ )
294
+
295
+ except Exception as e:
296
+ import traceback
297
+
298
+ error_details = traceback.format_exc()
299
+ yield ExecutionResult(
300
+ output_blocks=[
301
+ TextContent(data=f"Error creating lesson plan: {str(e)}\n\nDetails:\n{error_details}"),
302
+ ],
303
+ )