mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
## Summary Port the agent generation pipeline from the external AgentGenerator service into local copilot tools, making the Claude Agent SDK itself handle validation, fixing, and block recommendation — no separate inner LLM calls needed. Key capabilities: - **Local agent generation**: Create, edit, and customize agents entirely within the SDK session - **Graph validation**: 9 validation checks (block existence, link references, type compatibility, IO blocks, etc.) - **Graph fixing**: 17+ auto-fix methods (ID repair, link rewiring, type conversion, credential stripping, dynamic block sink names, etc.) - **MCP tool blocks**: Guide and fixer support for MCPToolBlock nodes with proper dynamic input schema handling - **Sub-agent composition**: AgentExecutorBlock support with library agent schema enrichment - **Embedding fallback**: Falls back to OpenRouter for embeddings when `openai_internal_api_key` is unavailable - **Actionable error messages**: Excluded block types (MCP, Agent) return specific hints redirecting to the correct tool ### New Tools - `validate_agent_graph` — run 9 validation checks on agent JSON - `fix_agent_graph` — apply 17+ auto-fixes to agent JSON - `get_blocks_for_goal` — recommend blocks for a given goal (with optimized descriptions) ### Refactored Tools - `create_agent`, `edit_agent`, `customize_agent` — accept `agent_json` for local generation with shared fix→validate→save pipeline - `find_block` — added `include_schemas` parameter, excludes MCP/Agent blocks with actionable hints - `run_block` — actionable error messages for excluded block types - `find_library_agent` — enriched with `graph_version`, `input_schema`, `output_schema` for sub-agent composition ### Architecture - Split 2,558-line `validation.py` into `fixer.py`, `validator.py`, `helpers.py`, `pipeline.py` - Extracted shared `fix_validate_and_save()` pipeline (was duplicated across 3 tools) - Shared `OPENROUTER_BASE_URL` constant across codebase - Comprehensive test coverage: 78+ unit tests for fixer/validator, 8 run_block tests, 17 SDK compat tests ## Test plan - [x] `poetry run format` passes - [x] `poetry run pytest -s -vvv backend/copilot/` — all tests pass - [x] CI green on all Python versions (3.11, 3.12, 3.13) - [x] Manual E2E: copilot generates agents with correct IO blocks, links, and node structure - [x] Manual E2E: MCP tool blocks use bare field names for dynamic inputs - [x] Manual E2E: sub-agent composition with AgentExecutorBlock
135 lines
4.7 KiB
Python
135 lines
4.7 KiB
Python
"""FixAgentGraphTool - Auto-fixes common agent JSON issues."""
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
from backend.copilot.model import ChatSession
|
|
|
|
from .agent_generator.validation import AgentFixer, AgentValidator, get_blocks_as_dicts
|
|
from .base import BaseTool
|
|
from .models import ErrorResponse, FixResultResponse, ToolResponseBase
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class FixAgentGraphTool(BaseTool):
|
|
"""Tool for auto-fixing common issues in agent JSON graphs."""
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "fix_agent_graph"
|
|
|
|
@property
|
|
def description(self) -> str:
|
|
return (
|
|
"Auto-fix common issues in an agent JSON graph. Applies fixes for:\n"
|
|
"- Missing or invalid UUIDs on nodes and links\n"
|
|
"- StoreValueBlock prerequisites for ConditionBlock\n"
|
|
"- Double curly brace escaping in prompt templates\n"
|
|
"- AddToList/AddToDictionary prerequisite blocks\n"
|
|
"- CodeExecutionBlock output field naming\n"
|
|
"- Missing credentials configuration\n"
|
|
"- Node X coordinate spacing (800+ units apart)\n"
|
|
"- AI model default parameters\n"
|
|
"- Link static properties based on input schema\n"
|
|
"- Type mismatches (inserts conversion blocks)\n\n"
|
|
"Returns the fixed agent JSON plus a list of fixes applied. "
|
|
"After fixing, the agent is re-validated. If still invalid, "
|
|
"the remaining errors are included in the response."
|
|
)
|
|
|
|
@property
|
|
def requires_auth(self) -> bool:
|
|
return False
|
|
|
|
@property
|
|
def parameters(self) -> dict[str, Any]:
|
|
return {
|
|
"type": "object",
|
|
"properties": {
|
|
"agent_json": {
|
|
"type": "object",
|
|
"description": (
|
|
"The agent JSON to fix. Must contain 'nodes' and 'links' arrays."
|
|
),
|
|
},
|
|
},
|
|
"required": ["agent_json"],
|
|
}
|
|
|
|
async def _execute(
|
|
self,
|
|
user_id: str | None,
|
|
session: ChatSession,
|
|
**kwargs,
|
|
) -> ToolResponseBase:
|
|
agent_json = kwargs.get("agent_json")
|
|
session_id = session.session_id if session else None
|
|
|
|
if not agent_json or not isinstance(agent_json, dict):
|
|
return ErrorResponse(
|
|
message="Please provide a valid agent JSON object.",
|
|
error="Missing or invalid agent_json parameter",
|
|
session_id=session_id,
|
|
)
|
|
|
|
nodes = agent_json.get("nodes", [])
|
|
|
|
if not nodes:
|
|
return ErrorResponse(
|
|
message="The agent JSON has no nodes. An agent needs at least one block.",
|
|
error="empty_agent",
|
|
session_id=session_id,
|
|
)
|
|
|
|
try:
|
|
blocks = get_blocks_as_dicts()
|
|
fixer = AgentFixer()
|
|
fixed_agent = fixer.apply_all_fixes(agent_json, blocks)
|
|
fixes_applied = fixer.get_fixes_applied()
|
|
except Exception as e:
|
|
logger.error(f"Fixer error: {e}", exc_info=True)
|
|
return ErrorResponse(
|
|
message=f"Auto-fix encountered an error: {str(e)}",
|
|
error="fix_exception",
|
|
session_id=session_id,
|
|
)
|
|
|
|
# Re-validate after fixing
|
|
try:
|
|
validator = AgentValidator()
|
|
is_valid, _ = validator.validate(fixed_agent, blocks)
|
|
remaining_errors = validator.errors if not is_valid else []
|
|
except Exception as e:
|
|
logger.warning(f"Post-fix validation error: {e}", exc_info=True)
|
|
remaining_errors = [f"Post-fix validation failed: {str(e)}"]
|
|
is_valid = False
|
|
|
|
if is_valid:
|
|
return FixResultResponse(
|
|
message=(
|
|
f"Applied {len(fixes_applied)} fix(es). "
|
|
"Agent graph is now valid!"
|
|
),
|
|
fixed_agent_json=fixed_agent,
|
|
fixes_applied=fixes_applied,
|
|
fix_count=len(fixes_applied),
|
|
valid_after_fix=True,
|
|
remaining_errors=[],
|
|
session_id=session_id,
|
|
)
|
|
|
|
return FixResultResponse(
|
|
message=(
|
|
f"Applied {len(fixes_applied)} fix(es), but "
|
|
f"{len(remaining_errors)} issue(s) remain. "
|
|
"Review the remaining errors and fix manually."
|
|
),
|
|
fixed_agent_json=fixed_agent,
|
|
fixes_applied=fixes_applied,
|
|
fix_count=len(fixes_applied),
|
|
valid_after_fix=False,
|
|
remaining_errors=remaining_errors,
|
|
session_id=session_id,
|
|
)
|