massgen 0.1.4__py3-none-any.whl → 0.1.5__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 (46) hide show
  1. massgen/__init__.py +1 -1
  2. massgen/chat_agent.py +340 -20
  3. massgen/cli.py +326 -19
  4. massgen/configs/README.md +52 -10
  5. massgen/configs/memory/gpt5mini_gemini_baseline_research_to_implementation.yaml +94 -0
  6. massgen/configs/memory/gpt5mini_gemini_context_window_management.yaml +187 -0
  7. massgen/configs/memory/gpt5mini_gemini_research_to_implementation.yaml +127 -0
  8. massgen/configs/memory/gpt5mini_high_reasoning_gemini.yaml +107 -0
  9. massgen/configs/memory/single_agent_compression_test.yaml +64 -0
  10. massgen/configs/tools/custom_tools/multimodal_tools/playwright_with_img_understanding.yaml +98 -0
  11. massgen/configs/tools/custom_tools/multimodal_tools/understand_video_example.yaml +54 -0
  12. massgen/memory/README.md +277 -0
  13. massgen/memory/__init__.py +26 -0
  14. massgen/memory/_base.py +193 -0
  15. massgen/memory/_compression.py +237 -0
  16. massgen/memory/_context_monitor.py +211 -0
  17. massgen/memory/_conversation.py +255 -0
  18. massgen/memory/_fact_extraction_prompts.py +333 -0
  19. massgen/memory/_mem0_adapters.py +257 -0
  20. massgen/memory/_persistent.py +687 -0
  21. massgen/memory/docker-compose.qdrant.yml +36 -0
  22. massgen/memory/docs/DESIGN.md +388 -0
  23. massgen/memory/docs/QUICKSTART.md +409 -0
  24. massgen/memory/docs/SUMMARY.md +319 -0
  25. massgen/memory/docs/agent_use_memory.md +408 -0
  26. massgen/memory/docs/orchestrator_use_memory.md +586 -0
  27. massgen/memory/examples.py +237 -0
  28. massgen/orchestrator.py +207 -7
  29. massgen/tests/memory/test_agent_compression.py +174 -0
  30. massgen/tests/memory/test_context_window_management.py +286 -0
  31. massgen/tests/memory/test_force_compression.py +154 -0
  32. massgen/tests/memory/test_simple_compression.py +147 -0
  33. massgen/tests/test_agent_memory.py +534 -0
  34. massgen/tests/test_conversation_memory.py +382 -0
  35. massgen/tests/test_orchestrator_memory.py +620 -0
  36. massgen/tests/test_persistent_memory.py +435 -0
  37. massgen/token_manager/token_manager.py +6 -0
  38. massgen/tools/__init__.py +8 -0
  39. massgen/tools/_planning_mcp_server.py +520 -0
  40. massgen/tools/planning_dataclasses.py +434 -0
  41. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/METADATA +109 -76
  42. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/RECORD +46 -12
  43. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/WHEEL +0 -0
  44. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/entry_points.txt +0 -0
  45. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/licenses/LICENSE +0 -0
  46. {massgen-0.1.4.dist-info → massgen-0.1.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,520 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Planning MCP Server for MassGen
5
+
6
+ This MCP server provides task planning and management tools for agents,
7
+ enabling them to create, track, and manage task plans with dependencies.
8
+
9
+ Tools provided:
10
+ - create_task_plan: Create a new task plan with dependencies
11
+ - add_task: Add a new task to the plan
12
+ - update_task_status: Update task status and detect newly ready tasks
13
+ - edit_task: Edit a task's description
14
+ - get_task_plan: Get the complete current task plan
15
+ - delete_task: Remove a task from the plan
16
+ - get_ready_tasks: Get tasks ready to start (dependencies satisfied)
17
+ - get_blocked_tasks: Get tasks blocked by dependencies
18
+ """
19
+
20
+ import argparse
21
+ import uuid
22
+ from typing import Any, Dict, List, Optional, Union
23
+
24
+ import fastmcp
25
+
26
+ from massgen.tools.planning_dataclasses import TaskPlan
27
+
28
+ # Global storage for task plans (keyed by agent_id)
29
+ _task_plans: Dict[str, TaskPlan] = {}
30
+
31
+
32
+ def _get_or_create_plan(agent_id: str, orchestrator_id: str) -> TaskPlan:
33
+ """
34
+ Get existing plan or create new one for agent.
35
+
36
+ Args:
37
+ agent_id: Agent identifier
38
+ orchestrator_id: Orchestrator identifier
39
+
40
+ Returns:
41
+ TaskPlan for the agent
42
+ """
43
+ key = f"{orchestrator_id}:{agent_id}"
44
+ if key not in _task_plans:
45
+ _task_plans[key] = TaskPlan(agent_id=key)
46
+ return _task_plans[key]
47
+
48
+
49
+ def _resolve_dependency_references(
50
+ task_list: List[Union[str, Dict[str, Any]]]
51
+ ) -> List[Dict[str, Any]]:
52
+ """
53
+ Resolve dependency references (indices -> IDs) in task list.
54
+
55
+ Args:
56
+ task_list: List of task specifications
57
+
58
+ Returns:
59
+ List of normalized task dictionaries with resolved dependencies
60
+
61
+ Raises:
62
+ ValueError: If dependencies are invalid
63
+ """
64
+ # First pass: Generate IDs for all tasks
65
+ normalized_tasks = []
66
+ for i, task_spec in enumerate(task_list):
67
+ if isinstance(task_spec, str):
68
+ # Simple string task
69
+ task_dict = {
70
+ "id": f"task_{i}_{uuid.uuid4().hex[:8]}",
71
+ "description": task_spec,
72
+ "depends_on": []
73
+ }
74
+ elif isinstance(task_spec, dict):
75
+ # Dictionary task
76
+ task_dict = task_spec.copy()
77
+ if "id" not in task_dict:
78
+ task_dict["id"] = f"task_{i}_{uuid.uuid4().hex[:8]}"
79
+ if "depends_on" not in task_dict:
80
+ task_dict["depends_on"] = []
81
+ else:
82
+ raise ValueError(f"Invalid task specification at index {i}: {task_spec}")
83
+
84
+ normalized_tasks.append(task_dict)
85
+
86
+ # Second pass: Resolve index-based dependencies to IDs
87
+ for i, task_dict in enumerate(normalized_tasks):
88
+ resolved_deps = []
89
+ for dep in task_dict.get("depends_on", []):
90
+ if isinstance(dep, int):
91
+ # Index-based reference
92
+ if dep < 0 or dep >= len(normalized_tasks):
93
+ raise ValueError(
94
+ f"Task '{task_dict['id']}': Invalid dependency index {dep}"
95
+ )
96
+ if dep >= i:
97
+ raise ValueError(
98
+ f"Task '{task_dict['id']}': Dependencies must reference earlier tasks"
99
+ )
100
+ resolved_deps.append(normalized_tasks[dep]["id"])
101
+ else:
102
+ # ID-based reference
103
+ resolved_deps.append(dep)
104
+
105
+ task_dict["depends_on"] = resolved_deps
106
+
107
+ return normalized_tasks
108
+
109
+
110
+ async def create_server() -> fastmcp.FastMCP:
111
+ """Factory function to create and configure the planning MCP server."""
112
+
113
+ parser = argparse.ArgumentParser(description="Planning MCP Server")
114
+ parser.add_argument(
115
+ "--agent-id",
116
+ type=str,
117
+ required=True,
118
+ help="ID of the agent using this planning server",
119
+ )
120
+ parser.add_argument(
121
+ "--orchestrator-id",
122
+ type=str,
123
+ required=True,
124
+ help="ID of the orchestrator managing this agent",
125
+ )
126
+ args = parser.parse_args()
127
+
128
+ # Create the FastMCP server
129
+ mcp = fastmcp.FastMCP("Agent Task Planning")
130
+
131
+ # Store agent and orchestrator IDs
132
+ mcp.agent_id = args.agent_id
133
+ mcp.orchestrator_id = args.orchestrator_id
134
+
135
+ @mcp.tool()
136
+ def create_task_plan(tasks: List[Union[str, Dict[str, Any]]]) -> Dict[str, Any]:
137
+ """
138
+ Create a new task plan with a list of tasks.
139
+
140
+ Tasks can be simple strings or structured dictionaries with dependencies.
141
+
142
+ Args:
143
+ tasks: List of task descriptions or task objects
144
+
145
+ Returns:
146
+ Dictionary with plan_id and created task list
147
+
148
+ Examples:
149
+ # Simple tasks (no dependencies)
150
+ create_task_plan([
151
+ "Research existing authentication methods",
152
+ "Design new OAuth flow",
153
+ "Implement backend endpoints"
154
+ ])
155
+
156
+ # Tasks with dependencies (by index)
157
+ create_task_plan([
158
+ "Research OAuth providers",
159
+ {
160
+ "description": "Implement OAuth endpoints",
161
+ "depends_on": [0] # Depends on task at index 0
162
+ },
163
+ {
164
+ "description": "Write integration tests",
165
+ "depends_on": [1] # Depends on task at index 1
166
+ }
167
+ ])
168
+
169
+ # Tasks with named IDs and dependencies
170
+ create_task_plan([
171
+ {
172
+ "id": "research_oauth",
173
+ "description": "Research OAuth providers"
174
+ },
175
+ {
176
+ "id": "implement_oauth",
177
+ "description": "Implement OAuth endpoints",
178
+ "depends_on": ["research_oauth"]
179
+ },
180
+ {
181
+ "id": "integration_tests",
182
+ "description": "Run integration tests",
183
+ "depends_on": ["implement_oauth"]
184
+ }
185
+ ])
186
+
187
+ Dependency Rules:
188
+ - Can reference by index (0-based) or by custom task ID
189
+ - Dependencies must reference earlier tasks in the list
190
+ - Circular dependencies are rejected
191
+ - Tasks with no dependencies can start immediately
192
+ - Tasks with dependencies wait until all deps are completed
193
+ """
194
+ try:
195
+ # Get or create plan for this agent
196
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
197
+
198
+ # Clear existing tasks (creating new plan)
199
+ plan.tasks.clear()
200
+ plan._task_index.clear()
201
+
202
+ # Validate and resolve dependencies
203
+ normalized_tasks = _resolve_dependency_references(tasks)
204
+ plan.validate_dependencies(normalized_tasks)
205
+
206
+ # Create tasks
207
+ created_tasks = []
208
+ for task_spec in normalized_tasks:
209
+ task = plan.add_task(
210
+ description=task_spec["description"],
211
+ task_id=task_spec["id"],
212
+ depends_on=task_spec.get("depends_on", [])
213
+ )
214
+ created_tasks.append(task.to_dict())
215
+
216
+ return {
217
+ "success": True,
218
+ "operation": "create_task_plan",
219
+ "plan_id": plan.agent_id,
220
+ "tasks": created_tasks,
221
+ "summary": {
222
+ "total_tasks": len(created_tasks),
223
+ "ready_tasks": len(plan.get_ready_tasks()),
224
+ "blocked_tasks": len(plan.get_blocked_tasks())
225
+ }
226
+ }
227
+
228
+ except Exception as e:
229
+ return {
230
+ "success": False,
231
+ "operation": "create_task_plan",
232
+ "error": str(e)
233
+ }
234
+
235
+ @mcp.tool()
236
+ def add_task(
237
+ description: str,
238
+ after_task_id: Optional[str] = None,
239
+ depends_on: Optional[List[str]] = None
240
+ ) -> Dict[str, Any]:
241
+ """
242
+ Add a new task to the plan.
243
+
244
+ Args:
245
+ description: Task description
246
+ after_task_id: Optional ID to insert after (otherwise appends)
247
+ depends_on: Optional list of task IDs this task depends on
248
+
249
+ Returns:
250
+ Dictionary with new task details
251
+
252
+ Example:
253
+ # Add task with dependencies
254
+ add_task(
255
+ "Deploy to production",
256
+ depends_on=["run_tests", "update_docs"]
257
+ )
258
+ """
259
+ try:
260
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
261
+
262
+ task = plan.add_task(
263
+ description=description,
264
+ after_task_id=after_task_id,
265
+ depends_on=depends_on or []
266
+ )
267
+
268
+ return {
269
+ "success": True,
270
+ "operation": "add_task",
271
+ "task": task.to_dict()
272
+ }
273
+
274
+ except Exception as e:
275
+ return {
276
+ "success": False,
277
+ "operation": "add_task",
278
+ "error": str(e)
279
+ }
280
+
281
+ @mcp.tool()
282
+ def update_task_status(
283
+ task_id: str,
284
+ status: str # Will be validated as Literal in the function
285
+ ) -> Dict[str, Any]:
286
+ """
287
+ Update the status of a task.
288
+
289
+ Args:
290
+ task_id: ID of task to update
291
+ status: New status (pending/in_progress/completed/blocked)
292
+
293
+ Returns:
294
+ Dictionary with updated task details and newly ready tasks
295
+
296
+ Example:
297
+ update_task_status("research_oauth", "completed")
298
+ """
299
+ try:
300
+ # Validate status
301
+ valid_statuses = ["pending", "in_progress", "completed", "blocked"]
302
+ if status not in valid_statuses:
303
+ raise ValueError(
304
+ f"Invalid status '{status}'. Must be one of: {valid_statuses}"
305
+ )
306
+
307
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
308
+ result = plan.update_task_status(task_id, status)
309
+
310
+ return {
311
+ "success": True,
312
+ "operation": "update_task_status",
313
+ **result
314
+ }
315
+
316
+ except Exception as e:
317
+ return {
318
+ "success": False,
319
+ "operation": "update_task_status",
320
+ "error": str(e)
321
+ }
322
+
323
+ @mcp.tool()
324
+ def edit_task(
325
+ task_id: str,
326
+ description: Optional[str] = None
327
+ ) -> Dict[str, Any]:
328
+ """
329
+ Edit a task's description.
330
+
331
+ Args:
332
+ task_id: ID of task to edit
333
+ description: New description (if provided)
334
+
335
+ Returns:
336
+ Dictionary with updated task details
337
+
338
+ Example:
339
+ edit_task("research_oauth", "Research OAuth 2.0 providers and best practices")
340
+ """
341
+ try:
342
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
343
+ task = plan.edit_task(task_id, description)
344
+
345
+ return {
346
+ "success": True,
347
+ "operation": "edit_task",
348
+ "task": task.to_dict()
349
+ }
350
+
351
+ except Exception as e:
352
+ return {
353
+ "success": False,
354
+ "operation": "edit_task",
355
+ "error": str(e)
356
+ }
357
+
358
+ @mcp.tool()
359
+ def get_task_plan() -> Dict[str, Any]:
360
+ """
361
+ Get the current task plan for this agent.
362
+
363
+ Returns:
364
+ Dictionary with complete task plan including all tasks and their statuses
365
+
366
+ Example:
367
+ plan = get_task_plan()
368
+ print(f"Total tasks: {plan['summary']['total_tasks']}")
369
+ print(f"Ready tasks: {plan['summary']['ready_tasks']}")
370
+ """
371
+ try:
372
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
373
+
374
+ ready_tasks = plan.get_ready_tasks()
375
+ blocked_tasks = plan.get_blocked_tasks()
376
+
377
+ return {
378
+ "success": True,
379
+ "operation": "get_task_plan",
380
+ "plan": plan.to_dict(),
381
+ "summary": {
382
+ "total_tasks": len(plan.tasks),
383
+ "completed_tasks": sum(1 for t in plan.tasks if t.status == "completed"),
384
+ "in_progress_tasks": sum(1 for t in plan.tasks if t.status == "in_progress"),
385
+ "ready_tasks": len(ready_tasks),
386
+ "blocked_tasks": len(blocked_tasks)
387
+ }
388
+ }
389
+
390
+ except Exception as e:
391
+ return {
392
+ "success": False,
393
+ "operation": "get_task_plan",
394
+ "error": str(e)
395
+ }
396
+
397
+ @mcp.tool()
398
+ def delete_task(task_id: str) -> Dict[str, Any]:
399
+ """
400
+ Remove a task from the plan.
401
+
402
+ Args:
403
+ task_id: ID of task to delete
404
+
405
+ Returns:
406
+ Success confirmation
407
+
408
+ Raises:
409
+ Error if other tasks depend on this task
410
+
411
+ Example:
412
+ delete_task("obsolete_task_id")
413
+ """
414
+ try:
415
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
416
+ plan.delete_task(task_id)
417
+
418
+ return {
419
+ "success": True,
420
+ "operation": "delete_task",
421
+ "deleted_task_id": task_id
422
+ }
423
+
424
+ except Exception as e:
425
+ return {
426
+ "success": False,
427
+ "operation": "delete_task",
428
+ "error": str(e)
429
+ }
430
+
431
+ @mcp.tool()
432
+ def get_ready_tasks() -> Dict[str, Any]:
433
+ """
434
+ Get all tasks that are ready to start (dependencies satisfied).
435
+
436
+ Returns:
437
+ Dictionary with list of tasks that have status='pending' and all
438
+ dependencies completed
439
+
440
+ Use cases:
441
+ - Identify which tasks can be worked on now
442
+ - Find tasks that can be delegated in parallel
443
+ - Avoid blocking on incomplete dependencies
444
+
445
+ Example:
446
+ result = get_ready_tasks()
447
+ for task in result['ready_tasks']:
448
+ print(f"Ready: {task['description']}")
449
+ """
450
+ try:
451
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
452
+ ready_tasks = plan.get_ready_tasks()
453
+
454
+ return {
455
+ "success": True,
456
+ "operation": "get_ready_tasks",
457
+ "ready_tasks": [t.to_dict() for t in ready_tasks],
458
+ "count": len(ready_tasks)
459
+ }
460
+
461
+ except Exception as e:
462
+ return {
463
+ "success": False,
464
+ "operation": "get_ready_tasks",
465
+ "error": str(e)
466
+ }
467
+
468
+ @mcp.tool()
469
+ def get_blocked_tasks() -> Dict[str, Any]:
470
+ """
471
+ Get all tasks that are blocked by incomplete dependencies.
472
+
473
+ Returns:
474
+ Dictionary with list of tasks that have status='pending' but
475
+ dependencies not completed, including what each task is waiting on
476
+
477
+ Use cases:
478
+ - Understand what's blocking progress
479
+ - Prioritize completing blocking tasks
480
+ - Visualize dependency chains
481
+
482
+ Example:
483
+ result = get_blocked_tasks()
484
+ for task in result['blocked_tasks']:
485
+ print(f"Blocked: {task['description']}")
486
+ print(f" Waiting on: {task['blocking_task_ids']}")
487
+ """
488
+ try:
489
+ plan = _get_or_create_plan(mcp.agent_id, mcp.orchestrator_id)
490
+ blocked_tasks = plan.get_blocked_tasks()
491
+
492
+ # Add blocking task info for each blocked task
493
+ blocked_with_info = []
494
+ for task in blocked_tasks:
495
+ task_dict = task.to_dict()
496
+ task_dict["blocking_task_ids"] = plan.get_blocking_tasks(task.id)
497
+ blocked_with_info.append(task_dict)
498
+
499
+ return {
500
+ "success": True,
501
+ "operation": "get_blocked_tasks",
502
+ "blocked_tasks": blocked_with_info,
503
+ "count": len(blocked_tasks)
504
+ }
505
+
506
+ except Exception as e:
507
+ return {
508
+ "success": False,
509
+ "operation": "get_blocked_tasks",
510
+ "error": str(e)
511
+ }
512
+
513
+ return mcp
514
+
515
+
516
+ if __name__ == "__main__":
517
+ import asyncio
518
+ import fastmcp
519
+
520
+ asyncio.run(fastmcp.run(create_server))