mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-03 19:35:15 -05:00
fix: include graph schemas for marketplace agents in Agent Generator
When marketplace agents are included in the library_agents payload to the Agent Generator service, they were missing required fields (graph_id, graph_version, input_schema, output_schema), causing Pydantic validation to fail with HTTP 422. Changes: - Add agent_graph_id field to StoreAgent model - Include agentGraphId in hybrid search SQL query - Update search_marketplace_agents_for_generation to fetch full graph schemas for marketplace agents - Marketplace agents now return LibraryAgentSummary (unified type) with complete schemas for sub-agent composition - Update deduplication logic to use graph_id instead of name This fixes agent creation failures on dev where the decompose-description endpoint was returning 422 Unprocessable Entity. Fixes: SECRT-1817
This commit is contained in:
@@ -7,6 +7,7 @@ from typing import Any, NotRequired, TypedDict
|
||||
|
||||
from backend.api.features.library import db as library_db
|
||||
from backend.api.features.store import db as store_db
|
||||
from backend.api.features.store import model as store_model
|
||||
from backend.data.graph import (
|
||||
Graph,
|
||||
Link,
|
||||
@@ -266,18 +267,18 @@ async def get_library_agents_for_generation(
|
||||
async def search_marketplace_agents_for_generation(
|
||||
search_query: str,
|
||||
max_results: int = 10,
|
||||
) -> list[MarketplaceAgentSummary]:
|
||||
) -> list[LibraryAgentSummary]:
|
||||
"""Search marketplace agents formatted for Agent Generator.
|
||||
|
||||
Note: This returns basic agent info. Full input/output schemas would require
|
||||
additional graph fetches and is a potential future enhancement.
|
||||
Fetches marketplace agents and their full schemas so they can be used
|
||||
as sub-agents in generated workflows.
|
||||
|
||||
Args:
|
||||
search_query: Search term to find relevant public agents
|
||||
max_results: Maximum number of agents to return (default 10)
|
||||
|
||||
Returns:
|
||||
List of MarketplaceAgentSummary (without detailed schemas for now)
|
||||
List of LibraryAgentSummary with full input/output schemas
|
||||
"""
|
||||
try:
|
||||
response = await store_db.get_store_agents(
|
||||
@@ -286,18 +287,49 @@ async def search_marketplace_agents_for_generation(
|
||||
page_size=max_results,
|
||||
)
|
||||
|
||||
results: list[MarketplaceAgentSummary] = []
|
||||
for agent in response.agents:
|
||||
results.append(
|
||||
MarketplaceAgentSummary(
|
||||
name=agent.agent_name,
|
||||
description=agent.description,
|
||||
sub_heading=agent.sub_heading,
|
||||
creator=agent.creator,
|
||||
is_marketplace_agent=True,
|
||||
# Filter to agents that have a graph ID
|
||||
agents_with_graphs = [
|
||||
agent for agent in response.agents if agent.agent_graph_id
|
||||
]
|
||||
|
||||
if not agents_with_graphs:
|
||||
return []
|
||||
|
||||
# Batch-fetch graphs to get input/output schemas
|
||||
# Use get_graph with user_id=None for public marketplace graphs
|
||||
import asyncio
|
||||
|
||||
async def fetch_graph_schema(
|
||||
agent: store_model.StoreAgent,
|
||||
) -> LibraryAgentSummary | None:
|
||||
try:
|
||||
graph = await get_graph(
|
||||
graph_id=agent.agent_graph_id, # type: ignore
|
||||
version=None, # Get active version
|
||||
user_id=None, # Public graph
|
||||
)
|
||||
)
|
||||
return results
|
||||
if graph:
|
||||
return LibraryAgentSummary(
|
||||
graph_id=graph.id,
|
||||
graph_version=graph.version,
|
||||
name=agent.agent_name,
|
||||
description=agent.description,
|
||||
input_schema=graph.input_schema,
|
||||
output_schema=graph.output_schema,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug(
|
||||
f"Failed to fetch schema for marketplace agent {agent.agent_name}: {e}"
|
||||
)
|
||||
return None
|
||||
|
||||
# Fetch all schemas concurrently
|
||||
results = await asyncio.gather(
|
||||
*[fetch_graph_schema(agent) for agent in agents_with_graphs]
|
||||
)
|
||||
|
||||
# Filter out None results
|
||||
return [r for r in results if r is not None]
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to search marketplace agents: {e}")
|
||||
return []
|
||||
@@ -327,8 +359,7 @@ async def get_all_relevant_agents_for_generation(
|
||||
max_marketplace_results: Max marketplace agents to return (default 10)
|
||||
|
||||
Returns:
|
||||
List of AgentSummary, library agents first (with full schemas),
|
||||
then marketplace agents (basic info only)
|
||||
List of AgentSummary with full schemas (both library and marketplace agents)
|
||||
"""
|
||||
agents: list[AgentSummary] = []
|
||||
seen_graph_ids: set[str] = set()
|
||||
@@ -365,16 +396,12 @@ async def get_all_relevant_agents_for_generation(
|
||||
search_query=search_query,
|
||||
max_results=max_marketplace_results,
|
||||
)
|
||||
library_names: set[str] = set()
|
||||
for a in agents:
|
||||
name = a.get("name")
|
||||
if name and isinstance(name, str):
|
||||
library_names.add(name.lower())
|
||||
# Deduplicate by graph_id (marketplace agents now have full schemas)
|
||||
for agent in marketplace_agents:
|
||||
agent_name = agent.get("name")
|
||||
if agent_name and isinstance(agent_name, str):
|
||||
if agent_name.lower() not in library_names:
|
||||
agents.append(agent)
|
||||
graph_id = agent.get("graph_id")
|
||||
if graph_id and graph_id not in seen_graph_ids:
|
||||
agents.append(agent)
|
||||
seen_graph_ids.add(graph_id)
|
||||
|
||||
return agents
|
||||
|
||||
|
||||
@@ -112,6 +112,7 @@ async def get_store_agents(
|
||||
description=agent["description"],
|
||||
runs=agent["runs"],
|
||||
rating=agent["rating"],
|
||||
agent_graph_id=agent.get("agentGraphId"),
|
||||
)
|
||||
store_agents.append(store_agent)
|
||||
except Exception as e:
|
||||
@@ -170,6 +171,7 @@ async def get_store_agents(
|
||||
description=agent.description,
|
||||
runs=agent.runs,
|
||||
rating=agent.rating,
|
||||
agent_graph_id=agent.agentGraphId,
|
||||
)
|
||||
# Add to the list only if creation was successful
|
||||
store_agents.append(store_agent)
|
||||
|
||||
@@ -600,6 +600,7 @@ async def hybrid_search(
|
||||
sa.featured,
|
||||
sa.is_available,
|
||||
sa.updated_at,
|
||||
sa."agentGraphId",
|
||||
-- Searchable text for BM25 reranking
|
||||
COALESCE(sa.agent_name, '') || ' ' || COALESCE(sa.sub_heading, '') || ' ' || COALESCE(sa.description, '') as searchable_text,
|
||||
-- Semantic score
|
||||
@@ -659,6 +660,7 @@ async def hybrid_search(
|
||||
featured,
|
||||
is_available,
|
||||
updated_at,
|
||||
"agentGraphId",
|
||||
searchable_text,
|
||||
semantic_score,
|
||||
lexical_score,
|
||||
|
||||
@@ -38,6 +38,7 @@ class StoreAgent(pydantic.BaseModel):
|
||||
description: str
|
||||
runs: int
|
||||
rating: float
|
||||
agent_graph_id: str | None = None # Graph ID for sub-agent composition
|
||||
|
||||
|
||||
class StoreAgentsResponse(pydantic.BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user