fast-agent-mcp 0.0.16__py3-none-any.whl → 0.1.0__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.
@@ -129,6 +129,27 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
129
129
  self.logger = logger
130
130
  self.name = name
131
131
 
132
+ # Store agents by name - COMPLETE REWRITE OF AGENT STORAGE
133
+ self.agents = {}
134
+ for agent in available_agents:
135
+ # Fix: Remove all special handling of agent names and store them exactly as they are
136
+ agent_name = agent.name
137
+
138
+ # Verify if the name is actually "None" (string) or None (NoneType)
139
+ if agent_name == "None":
140
+ # Try to get a better name from config if available
141
+ if hasattr(agent, "config") and agent.config and agent.config.name:
142
+ agent_name = agent.config.name
143
+ elif agent_name is None:
144
+ # Try to get a better name from config if available
145
+ if hasattr(agent, "config") and agent.config and agent.config.name:
146
+ agent_name = agent.config.name
147
+ else:
148
+ agent_name = f"unnamed_agent_{len(self.agents)}"
149
+
150
+ self.logger.info(f"Adding agent '{agent_name}' to orchestrator")
151
+ self.agents[agent_name] = agent
152
+
132
153
  async def generate(
133
154
  self,
134
155
  message: str | MessageParamT | List[MessageParamT],
@@ -165,19 +186,21 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
165
186
  """Request a structured LLM generation and return the result as a Pydantic model."""
166
187
  import json
167
188
  from pydantic import ValidationError
168
-
189
+
169
190
  params = self.get_request_params(request_params)
170
191
  result_str = await self.generate_str(message=message, request_params=params)
171
-
192
+
172
193
  try:
173
194
  # Directly parse JSON and create model instance
174
195
  parsed_data = json.loads(result_str)
175
196
  return response_model(**parsed_data)
176
197
  except (json.JSONDecodeError, ValidationError) as e:
177
198
  # Log the error and fall back to the original method if direct parsing fails
178
- self.logger.error(f"Direct JSON parsing failed: {str(e)}. Falling back to standard method.")
199
+ self.logger.error(
200
+ f"Direct JSON parsing failed: {str(e)}. Falling back to standard method."
201
+ )
179
202
  self.logger.debug(f"Failed JSON content: {result_str}")
180
-
203
+
181
204
  # Use AugmentedLLM's structured output handling as fallback
182
205
  return await super().generate_structured(
183
206
  message=result_str,
@@ -312,8 +335,14 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
312
335
  context=context,
313
336
  )
314
337
 
315
- # All agents should now be LLM-capable
316
- futures.append(agent._llm.generate_str(message=task_description))
338
+ # Handle both Agent objects and AugmentedLLM objects
339
+ from mcp_agent.workflows.llm.augmented_llm import AugmentedLLM
340
+
341
+ if isinstance(agent, AugmentedLLM):
342
+ futures.append(agent.generate_str(message=task_description))
343
+ else:
344
+ # Traditional Agent objects with _llm property
345
+ futures.append(agent._llm.generate_str(message=task_description))
317
346
 
318
347
  # Wait for all tasks (only if we have valid futures)
319
348
  results = await self.executor.execute(*futures) if futures else []
@@ -332,7 +361,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
332
361
  task_result = TaskWithResult(
333
362
  description=task_model["description"],
334
363
  agent=task_model["agent"], # Track which agent produced this result
335
- result=str(result)
364
+ result=str(result),
336
365
  )
337
366
  step_result.add_task_result(task_result)
338
367
  task_index += 1
@@ -344,7 +373,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
344
373
  TaskWithResult(
345
374
  description=task_model["description"],
346
375
  agent=task_model["agent"],
347
- result=f"ERROR: {error_message}"
376
+ result=f"ERROR: {error_message}",
348
377
  )
349
378
  )
350
379
 
@@ -361,21 +390,39 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
361
390
  """Generate full plan considering previous results"""
362
391
  import json
363
392
  from pydantic import ValidationError
364
- from mcp_agent.workflows.orchestrator.orchestrator_models import Plan, Step, AgentTask
393
+ from mcp_agent.workflows.orchestrator.orchestrator_models import (
394
+ Plan,
395
+ Step,
396
+ AgentTask,
397
+ )
365
398
 
366
399
  params = self.get_request_params(request_params)
367
400
  params = params.model_copy(update={"use_history": False})
368
401
 
402
+ # Debug: Print agent names before formatting
403
+ print("\n------ AGENT NAMES BEFORE FORMATTING ------")
404
+ for agent_name in self.agents.keys():
405
+ print(f"Agent name: '{agent_name}'")
406
+ print("------------------------------------------\n")
407
+
369
408
  # Format agents without numeric prefixes for cleaner XML
