fast-agent-mcp 0.0.15__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.
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/METADATA +121 -21
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/RECORD +27 -25
- mcp_agent/cli/__main__.py +3 -0
- mcp_agent/cli/commands/bootstrap.py +1 -1
- mcp_agent/cli/commands/setup.py +4 -1
- mcp_agent/cli/main.py +13 -3
- mcp_agent/config.py +19 -11
- mcp_agent/core/agent_app.py +1 -1
- mcp_agent/core/enhanced_prompt.py +13 -5
- mcp_agent/core/fastagent.py +87 -49
- mcp_agent/resources/examples/data-analysis/analysis-campaign.py +188 -0
- mcp_agent/resources/examples/data-analysis/analysis.py +26 -0
- mcp_agent/resources/examples/workflows/evaluator.py +3 -3
- mcp_agent/resources/examples/workflows/orchestrator.py +1 -1
- mcp_agent/resources/examples/workflows/parallel.py +0 -4
- mcp_agent/workflows/evaluator_optimizer/evaluator_optimizer.py +229 -91
- mcp_agent/workflows/llm/augmented_llm_anthropic.py +16 -2
- mcp_agent/workflows/llm/augmented_llm_openai.py +13 -1
- mcp_agent/workflows/llm/prompt_utils.py +137 -0
- mcp_agent/workflows/orchestrator/orchestrator.py +252 -50
- mcp_agent/workflows/orchestrator/orchestrator_models.py +81 -9
- mcp_agent/workflows/orchestrator/orchestrator_prompts.py +112 -42
- mcp_agent/workflows/router/router_base.py +113 -21
- mcp_agent/workflows/router/router_llm.py +19 -5
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/WHEEL +0 -0
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/entry_points.txt +0 -0
- {fast_agent_mcp-0.0.15.dist-info → fast_agent_mcp-0.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -21,7 +21,7 @@ from mcp_agent.workflows.llm.augmented_llm import (
|
|
21
21
|
)
|
22
22
|
from mcp_agent.workflows.orchestrator.orchestrator_models import (
|
23
23
|
format_plan_result,
|
24
|
-
|
24
|
+
format_step_result_text,
|
25
25
|
NextStep,
|
26
26
|
Plan,
|
27
27
|
PlanResult,
|
@@ -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],
|
@@ -163,15 +184,29 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
163
184
|
request_params: RequestParams | None = None,
|
164
185
|
) -> ModelT:
|
165
186
|
"""Request a structured LLM generation and return the result as a Pydantic model."""
|
187
|
+
import json
|
188
|
+
from pydantic import ValidationError
|
189
|
+
|
166
190
|
params = self.get_request_params(request_params)
|
167
191
|
result_str = await self.generate_str(message=message, request_params=params)
|
168
192
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
response_model
|
173
|
-
|
174
|
-
|
193
|
+
try:
|
194
|
+
# Directly parse JSON and create model instance
|
195
|
+
parsed_data = json.loads(result_str)
|
196
|
+
return response_model(**parsed_data)
|
197
|
+
except (json.JSONDecodeError, ValidationError) as e:
|
198
|
+
# Log the error and fall back to the original method if direct parsing fails
|
199
|
+
self.logger.error(
|
200
|
+
f"Direct JSON parsing failed: {str(e)}. Falling back to standard method."
|
201
|
+
)
|
202
|
+
self.logger.debug(f"Failed JSON content: {result_str}")
|
203
|
+
|
204
|
+
# Use AugmentedLLM's structured output handling as fallback
|
205
|
+
return await super().generate_structured(
|
206
|
+
message=result_str,
|
207
|
+
response_model=response_model,
|
208
|
+
request_params=params,
|
209
|
+
)
|
175
210
|
|
176
211
|
async def execute(
|
177
212
|
self, objective: str, request_params: RequestParams | None = None
|
@@ -205,11 +240,15 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
205
240
|
)
|
206
241
|
logger.debug(f"Iteration {iterations}: Iterative plan:", data=next_step)
|
207
242
|
plan = Plan(steps=[next_step], is_complete=next_step.is_complete)
|
243
|
+
# Validate agent names in the plan early
|
244
|
+
self._validate_agent_names(plan)
|
208
245
|
elif self.plan_type == "full":
|
209
246
|
plan = await self._get_full_plan(
|
210
247
|
objective=objective, plan_result=plan_result, request_params=params
|
211
248
|
)
|
212
249
|
logger.debug(f"Iteration {iterations}: Full Plan:", data=plan)
|
250
|
+
# Validate agent names in the plan early
|
251
|
+
self._validate_agent_names(plan)
|
213
252
|
else:
|
214
253
|
raise ValueError(f"Invalid plan type {self.plan_type}")
|
215
254
|
|
@@ -221,6 +260,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
221
260
|
plan_result.is_complete = True
|
222
261
|
|
223
262
|
# Synthesize final result into a single message
|
263
|
+
# Use the structured XML format for better context
|
224
264
|
synthesis_prompt = SYNTHESIZE_PLAN_PROMPT_TEMPLATE.format(
|
225
265
|
plan_result=format_plan_result(plan_result)
|
226
266
|
)
|
@@ -265,6 +305,7 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
265
305
|
"""Execute a step's subtasks in parallel and synthesize results"""
|
266
306
|
|
267
307
|
step_result = StepResult(step=step, task_results=[])
|
308
|
+
# Use structured XML format for context to help agents better understand the context
|
268
309
|
context = format_plan_result(previous_result)
|
269
310
|
|
270
311
|
# Execute tasks
|
@@ -272,16 +313,18 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
272
313
|
error_tasks = []
|
273
314
|
|
274
315
|
for task in step.tasks:
|
316
|
+
# Make sure we're using a valid agent name
|
275
317
|
agent = self.agents.get(task.agent)
|
276
318
|
if not agent:
|
277
|
-
#
|
319
|
+
# Log a more prominent error - this is a serious problem that shouldn't happen
|
320
|
+
# with the improved prompt
|
278
321
|
self.logger.error(
|
279
|
-
f"No agent found matching '{task.agent}'. Available agents: {list(self.agents.keys())}"
|
322
|
+
f"AGENT VALIDATION ERROR: No agent found matching '{task.agent}'. Available agents: {list(self.agents.keys())}"
|
280
323
|
)
|
281
324
|
error_tasks.append(
|
282
325
|
(
|
283
326
|
task,
|
284
|
-
f"Error: Agent '{task.agent}' not found. Available agents: {', '.join(self.agents.keys())}",
|
327
|
+
f"Error: Agent '{task.agent}' not found. This indicates a problem with the plan generation. Available agents: {', '.join(self.agents.keys())}",
|
285
328
|
)
|
286
329
|
)
|
287
330
|
continue
|
@@ -292,8 +335,14 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
292
335
|
context=context,
|
293
336
|
)
|
294
337
|
|
295
|
-
#
|
296
|
-
|
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))
|
297
346
|
|
298
347
|
# Wait for all tasks (only if we have valid futures)
|
299
348
|
results = await self.executor.execute(*futures) if futures else []
|
@@ -307,18 +356,29 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
307
356
|
|
308
357
|
if task_index < len(results):
|
309
358
|
result = results[task_index]
|
310
|
-
|
311
|
-
|
359
|
+
# Create a TaskWithResult that includes the agent name for attribution
|
360
|
+
task_model = task.model_dump()
|
361
|
+
task_result = TaskWithResult(
|
362
|
+
description=task_model["description"],
|
363
|
+
agent=task_model["agent"], # Track which agent produced this result
|
364
|
+
result=str(result),
|
312
365
|
)
|
366
|
+
step_result.add_task_result(task_result)
|
313
367
|
task_index += 1
|
314
368
|
|
315
369
|
# Add error task results
|
316
370
|
for task, error_message in error_tasks:
|
371
|
+
task_model = task.model_dump()
|
317
372
|
step_result.add_task_result(
|
318
|
-
TaskWithResult(
|
373
|
+
TaskWithResult(
|
374
|
+
description=task_model["description"],
|
375
|
+
agent=task_model["agent"],
|
376
|
+
result=f"ERROR: {error_message}",
|
377
|
+
)
|
319
378
|
)
|
320
379
|
|
321
|
-
|
380
|
+
# Use text formatting for display in logs
|
381
|
+
step_result.result = format_step_result_text(step_result)
|
322
382
|
return step_result
|
323
383
|
|
324
384
|
async def _get_full_plan(
|
@@ -328,30 +388,92 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
328
388
|
request_params: RequestParams | None = None,
|
329
389
|
) -> Plan:
|
330
390
|
"""Generate full plan considering previous results"""
|
391
|
+
import json
|
392
|
+
from pydantic import ValidationError
|
393
|
+
from mcp_agent.workflows.orchestrator.orchestrator_models import (
|
394
|
+
Plan,
|
395
|
+
Step,
|
396
|
+
AgentTask,
|
397
|
+
)
|
398
|
+
|
331
399
|
params = self.get_request_params(request_params)
|
332
400
|
params = params.model_copy(update={"use_history": False})
|
333
401
|
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
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
|
+
|
408
|
+
# Format agents without numeric prefixes for cleaner XML
|
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)
|
416
|
+
|
417
|
+
# Create clear plan status indicator for the template
|
418
|
+
plan_status = "Plan Status: Not Started"
|
419
|
+
if hasattr(plan_result, "is_complete"):
|
420
|
+
plan_status = (
|
421
|
+
"Plan Status: Complete"
|
422
|
+
if plan_result.is_complete
|
423
|
+
else "Plan Status: In Progress"
|
424
|
+
)
|
340
425
|
|
341
426
|
prompt = FULL_PLAN_PROMPT_TEMPLATE.format(
|
342
427
|
objective=objective,
|
343
428
|
plan_result=format_plan_result(plan_result),
|
429
|
+
plan_status=plan_status,
|
344
430
|
agents=agents,
|
345
431
|
)
|
346
432
|
|
347
|
-
#
|
348
|
-
|
433
|
+
# Get raw JSON response from LLM
|
434
|
+
result_str = await self.planner.generate_str(
|
349
435
|
message=prompt,
|
350
|
-
response_model=Plan,
|
351
436
|
request_params=params,
|
352
437
|
)
|
353
438
|
|
354
|
-
|
439
|
+
try:
|
440
|
+
# Parse JSON directly
|
441
|
+
data = json.loads(result_str)
|
442
|
+
|
443
|
+
# Create models manually to ensure agent names are preserved exactly as returned
|
444
|
+
steps = []
|
445
|
+
for step_data in data.get("steps", []):
|
446
|
+
tasks = []
|
447
|
+
for task_data in step_data.get("tasks", []):
|
448
|
+
# Create AgentTask directly from dict, preserving exact agent string
|
449
|
+
task = AgentTask(
|
450
|
+
description=task_data.get("description", ""),
|
451
|
+
agent=task_data.get("agent", ""), # Preserve exact agent name
|
452
|
+
)
|
453
|
+
tasks.append(task)
|
454
|
+
|
455
|
+
# Create Step with the exact task objects we created
|
456
|
+
step = Step(description=step_data.get("description", ""), tasks=tasks)
|
457
|
+
steps.append(step)
|
458
|
+
|
459
|
+
# Create final Plan
|
460
|
+
plan = Plan(steps=steps, is_complete=data.get("is_complete", False))
|
461
|
+
|
462
|
+
return plan
|
463
|
+
|
464
|
+
except (json.JSONDecodeError, ValidationError, KeyError) as e:
|
465
|
+
# Log detailed error and fall back to the original method as last resort
|
466
|
+
self.logger.error(f"Error parsing plan JSON: {str(e)}")
|
467
|
+
self.logger.debug(f"Failed JSON content: {result_str}")
|
468
|
+
|
469
|
+
# Use the normal structured parsing as fallback
|
470
|
+
plan = await self.planner.generate_structured(
|
471
|
+
message=result_str,
|
472
|
+
response_model=Plan,
|
473
|
+
request_params=params,
|
474
|
+
)
|
475
|
+
|
476
|
+
return plan
|
355
477
|
|
356
478
|
async def _get_next_step(
|
357
479
|
self,
|
@@ -360,54 +482,134 @@ class Orchestrator(AugmentedLLM[MessageParamT, MessageT]):
|
|
360
482
|
request_params: RequestParams | None = None,
|
361
483
|
) -> NextStep:
|
362
484
|
"""Generate just the next needed step"""
|
485
|
+
import json
|
486
|
+
from pydantic import ValidationError
|
487
|
+
from mcp_agent.workflows.orchestrator.orchestrator_models import (
|
488
|
+
NextStep,
|
489
|
+
AgentTask,
|
490
|
+
)
|
491
|
+
|
363
492
|
params = self.get_request_params(request_params)
|
364
493
|
params = params.model_copy(update={"use_history": False})
|
365
494
|
|
495
|
+
# Format agents without numeric prefixes for cleaner XML
|
496
|
+
# FIX: Iterate over agent names instead of agent objects
|
366
497
|
agents = "\n".join(
|
367
|
-
[
|
368
|
-
f"{idx}. {self._format_agent_info(agent)}"
|
369
|
-
for idx, agent in enumerate(self.agents, 1)
|
370
|
-
]
|
498
|
+
[self._format_agent_info(agent_name) for agent_name in self.agents.keys()]
|
371
499
|
)
|
372
500
|
|
501
|
+
# Create clear plan status indicator for the template
|
502
|
+
plan_status = "Plan Status: Not Started"
|
503
|
+
if hasattr(plan_result, "is_complete"):
|
504
|
+
plan_status = (
|
505
|
+
"Plan Status: Complete"
|
506
|
+
if plan_result.is_complete
|
507
|
+
else "Plan Status: In Progress"
|
508
|
+
)
|
509
|
+
|
373
510
|
prompt = ITERATIVE_PLAN_PROMPT_TEMPLATE.format(
|
374
511
|
objective=objective,
|
375
512
|
plan_result=format_plan_result(plan_result),
|
513
|
+
plan_status=plan_status,
|
376
514
|
agents=agents,
|
377
515
|
)
|
378
516
|
|
379
|
-
#
|
380
|
-
|
517
|
+
# Get raw JSON response from LLM
|
518
|
+
result_str = await self.planner.generate_str(
|
381
519
|
message=prompt,
|
382
|
-
response_model=NextStep,
|
383
520
|
request_params=params,
|
384
521
|
)
|
385
|
-
|
522
|
+
|
523
|
+
try:
|
524
|
+
# Parse JSON directly
|
525
|
+
data = json.loads(result_str)
|
526
|
+
|
527
|
+
# Create task objects manually to preserve exact agent names
|
528
|
+
tasks = []
|
529
|
+
for task_data in data.get("tasks", []):
|
530
|
+
# Preserve the exact agent name as specified in the JSON
|
531
|
+
task = AgentTask(
|
532
|
+
description=task_data.get("description", ""),
|
533
|
+
agent=task_data.get("agent", ""),
|
534
|
+
)
|
535
|
+
tasks.append(task)
|
536
|
+
|
537
|
+
# Create step with manually constructed tasks
|
538
|
+
next_step = NextStep(
|
539
|
+
description=data.get("description", ""),
|
540
|
+
tasks=tasks,
|
541
|
+
is_complete=data.get("is_complete", False),
|
542
|
+
)
|
543
|
+
|
544
|
+
return next_step
|
545
|
+
|
546
|
+
except (json.JSONDecodeError, ValidationError, KeyError) as e:
|
547
|
+
# Log detailed error and fall back to the original method
|
548
|
+
self.logger.error(f"Error parsing next step JSON: {str(e)}")
|
549
|
+
self.logger.debug(f"Failed JSON content: {result_str}")
|
550
|
+
|
551
|
+
# Use the normal structured parsing as fallback
|
552
|
+
next_step = await self.planner.generate_structured(
|
553
|
+
message=result_str,
|
554
|
+
response_model=NextStep,
|
555
|
+
request_params=params,
|
556
|
+
)
|
557
|
+
|
558
|
+
return next_step
|
386
559
|
|
387
560
|
def _format_server_info(self, server_name: str) -> str:
|
388
|
-
"""Format server information for display to planners"""
|
561
|
+
"""Format server information for display to planners using XML tags"""
|
562
|
+
from mcp_agent.workflows.llm.prompt_utils import format_server_info
|
563
|
+
|
389
564
|
server_config = self.server_registry.get_server_config(server_name)
|
390
|
-
server_str = f"Server Name: {server_name}"
|
391
|
-
if not server_config:
|
392
|
-
return server_str
|
393
565
|
|
394
|
-
description
|
395
|
-
|
396
|
-
|
566
|
+
# Get description or empty string if not available
|
567
|
+
description = ""
|
568
|
+
if server_config and server_config.description:
|
569
|
+
description = server_config.description
|
570
|
+
|
571
|
+
return format_server_info(server_name, description)
|
572
|
+
|
573
|
+
def _validate_agent_names(self, plan: Plan) -> None:
|
574
|
+
"""
|
575
|
+
Validate all agent names in a plan before execution.
|
576
|
+
This helps catch invalid agent references early.
|
577
|
+
"""
|
578
|
+
invalid_agents = []
|
579
|
+
|
580
|
+
for step in plan.steps:
|
581
|
+
for task in step.tasks:
|
582
|
+
if task.agent not in self.agents:
|
583
|
+
invalid_agents.append(task.agent)
|
397
584
|
|
398
|
-
|
585
|
+
if invalid_agents:
|
586
|
+
available_agents = ", ".join(self.agents.keys())
|
587
|
+
invalid_list = ", ".join(invalid_agents)
|
588
|
+
error_msg = f"Plan contains invalid agent names: {invalid_list}. Available agents: {available_agents}"
|
589
|
+
self.logger.error(error_msg)
|
590
|
+
# We don't raise an exception here as the execution will handle invalid agents
|
591
|
+
# by logging errors for individual tasks
|
399
592
|
|
400
593
|
def _format_agent_info(self, agent_name: str) -> str:
|
401
|
-
"""Format Agent information for display to planners"""
|
594
|
+
"""Format Agent information for display to planners using XML tags"""
|
595
|
+
from mcp_agent.workflows.llm.prompt_utils import format_agent_info
|
596
|
+
|
402
597
|
agent = self.agents.get(agent_name)
|
403
598
|
if not agent:
|
599
|
+
self.logger.error(f"Agent '{agent_name}' not found in orchestrator agents")
|
404
600
|
return ""
|
601
|
+
instruction = agent.instruction
|
405
602
|
|
406
|
-
servers
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
603
|
+
# Get servers information
|
604
|
+
server_info = []
|
605
|
+
for server_name in agent.server_names:
|
606
|
+
server_config = self.server_registry.get_server_config(server_name)
|
607
|
+
description = ""
|
608
|
+
if server_config and server_config.description:
|
609
|
+
description = server_config.description
|
610
|
+
|
611
|
+
server_info.append({"name": server_name, "description": description})
|
412
612
|
|
413
|
-
return
|
613
|
+
return format_agent_info(
|
614
|
+
agent.name, instruction, server_info if server_info else None
|
615
|
+
)
|
@@ -62,6 +62,10 @@ class TaskWithResult(Task):
|
|
62
62
|
description="Result of executing the task", default="Task completed"
|
63
63
|
)
|
64
64
|
|
65
|
+
agent: str = Field(
|
66
|
+
description="Name of the agent that executed this task", default=""
|
67
|
+
)
|
68
|
+
|
65
69
|
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
|
66
70
|
|
67
71
|
|
@@ -116,17 +120,17 @@ class NextStep(Step):
|
|
116
120
|
)
|
117
121
|
|
118
122
|
|
119
|
-
def
|
120
|
-
"""Format a task result for display
|
123
|
+
def format_task_result_text(task_result: TaskWithResult) -> str:
|
124
|
+
"""Format a task result as plain text for display"""
|
121
125
|
return TASK_RESULT_TEMPLATE.format(
|
122
126
|
task_description=task_result.description, task_result=task_result.result
|
123
127
|
)
|
124
128
|
|
125
129
|
|
126
|
-
def
|
127
|
-
"""Format a step result for display
|
130
|
+
def format_step_result_text(step_result: StepResult) -> str:
|
131
|
+
"""Format a step result as plain text for display"""
|
128
132
|
tasks_str = "\n".join(
|
129
|
-
f" - {
|
133
|
+
f" - {format_task_result_text(task)}" for task in step_result.task_results
|
130
134
|
)
|
131
135
|
return STEP_RESULT_TEMPLATE.format(
|
132
136
|
step_description=step_result.step.description,
|
@@ -135,11 +139,11 @@ def format_step_result(step_result: StepResult) -> str:
|
|
135
139
|
)
|
136
140
|
|
137
141
|
|
138
|
-
def
|
139
|
-
"""Format the full plan execution state for display
|
142
|
+
def format_plan_result_text(plan_result: PlanResult) -> str:
|
143
|
+
"""Format the full plan execution state as plain text for display"""
|
140
144
|
steps_str = (
|
141
145
|
"\n\n".join(
|
142
|
-
f"{i + 1}:\n{
|
146
|
+
f"{i + 1}:\n{format_step_result_text(step)}"
|
143
147
|
for i, step in enumerate(plan_result.step_results)
|
144
148
|
)
|
145
149
|
if plan_result.step_results
|
@@ -149,6 +153,74 @@ def format_plan_result(plan_result: PlanResult) -> str:
|
|
149
153
|
return PLAN_RESULT_TEMPLATE.format(
|
150
154
|
plan_objective=plan_result.objective,
|
151
155
|
steps_str=steps_str,
|
152
|
-
plan_status="Complete" if plan_result.is_complete else "In Progress",
|
153
156
|
plan_result=plan_result.result if plan_result.is_complete else "In Progress",
|
154
157
|
)
|
158
|
+
|
159
|
+
|
160
|
+
def format_task_result_xml(task_result: TaskWithResult) -> str:
|
161
|
+
"""Format a task result with XML tags for better semantic understanding"""
|
162
|
+
from mcp_agent.workflows.llm.prompt_utils import format_fastagent_tag
|
163
|
+
|
164
|
+
return format_fastagent_tag(
|
165
|
+
"task-result",
|
166
|
+
f"\n<fastagent:description>{task_result.description}</fastagent:description>\n"
|
167
|
+
f"<fastagent:result>{task_result.result}</fastagent:result>\n",
|
168
|
+
{
|
169
|
+
"description": task_result.description[:50] + "..."
|
170
|
+
if len(task_result.description) > 50
|
171
|
+
else task_result.description
|
172
|
+
},
|
173
|
+
)
|
174
|
+
|
175
|
+
|
176
|
+
def format_step_result_xml(step_result: StepResult) -> str:
|
177
|
+
"""Format a step result with XML tags for better semantic understanding"""
|
178
|
+
from mcp_agent.workflows.llm.prompt_utils import format_fastagent_tag
|
179
|
+
|
180
|
+
# Format each task result with XML
|
181
|
+
task_results = []
|
182
|
+
for task in step_result.task_results:
|
183
|
+
task_results.append(format_task_result_xml(task))
|
184
|
+
|
185
|
+
# Combine task results
|
186
|
+
task_results_str = "\n".join(task_results)
|
187
|
+
|
188
|
+
# Build step result with metadata and tasks
|
189
|
+
step_content = (
|
190
|
+
f"<fastagent:description>{step_result.step.description}</fastagent:description>\n"
|
191
|
+
f"<fastagent:summary>{step_result.result}</fastagent:summary>\n"
|
192
|
+
f"<fastagent:task-results>\n{task_results_str}\n</fastagent:task-results>\n"
|
193
|
+
)
|
194
|
+
|
195
|
+
return format_fastagent_tag("step-result", step_content)
|
196
|
+
|
197
|
+
|
198
|
+
def format_plan_result(plan_result: PlanResult) -> str:
|
199
|
+
"""Format the full plan execution state with XML for better semantic understanding"""
|
200
|
+
from mcp_agent.workflows.llm.prompt_utils import format_fastagent_tag
|
201
|
+
|
202
|
+
# Format objective
|
203
|
+
objective_tag = format_fastagent_tag("objective", plan_result.objective)
|
204
|
+
|
205
|
+
# Format step results
|
206
|
+
step_results = []
|
207
|
+
for step in plan_result.step_results:
|
208
|
+
step_results.append(format_step_result_xml(step))
|
209
|
+
|
210
|
+
# Build progress section
|
211
|
+
if step_results:
|
212
|
+
steps_content = "\n".join(step_results)
|
213
|
+
progress_content = (
|
214
|
+
f"{objective_tag}\n"
|
215
|
+
f"<fastagent:steps>\n{steps_content}\n</fastagent:steps>\n"
|
216
|
+
f"<fastagent:status>{plan_result.result if plan_result.is_complete else 'In Progress'}</fastagent:status>\n"
|
217
|
+
)
|
218
|
+
else:
|
219
|
+
# No steps executed yet
|
220
|
+
progress_content = (
|
221
|
+
f"{objective_tag}\n"
|
222
|
+
f"<fastagent:steps>No steps executed yet</fastagent:steps>\n"
|
223
|
+
f"<fastagent:status>Not Started</fastagent:status>\n"
|
224
|
+
)
|
225
|
+
|
226
|
+
return format_fastagent_tag("progress", progress_content)
|