370
- agents = "\n".join(
371
- [self._format_agent_info(agent) for agent in self.agents]
372
- )
409
+ agent_formats = []
410
+ for agent_name in self.agents.keys():
411
+ formatted = self._format_agent_info(agent_name)
412
+ agent_formats.append(formatted)
413
+ print(f"Formatted agent '{agent_name}':\n{formatted[:200]}...\n")
414
+
415
+ agents = "\n".join(agent_formats)
373
416
 
374
417
  # Create clear plan status indicator for the template
375
418
  plan_status = "Plan Status: Not Started"
376
419
  if hasattr(plan_result, "is_complete"):
377
- plan_status = "Plan Status: Complete" if plan_result.is_complete else "Plan Status: In Progress"
378
-
420
+ plan_status = (
421
+ "Plan Status: Complete"
422
+ if plan_result.is_complete
423
+ else "Plan Status: In Progress"
424
+ )
425
+
379
426
  prompt = FULL_PLAN_PROMPT_TEMPLATE.format(
380
427
  objective=objective,
381
428
  plan_result=format_plan_result(plan_result),
@@ -388,50 +435,44 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
388
435
  message=prompt,
389
436
  request_params=params,
390
437
  )
391
-
438
+
392
439
  try:
393
440
  # Parse JSON directly
394
441
  data = json.loads(result_str)
395
-
442
+
396
443
  # Create models manually to ensure agent names are preserved exactly as returned
397
444
  steps = []
398
- for step_data in data.get('steps', []):
445
+ for step_data in data.get("steps", []):
399
446
  tasks = []
400
- for task_data in step_data.get('tasks', []):
447
+ for task_data in step_data.get("tasks", []):
401
448
  # Create AgentTask directly from dict, preserving exact agent string
402
449
  task = AgentTask(
403
- description=task_data.get('description', ''),
404
- agent=task_data.get('agent', '') # Preserve exact agent name
450
+ description=task_data.get("description", ""),
451
+ agent=task_data.get("agent", ""), # Preserve exact agent name
405
452
  )
406
453
  tasks.append(task)
407
-
454
+
408
455
  # Create Step with the exact task objects we created
409
- step = Step(
410
- description=step_data.get('description', ''),
411
- tasks=tasks
412
- )
456
+ step = Step(description=step_data.get("description", ""), tasks=tasks)
413
457
  steps.append(step)
414
-
458
+
415
459
  # Create final Plan
416
- plan = Plan(
417
- steps=steps,
418
- is_complete=data.get('is_complete', False)
419
- )
420
-
460
+ plan = Plan(steps=steps, is_complete=data.get("is_complete", False))
461
+
421
462
  return plan
422
-
463
+
423
464
  except (json.JSONDecodeError, ValidationError, KeyError) as e:
424
465
  # Log detailed error and fall back to the original method as last resort
425
466
  self.logger.error(f"Error parsing plan JSON: {str(e)}")
426
467
  self.logger.debug(f"Failed JSON content: {result_str}")
427
-
468
+
428
469
  # Use the normal structured parsing as fallback
429
470
  plan = await self.planner.generate_structured(
430
471
  message=result_str,
431
472
  response_model=Plan,
432
473
  request_params=params,
433
474
  )
434
-
475
+
435
476
  return plan
436
477
 
437
478
  async def _get_next_step(
@@ -443,21 +484,29 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
443
484
  """Generate just the next needed step"""
444
485
  import json
445
486
  from pydantic import ValidationError
446
- from mcp_agent.workflows.orchestrator.orchestrator_models import NextStep, AgentTask
447
-
487
+ from mcp_agent.workflows.orchestrator.orchestrator_models import (
488
+ NextStep,
489
+ AgentTask,
490
+ )
491
+
448
492
  params = self.get_request_params(request_params)
449
493
  params = params.model_copy(update={"use_history": False})
450
494
 
451
495
  # Format agents without numeric prefixes for cleaner XML
496
+ # FIX: Iterate over agent names instead of agent objects
452
497
  agents = "\n".join(
453
- [self._format_agent_info(agent) for agent in self.agents]
498
+ [self._format_agent_info(agent_name) for agent_name in self.agents.keys()]
454
499
  )
455
500
 
456
501
  # Create clear plan status indicator for the template
457
502
  plan_status = "Plan Status: Not Started"
458
503
  if hasattr(plan_result, "is_complete"):
459
- plan_status = "Plan Status: Complete" if plan_result.is_complete else "Plan Status: In Progress"
460
-
504
+ plan_status = (
505
+ "Plan Status: Complete"
506
+ if plan_result.is_complete
507
+ else "Plan Status: In Progress"
508
+ )
509
+
461
510
  prompt = ITERATIVE_PLAN_PROMPT_TEMPLATE.format(
462
511
  objective=objective,
463
512
  plan_result=format_plan_result(plan_result),
@@ -470,55 +519,55 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
470
519
  message=prompt,
471
520
  request_params=params,
472
521
  )
473
-
522
+
474
523
  try:
475
524
  # Parse JSON directly
476
525
  data = json.loads(result_str)
477
-
526
+
478
527
  # Create task objects manually to preserve exact agent names
479
528
  tasks = []
480
- for task_data in data.get('tasks', []):
529
+ for task_data in data.get("tasks", []):
481
530
  # Preserve the exact agent name as specified in the JSON
482
531
  task = AgentTask(
483
- description=task_data.get('description', ''),
484
- agent=task_data.get('agent', '')
532
+ description=task_data.get("description", ""),
533
+ agent=task_data.get("agent", ""),
485
534
  )
486
535
  tasks.append(task)
487
-
536
+
488
537
  # Create step with manually constructed tasks
489
538
  next_step = NextStep(
490
- description=data.get('description', ''),
539
+ description=data.get("description", ""),
491
540
  tasks=tasks,
492
- is_complete=data.get('is_complete', False)
541
+ is_complete=data.get("is_complete", False),
493
542
  )
494
-
543
+
495
544
  return next_step
496
-
545
+
497
546
  except (json.JSONDecodeError, ValidationError, KeyError) as e:
498
547
  # Log detailed error and fall back to the original method
499
548
  self.logger.error(f"Error parsing next step JSON: {str(e)}")
500
549
  self.logger.debug(f"Failed JSON content: {result_str}")
501
-
550
+
502
551
  # Use the normal structured parsing as fallback
503
552
  next_step = await self.planner.generate_structured(
504
553
  message=result_str,
505
554
  response_model=NextStep,
506
555
  request_params=params,
507
556
  )
508
-
557
+
509
558
  return next_step
510
559
 
511
560
  def _format_server_info(self, server_name: str) -> str:
512
561
  """Format server information for display to planners using XML tags"""
513
562
  from mcp_agent.workflows.llm.prompt_utils import format_server_info
514
-
563
+
515
564
  server_config = self.server_registry.get_server_config(server_name)
516
-
565
+
517
566
  # Get description or empty string if not available
518
567
  description = ""
519
568
  if server_config and server_config.description:
520
569
  description = server_config.description
521
-
570
+
522
571
  return format_server_info(server_name, description)
523
572
 
524
573
  def _validate_agent_names(self, plan: Plan) -> None:
@@ -527,12 +576,12 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
527
576
  This helps catch invalid agent references early.
528
577
  """
529
578
  invalid_agents = []
530
-
579
+
531
580
  for step in plan.steps:
532
581
  for task in step.tasks:
533
582
  if task.agent not in self.agents:
534
583
  invalid_agents.append(task.agent)
535
-
584
+
536
585
  if invalid_agents:
537
586
  available_agents = ", ".join(self.agents.keys())
538
587
  invalid_list = ", ".join(invalid_agents)
@@ -540,20 +589,17 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
540
589
  self.logger.error(error_msg)
541
590
  # We don't raise an exception here as the execution will handle invalid agents
542
591
  # by logging errors for individual tasks
543
-
592
+
544
593
  def _format_agent_info(self, agent_name: str) -> str:
545
594
  """Format Agent information for display to planners using XML tags"""
546
595
  from mcp_agent.workflows.llm.prompt_utils import format_agent_info
547
-
596
+
548
597
  agent = self.agents.get(agent_name)
549
598
  if not agent:
599
+ self.logger.error(f"Agent '{agent_name}' not found in orchestrator agents")
550
600
  return ""
551
-
552
- # Get agent instruction as string
553
601
  instruction = agent.instruction
554
- if callable(instruction):
555
- instruction = instruction({})
556
-
602
+
557
603
  # Get servers information
558
604
  server_info = []
559
605
  for server_name in agent.server_names:
@@ -561,7 +607,9 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
561
607
  description = ""
562
608
  if server_config and server_config.description:
563
609
  description = server_config.description
564
-
610
+
565
611
  server_info.append({"name": server_name, "description": description})
566
-
567
- return format_agent_info(agent.name, instruction, server_info if server_info else None)
612
+
613
+ return format_agent_info(
614
+ agent.name, instruction, server_info if server_info else None
615
+ )