chore: update environment variables and improve agent configuration
This commit is contained in:
parent
0a27670de5
commit
13a6247780
4
.env
4
.env
@ -1,6 +1,10 @@
|
|||||||
API_TITLE="Evo API"
|
API_TITLE="Evo API"
|
||||||
API_DESCRIPTION="API para execução de agentes de IA"
|
API_DESCRIPTION="API para execução de agentes de IA"
|
||||||
API_VERSION="1.0.0"
|
API_VERSION="1.0.0"
|
||||||
|
API_URL="http://localhost:8000"
|
||||||
|
|
||||||
|
ORGANIZATION_NAME="Evo AI"
|
||||||
|
ORGANIZATION_URL="https://evoai.evoapicloud.com"
|
||||||
|
|
||||||
# Configurações do banco de dados
|
# Configurações do banco de dados
|
||||||
POSTGRES_CONNECTION_STRING="postgresql://postgres:root@localhost:5432/evo_ai"
|
POSTGRES_CONNECTION_STRING="postgresql://postgres:root@localhost:5432/evo_ai"
|
||||||
|
@ -1,3 +1,11 @@
|
|||||||
|
API_TITLE="Evo API"
|
||||||
|
API_DESCRIPTION="API para execução de agentes de IA"
|
||||||
|
API_VERSION="1.0.0"
|
||||||
|
API_URL="http://localhost:8000"
|
||||||
|
|
||||||
|
ORGANIZATION_NAME="Evo AI"
|
||||||
|
ORGANIZATION_URL="https://evoai.evoapicloud.com"
|
||||||
|
|
||||||
# Database settings
|
# Database settings
|
||||||
POSTGRES_CONNECTION_STRING="postgresql://postgres:root@localhost:5432/evo_ai"
|
POSTGRES_CONNECTION_STRING="postgresql://postgres:root@localhost:5432/evo_ai"
|
||||||
|
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -126,4 +126,6 @@ celerybeat-schedule
|
|||||||
*.swo
|
*.swo
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
uv.lock
|
||||||
|
2
Makefile
2
Makefile
@ -18,7 +18,7 @@ alembic-downgrade:
|
|||||||
|
|
||||||
# Command to run the server
|
# Command to run the server
|
||||||
run:
|
run:
|
||||||
uvicorn src.main:app --reload --host 0.0.0.0 --port 8000 --reload-dir src
|
uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload --env-file .env
|
||||||
|
|
||||||
# Command to run the server in production mode
|
# Command to run the server in production mode
|
||||||
run-prod:
|
run-prod:
|
||||||
|
54
README.md
54
README.md
@ -12,6 +12,7 @@ The Evo AI platform allows:
|
|||||||
- MCP server configuration
|
- MCP server configuration
|
||||||
- Custom tools management
|
- Custom tools management
|
||||||
- JWT authentication with email verification
|
- JWT authentication with email verification
|
||||||
|
- **Agent 2 Agent (A2A) Protocol Support**: Interoperability between AI agents following Google's A2A specification
|
||||||
|
|
||||||
## 🛠️ Technologies
|
## 🛠️ Technologies
|
||||||
|
|
||||||
@ -27,6 +28,59 @@ The Evo AI platform allows:
|
|||||||
- **Jinja2**: Template engine for email rendering
|
- **Jinja2**: Template engine for email rendering
|
||||||
- **Bcrypt**: Password hashing and security
|
- **Bcrypt**: Password hashing and security
|
||||||
|
|
||||||
|
## 🤖 Agent 2 Agent (A2A) Protocol Support
|
||||||
|
|
||||||
|
Evo AI implements the Google's Agent 2 Agent (A2A) protocol, enabling seamless communication and interoperability between AI agents. This implementation includes:
|
||||||
|
|
||||||
|
### Key Features
|
||||||
|
|
||||||
|
- **Standardized Communication**: Agents can communicate using a common protocol regardless of their underlying implementation
|
||||||
|
- **Interoperability**: Support for agents built with different frameworks and technologies
|
||||||
|
- **Well-Known Endpoints**: Standardized endpoints for agent discovery and interaction
|
||||||
|
- **Task Management**: Support for task-based interactions between agents
|
||||||
|
- **State Management**: Tracking of agent states and conversation history
|
||||||
|
- **Authentication**: Secure API key-based authentication for agent interactions
|
||||||
|
|
||||||
|
### Implementation Details
|
||||||
|
|
||||||
|
- **Agent Card**: Each agent exposes a `.well-known/agent.json` endpoint with its capabilities and configuration
|
||||||
|
- **Task Handling**: Support for task creation, execution, and status tracking
|
||||||
|
- **Message Format**: Standardized message format for agent communication
|
||||||
|
- **History Tracking**: Maintains conversation history between agents
|
||||||
|
- **Artifact Management**: Support for handling different types of artifacts (text, files, etc.)
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
|
||||||
|
```json
|
||||||
|
// Agent Card Example
|
||||||
|
{
|
||||||
|
"name": "My Agent",
|
||||||
|
"description": "A helpful AI assistant",
|
||||||
|
"url": "https://api.example.com/agents/123",
|
||||||
|
"capabilities": {
|
||||||
|
"streaming": false,
|
||||||
|
"pushNotifications": false,
|
||||||
|
"stateTransitionHistory": true
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"schemes": ["apiKey"],
|
||||||
|
"credentials": {
|
||||||
|
"in": "header",
|
||||||
|
"name": "x-api-key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
{
|
||||||
|
"id": "search",
|
||||||
|
"name": "Web Search",
|
||||||
|
"description": "Search the web for information"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information about the A2A protocol, visit [Google's A2A Protocol Documentation](https://google.github.io/A2A/).
|
||||||
|
|
||||||
## 📁 Project Structure
|
## 📁 Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -40,6 +40,7 @@ dependencies = [
|
|||||||
"fastapi_utils==0.8.0",
|
"fastapi_utils==0.8.0",
|
||||||
"bcrypt==4.3.0",
|
"bcrypt==4.3.0",
|
||||||
"jinja2==3.1.6",
|
"jinja2==3.1.6",
|
||||||
|
"pydantic[email]==2.11.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
@ -17,43 +17,52 @@ from dotenv import load_dotenv
|
|||||||
from src.models.models import Agent, Client, User
|
from src.models.models import Agent, Client, User
|
||||||
|
|
||||||
# Configurar logging
|
# Configurar logging
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
logging.basicConfig(
|
||||||
|
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
||||||
|
)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def create_demo_agents():
|
def create_demo_agents():
|
||||||
"""Create example agents for the demo client"""
|
"""Create example agents for the demo client"""
|
||||||
try:
|
try:
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
# Get database settings
|
# Get database settings
|
||||||
db_url = os.getenv("POSTGRES_CONNECTION_STRING")
|
db_url = os.getenv("POSTGRES_CONNECTION_STRING")
|
||||||
if not db_url:
|
if not db_url:
|
||||||
logger.error("Environment variable POSTGRES_CONNECTION_STRING not defined")
|
logger.error("Environment variable POSTGRES_CONNECTION_STRING not defined")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Connect to the database
|
# Connect to the database
|
||||||
engine = create_engine(db_url)
|
engine = create_engine(db_url)
|
||||||
Session = sessionmaker(bind=engine)
|
Session = sessionmaker(bind=engine)
|
||||||
session = Session()
|
session = Session()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Obter o cliente demo pelo email do usuário
|
# Obter o cliente demo pelo email do usuário
|
||||||
demo_email = os.getenv("DEMO_EMAIL", "demo@exemplo.com")
|
demo_email = os.getenv("DEMO_EMAIL", "demo@exemplo.com")
|
||||||
demo_user = session.query(User).filter(User.email == demo_email).first()
|
demo_user = session.query(User).filter(User.email == demo_email).first()
|
||||||
|
|
||||||
if not demo_user or not demo_user.client_id:
|
if not demo_user or not demo_user.client_id:
|
||||||
logger.error(f"Demo user not found or not associated with a client: {demo_email}")
|
logger.error(
|
||||||
|
f"Demo user not found or not associated with a client: {demo_email}"
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
client_id = demo_user.client_id
|
client_id = demo_user.client_id
|
||||||
|
|
||||||
# Verificar se já existem agentes para este cliente
|
# Verificar se já existem agentes para este cliente
|
||||||
existing_agents = session.query(Agent).filter(Agent.client_id == client_id).all()
|
existing_agents = (
|
||||||
|
session.query(Agent).filter(Agent.client_id == client_id).all()
|
||||||
|
)
|
||||||
if existing_agents:
|
if existing_agents:
|
||||||
logger.info(f"There are already {len(existing_agents)} agents for the client {client_id}")
|
logger.info(
|
||||||
|
f"There are already {len(existing_agents)} agents for the client {client_id}"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Example agent definitions
|
# Example agent definitions
|
||||||
agents = [
|
agents = [
|
||||||
{
|
{
|
||||||
@ -69,11 +78,12 @@ def create_demo_agents():
|
|||||||
inform that you will consult a specialist and return soon.
|
inform that you will consult a specialist and return soon.
|
||||||
""",
|
""",
|
||||||
"config": {
|
"config": {
|
||||||
|
"api_key": uuid.uuid4(),
|
||||||
"tools": [],
|
"tools": [],
|
||||||
"mcp_servers": [],
|
"mcp_servers": [],
|
||||||
"custom_tools": [],
|
"custom_tools": [],
|
||||||
"sub_agents": []
|
"sub_agents": [],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Sales_Products",
|
"name": "Sales_Products",
|
||||||
@ -89,11 +99,12 @@ def create_demo_agents():
|
|||||||
the customer's needs before recommending a product.
|
the customer's needs before recommending a product.
|
||||||
""",
|
""",
|
||||||
"config": {
|
"config": {
|
||||||
|
"api_key": uuid.uuid4(),
|
||||||
"tools": [],
|
"tools": [],
|
||||||
"mcp_servers": [],
|
"mcp_servers": [],
|
||||||
"custom_tools": [],
|
"custom_tools": [],
|
||||||
"sub_agents": []
|
"sub_agents": [],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "FAQ_Bot",
|
"name": "FAQ_Bot",
|
||||||
@ -109,14 +120,15 @@ def create_demo_agents():
|
|||||||
appropriate support channel.
|
appropriate support channel.
|
||||||
""",
|
""",
|
||||||
"config": {
|
"config": {
|
||||||
|
"api_key": uuid.uuid4(),
|
||||||
"tools": [],
|
"tools": [],
|
||||||
"mcp_servers": [],
|
"mcp_servers": [],
|
||||||
"custom_tools": [],
|
"custom_tools": [],
|
||||||
"sub_agents": []
|
"sub_agents": [],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
# Create the agents
|
# Create the agents
|
||||||
for agent_data in agents:
|
for agent_data in agents:
|
||||||
# Create the agent
|
# Create the agent
|
||||||
@ -128,27 +140,32 @@ def create_demo_agents():
|
|||||||
model=agent_data["model"],
|
model=agent_data["model"],
|
||||||
api_key=agent_data["api_key"],
|
api_key=agent_data["api_key"],
|
||||||
instruction=agent_data["instruction"].strip(),
|
instruction=agent_data["instruction"].strip(),
|
||||||
config=agent_data["config"]
|
config=agent_data["config"],
|
||||||
)
|
)
|
||||||
|
|
||||||
session.add(agent)
|
session.add(agent)
|
||||||
logger.info(f"Agent '{agent_data['name']}' created for the client {client_id}")
|
logger.info(
|
||||||
|
f"Agent '{agent_data['name']}' created for the client {client_id}"
|
||||||
|
)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
logger.info(f"All example agents were created successfully for the client {client_id}")
|
logger.info(
|
||||||
|
f"All example agents were created successfully for the client {client_id}"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
session.rollback()
|
session.rollback()
|
||||||
logger.error(f"Database error when creating example agents: {str(e)}")
|
logger.error(f"Database error when creating example agents: {str(e)}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error when creating example agents: {str(e)}")
|
logger.error(f"Error when creating example agents: {str(e)}")
|
||||||
return False
|
return False
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
success = create_demo_agents()
|
success = create_demo_agents()
|
||||||
sys.exit(0 if success else 1)
|
sys.exit(0 if success else 1)
|
||||||
|
@ -62,7 +62,20 @@ def create_mcp_servers():
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
"environments": {},
|
"environments": {},
|
||||||
"tools": ["sequentialthinking"],
|
"tools": [
|
||||||
|
{
|
||||||
|
"id": "sequentialthinking",
|
||||||
|
"name": "Sequential Thinking",
|
||||||
|
"description": "Helps organize thoughts and break down complex problems through a structured workflow",
|
||||||
|
"tags": ["thinking", "analysis", "problem-solving"],
|
||||||
|
"examples": [
|
||||||
|
"Help me analyze this problem",
|
||||||
|
"Guide me through this decision making process",
|
||||||
|
],
|
||||||
|
"inputModes": ["text"],
|
||||||
|
"outputModes": ["text"],
|
||||||
|
}
|
||||||
|
],
|
||||||
"type": "community",
|
"type": "community",
|
||||||
"id": "4519dd69-9343-4792-af51-dc4d322fb0c9",
|
"id": "4519dd69-9343-4792-af51-dc4d322fb0c9",
|
||||||
"created_at": "2025-04-28T15:14:16.901236Z",
|
"created_at": "2025-04-28T15:14:16.901236Z",
|
||||||
@ -76,87 +89,30 @@ def create_mcp_servers():
|
|||||||
},
|
},
|
||||||
"environments": {},
|
"environments": {},
|
||||||
"tools": [
|
"tools": [
|
||||||
"worker_list",
|
{
|
||||||
"worker_get",
|
"id": "worker_list",
|
||||||
"worker_put",
|
"name": "List Workers",
|
||||||
"worker_delete",
|
"description": "List all Cloudflare Workers in your account",
|
||||||
"worker_get_worker",
|
"tags": ["workers", "cloudflare"],
|
||||||
"worker_logs_by_worker_name",
|
"examples": [
|
||||||
"worker_logs_by_ray_id",
|
"List all my workers",
|
||||||
"worker_logs_keys",
|
"Show me my Cloudflare workers",
|
||||||
"get_kvs",
|
],
|
||||||
"kv_get",
|
"inputModes": ["text"],
|
||||||
"kv_put",
|
"outputModes": ["application/json"],
|
||||||
"kv_list",
|
},
|
||||||
"kv_delete",
|
{
|
||||||
"r2_list_buckets",
|
"id": "worker_get",
|
||||||
"r2_create_bucket",
|
"name": "Get Worker",
|
||||||
"r2_delete_bucket",
|
"description": "Get details of a specific Cloudflare Worker",
|
||||||
"r2_list_objects",
|
"tags": ["workers", "cloudflare"],
|
||||||
"r2_get_object",
|
"examples": [
|
||||||
"r2_put_object",
|
"Show me details of worker X",
|
||||||
"r2_delete_object",
|
"Get information about worker Y",
|
||||||
"d1_list_databases",
|
],
|
||||||
"d1_create_database",
|
"inputModes": ["text"],
|
||||||
"d1_delete_database",
|
"outputModes": ["application/json"],
|
||||||
"d1_query",
|
},
|
||||||
"durable_objects_list",
|
|
||||||
"durable_objects_create",
|
|
||||||
"durable_objects_delete",
|
|
||||||
"durable_objects_list_instances",
|
|
||||||
"durable_objects_get_instance",
|
|
||||||
"durable_objects_delete_instance",
|
|
||||||
"queues_list",
|
|
||||||
"queues_create",
|
|
||||||
"queues_delete",
|
|
||||||
"queues_get",
|
|
||||||
"queues_send_message",
|
|
||||||
"queues_get_messages",
|
|
||||||
"queues_update_consumer",
|
|
||||||
"workers_ai_list_models",
|
|
||||||
"workers_ai_get_model",
|
|
||||||
"workers_ai_run_inference",
|
|
||||||
"workers_ai_list_tasks",
|
|
||||||
"workflows_list",
|
|
||||||
"workflows_create",
|
|
||||||
"workflows_delete",
|
|
||||||
"workflows_get",
|
|
||||||
"workflows_update",
|
|
||||||
"workflows_execute",
|
|
||||||
"templates_list",
|
|
||||||
"templates_get",
|
|
||||||
"templates_create_from_template",
|
|
||||||
"w4p_list_dispatchers",
|
|
||||||
"w4p_create_dispatcher",
|
|
||||||
"w4p_delete_dispatcher",
|
|
||||||
"w4p_get_dispatcher",
|
|
||||||
"w4p_update_dispatcher",
|
|
||||||
"bindings_list",
|
|
||||||
"bindings_create",
|
|
||||||
"bindings_update",
|
|
||||||
"bindings_delete",
|
|
||||||
"routing_list_routes",
|
|
||||||
"routing_create_route",
|
|
||||||
"routing_update_route",
|
|
||||||
"routing_delete_route",
|
|
||||||
"cron_list",
|
|
||||||
"cron_create",
|
|
||||||
"cron_update",
|
|
||||||
"cron_delete",
|
|
||||||
"zones_list",
|
|
||||||
"zones_create",
|
|
||||||
"zones_delete",
|
|
||||||
"zones_get",
|
|
||||||
"zones_check_activation",
|
|
||||||
"secrets_list",
|
|
||||||
"secrets_put",
|
|
||||||
"secrets_delete",
|
|
||||||
"versions_list",
|
|
||||||
"versions_get",
|
|
||||||
"versions_rollback",
|
|
||||||
"wrangler_get_config",
|
|
||||||
"wrangler_update_config",
|
|
||||||
"analytics_get",
|
|
||||||
],
|
],
|
||||||
"type": "official",
|
"type": "official",
|
||||||
"id": "9138d1a2-24e6-4a75-87b0-bfa4932273e8",
|
"id": "9138d1a2-24e6-4a75-87b0-bfa4932273e8",
|
||||||
@ -172,7 +128,32 @@ def create_mcp_servers():
|
|||||||
"env": {"BRAVE_API_KEY": "env@@BRAVE_API_KEY"},
|
"env": {"BRAVE_API_KEY": "env@@BRAVE_API_KEY"},
|
||||||
},
|
},
|
||||||
"environments": {"BRAVE_API_KEY": "env@@BRAVE_API_KEY"},
|
"environments": {"BRAVE_API_KEY": "env@@BRAVE_API_KEY"},
|
||||||
"tools": ["brave_web_search", "brave_local_search"],
|
"tools": [
|
||||||
|
{
|
||||||
|
"id": "brave_web_search",
|
||||||
|
"name": "Web Search",
|
||||||
|
"description": "Perform web searches using Brave Search",
|
||||||
|
"tags": ["search", "web"],
|
||||||
|
"examples": [
|
||||||
|
"Search for Python documentation",
|
||||||
|
"Find information about AI",
|
||||||
|
],
|
||||||
|
"inputModes": ["text"],
|
||||||
|
"outputModes": ["application/json"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "brave_local_search",
|
||||||
|
"name": "Local Search",
|
||||||
|
"description": "Search for local businesses and places",
|
||||||
|
"tags": ["search", "local"],
|
||||||
|
"examples": [
|
||||||
|
"Find restaurants near me",
|
||||||
|
"Search for hotels in New York",
|
||||||
|
],
|
||||||
|
"inputModes": ["text"],
|
||||||
|
"outputModes": ["application/json"],
|
||||||
|
},
|
||||||
|
],
|
||||||
"type": "official",
|
"type": "official",
|
||||||
"id": "416c94d7-77f5-43f4-8181-aeb87934ecbf",
|
"id": "416c94d7-77f5-43f4-8181-aeb87934ecbf",
|
||||||
"created_at": "2025-04-28T15:20:07.647225Z",
|
"created_at": "2025-04-28T15:20:07.647225Z",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
from fastapi import APIRouter, Depends, HTTPException, status, Header, Request
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from src.config.database import get_db
|
from src.config.database import get_db
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
@ -13,12 +15,59 @@ from src.schemas.schemas import (
|
|||||||
)
|
)
|
||||||
from src.services import (
|
from src.services import (
|
||||||
agent_service,
|
agent_service,
|
||||||
|
mcp_server_service,
|
||||||
|
)
|
||||||
|
from src.services.agent_runner import run_agent
|
||||||
|
from src.services.service_providers import (
|
||||||
|
session_service,
|
||||||
|
artifacts_service,
|
||||||
|
memory_service,
|
||||||
)
|
)
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from src.services.session_service import get_session_events
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def format_agent_tools(
|
||||||
|
mcp_servers: List[Dict[str, Any]], db: Session
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""Format MCP server tools for agent card skills"""
|
||||||
|
formatted_tools = []
|
||||||
|
|
||||||
|
for server in mcp_servers:
|
||||||
|
try:
|
||||||
|
# Get the MCP server by ID
|
||||||
|
server_id = uuid.UUID(server["id"])
|
||||||
|
mcp_server = mcp_server_service.get_mcp_server(db, server_id)
|
||||||
|
|
||||||
|
if not mcp_server:
|
||||||
|
logger.warning(f"MCP server not found: {server_id}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Format each tool
|
||||||
|
for tool in mcp_server.tools:
|
||||||
|
formatted_tool = {
|
||||||
|
"id": tool["id"],
|
||||||
|
"name": tool["name"],
|
||||||
|
"description": tool["description"],
|
||||||
|
"tags": tool["tags"],
|
||||||
|
"examples": tool["examples"],
|
||||||
|
"inputModes": tool["inputModes"],
|
||||||
|
"outputModes": tool["outputModes"],
|
||||||
|
}
|
||||||
|
formatted_tools.append(formatted_tool)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(
|
||||||
|
f"Error formatting tools for MCP server {server.get('id')}: {str(e)}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
return formatted_tools
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/agents",
|
prefix="/agents",
|
||||||
tags=["agents"],
|
tags=["agents"],
|
||||||
@ -38,23 +87,24 @@ async def create_agent(
|
|||||||
return agent_service.create_agent(db, agent)
|
return agent_service.create_agent(db, agent)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{client_id}", response_model=List[Agent])
|
@router.get("/", response_model=List[Agent])
|
||||||
async def read_agents(
|
async def read_agents(
|
||||||
client_id: uuid.UUID,
|
x_client_id: uuid.UUID = Header(..., alias="x-client-id"),
|
||||||
skip: int = 0,
|
skip: int = 0,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
payload: dict = Depends(get_jwt_token),
|
payload: dict = Depends(get_jwt_token),
|
||||||
):
|
):
|
||||||
# Verify if the user has access to this client's data
|
# Verify if the user has access to this client's data
|
||||||
await verify_user_client(payload, db, client_id)
|
await verify_user_client(payload, db, x_client_id)
|
||||||
|
|
||||||
return agent_service.get_agents_by_client(db, client_id, skip, limit)
|
return agent_service.get_agents_by_client(db, x_client_id, skip, limit)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{agent_id}", response_model=Agent)
|
@router.get("/{agent_id}", response_model=Agent)
|
||||||
async def read_agent(
|
async def read_agent(
|
||||||
agent_id: uuid.UUID,
|
agent_id: uuid.UUID,
|
||||||
|
x_client_id: uuid.UUID = Header(..., alias="x-client-id"),
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
payload: dict = Depends(get_jwt_token),
|
payload: dict = Depends(get_jwt_token),
|
||||||
):
|
):
|
||||||
@ -65,7 +115,7 @@ async def read_agent(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Verify if the user has access to the agent's client
|
# Verify if the user has access to the agent's client
|
||||||
await verify_user_client(payload, db, db_agent.client_id)
|
await verify_user_client(payload, db, x_client_id)
|
||||||
|
|
||||||
return db_agent
|
return db_agent
|
||||||
|
|
||||||
@ -115,3 +165,201 @@ async def delete_agent(
|
|||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_404_NOT_FOUND, detail="Agent not found"
|
status_code=status.HTTP_404_NOT_FOUND, detail="Agent not found"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{agent_id}/.well-known/agent.json")
|
||||||
|
async def get_agent_json(
|
||||||
|
agent_id: uuid.UUID,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
agent = agent_service.get_agent(db, agent_id)
|
||||||
|
if agent is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND, detail="Agent not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
mcp_servers = agent.config.get("mcp_servers", [])
|
||||||
|
formatted_tools = await format_agent_tools(mcp_servers, db)
|
||||||
|
|
||||||
|
AGENT_CARD = {
|
||||||
|
"name": agent.name,
|
||||||
|
"description": agent.description,
|
||||||
|
"url": f"{os.getenv('API_URL', '')}/api/v1/agents/{agent.id}",
|
||||||
|
"provider": {
|
||||||
|
"organization": os.getenv("ORGANIZATION_NAME", ""),
|
||||||
|
"url": os.getenv("ORGANIZATION_URL", ""),
|
||||||
|
},
|
||||||
|
"version": os.getenv("API_VERSION", ""),
|
||||||
|
"capabilities": {
|
||||||
|
"streaming": False,
|
||||||
|
"pushNotifications": False,
|
||||||
|
"stateTransitionHistory": True,
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"schemes": ["apiKey"],
|
||||||
|
"credentials": {"in": "header", "name": "x-api-key"},
|
||||||
|
},
|
||||||
|
"defaultInputModes": ["text", "application/json"],
|
||||||
|
"defaultOutputModes": ["text", "application/json"],
|
||||||
|
"skills": formatted_tools,
|
||||||
|
}
|
||||||
|
return AGENT_CARD
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Error generating agent card",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{agent_id}/tasks/send")
|
||||||
|
async def handle_task(
|
||||||
|
agent_id: uuid.UUID,
|
||||||
|
request: Request,
|
||||||
|
x_api_key: str = Header(..., alias="x-api-key"),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
"""Endpoint to clients A2A send a new task (with an initial user message)."""
|
||||||
|
try:
|
||||||
|
# Verify agent
|
||||||
|
agent = agent_service.get_agent(db, agent_id)
|
||||||
|
if agent is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND, detail="Agent not found"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify API key
|
||||||
|
agent_config = agent.config
|
||||||
|
if agent_config.get("api_key") != x_api_key:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
|
detail="Invalid API key for this agent",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process request
|
||||||
|
try:
|
||||||
|
task_request = await request.json()
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid request format"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Validate required fields
|
||||||
|
task_id = task_request.get("id")
|
||||||
|
if not task_id:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Task ID is required"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Extract user message
|
||||||
|
try:
|
||||||
|
user_message = task_request["message"]["parts"][0]["text"]
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid message format"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Configure session and metadata
|
||||||
|
session_id = f"{task_id}_{agent_id}"
|
||||||
|
metadata = task_request.get("metadata", {})
|
||||||
|
history_length = metadata.get("historyLength", 50)
|
||||||
|
|
||||||
|
# Initialize response
|
||||||
|
response_task = {
|
||||||
|
"id": task_id,
|
||||||
|
"sessionId": session_id,
|
||||||
|
"status": {
|
||||||
|
"state": "running",
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"message": None,
|
||||||
|
"error": None,
|
||||||
|
},
|
||||||
|
"artifacts": [],
|
||||||
|
"history": [],
|
||||||
|
"metadata": metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Execute agent
|
||||||
|
final_response_text = await run_agent(
|
||||||
|
str(agent_id),
|
||||||
|
task_id,
|
||||||
|
user_message,
|
||||||
|
session_service,
|
||||||
|
artifacts_service,
|
||||||
|
memory_service,
|
||||||
|
db,
|
||||||
|
session_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update status to completed
|
||||||
|
response_task["status"].update(
|
||||||
|
{
|
||||||
|
"state": "completed",
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"message": {
|
||||||
|
"role": "agent",
|
||||||
|
"parts": [{"type": "text", "text": final_response_text}],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add artifacts
|
||||||
|
if final_response_text:
|
||||||
|
response_task["artifacts"].append(
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"content": final_response_text,
|
||||||
|
"metadata": {
|
||||||
|
"generated_at": datetime.now().isoformat(),
|
||||||
|
"content_type": "text/plain",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# Update status to failed
|
||||||
|
response_task["status"].update(
|
||||||
|
{
|
||||||
|
"state": "failed",
|
||||||
|
"timestamp": datetime.now().isoformat(),
|
||||||
|
"error": {"code": "AGENT_EXECUTION_ERROR", "message": str(e)},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Process history
|
||||||
|
try:
|
||||||
|
history_messages = get_session_events(session_service, session_id)
|
||||||
|
history_messages = history_messages[-history_length:]
|
||||||
|
|
||||||
|
formatted_history = []
|
||||||
|
for event in history_messages:
|
||||||
|
if event.content and event.content.parts:
|
||||||
|
role = (
|
||||||
|
"agent" if event.content.role == "model" else event.content.role
|
||||||
|
)
|
||||||
|
formatted_history.append(
|
||||||
|
{
|
||||||
|
"role": role,
|
||||||
|
"parts": [
|
||||||
|
{"type": "text", "text": part.text}
|
||||||
|
for part in event.content.parts
|
||||||
|
if part.text
|
||||||
|
],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
response_task["history"] = formatted_history
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing history: {str(e)}")
|
||||||
|
return response_task
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Unexpected error in handle_task: {str(e)}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Internal server error",
|
||||||
|
)
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from pydantic_settings import BaseSettings
|
from pydantic_settings import BaseSettings
|
||||||
from functools import lru_cache
|
|
||||||
import secrets
|
import secrets
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# Carrega as variáveis do .env
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
@ -12,6 +15,13 @@ class Settings(BaseSettings):
|
|||||||
API_TITLE: str = os.getenv("API_TITLE", "Evo AI API")
|
API_TITLE: str = os.getenv("API_TITLE", "Evo AI API")
|
||||||
API_DESCRIPTION: str = os.getenv("API_DESCRIPTION", "API for executing AI agents")
|
API_DESCRIPTION: str = os.getenv("API_DESCRIPTION", "API for executing AI agents")
|
||||||
API_VERSION: str = os.getenv("API_VERSION", "1.0.0")
|
API_VERSION: str = os.getenv("API_VERSION", "1.0.0")
|
||||||
|
API_URL: str = os.getenv("API_URL", "http://localhost:8000")
|
||||||
|
|
||||||
|
# Organization settings
|
||||||
|
ORGANIZATION_NAME: str = os.getenv("ORGANIZATION_NAME", "Evo AI")
|
||||||
|
ORGANIZATION_URL: str = os.getenv(
|
||||||
|
"ORGANIZATION_URL", "https://evoai.evoapicloud.com"
|
||||||
|
)
|
||||||
|
|
||||||
# Database settings
|
# Database settings
|
||||||
POSTGRES_CONNECTION_STRING: str = os.getenv(
|
POSTGRES_CONNECTION_STRING: str = os.getenv(
|
||||||
@ -75,10 +85,10 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
|
env_file_encoding = "utf-8"
|
||||||
case_sensitive = True
|
case_sensitive = True
|
||||||
|
|
||||||
|
|
||||||
@lru_cache()
|
|
||||||
def get_settings() -> Settings:
|
def get_settings() -> Settings:
|
||||||
return Settings()
|
return Settings()
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
from sqlalchemy import (
|
from sqlalchemy import (
|
||||||
Column,
|
Column,
|
||||||
String,
|
String,
|
||||||
@ -83,6 +84,13 @@ class Agent(Base):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def agent_card_url(self) -> str:
|
||||||
|
"""URL virtual para o agent card que não é rastrada no banco de dados"""
|
||||||
|
return (
|
||||||
|
f"{os.getenv('API_URL', '')}/api/v1/agents/{self.id}/.well-known/agent.json"
|
||||||
|
)
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
"""Converts the object to a dictionary, converting UUIDs to strings"""
|
"""Converts the object to a dictionary, converting UUIDs to strings"""
|
||||||
result = {}
|
result = {}
|
||||||
@ -104,6 +112,8 @@ class Agent(Base):
|
|||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
result[key] = value
|
result[key] = value
|
||||||
|
# Adiciona a propriedade virtual ao dicionário
|
||||||
|
result["agent_card_url"] = self.agent_card_url
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _convert_dict(self, d):
|
def _convert_dict(self, d):
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from typing import List, Optional, Dict, Union
|
from typing import List, Optional, Dict, Union
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
|
|
||||||
|
|
||||||
class ToolConfig(BaseModel):
|
class ToolConfig(BaseModel):
|
||||||
@ -90,9 +92,20 @@ class CustomTools(BaseModel):
|
|||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
def generate_api_key(length: int = 32) -> str:
|
||||||
|
"""Generate a secure API key."""
|
||||||
|
alphabet = string.ascii_letters + string.digits
|
||||||
|
return "".join(secrets.choice(alphabet) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
class LLMConfig(BaseModel):
|
class LLMConfig(BaseModel):
|
||||||
"""Configuration for LLM agents"""
|
"""Configuration for LLM agents"""
|
||||||
|
|
||||||
|
api_key: str = Field(
|
||||||
|
default_factory=generate_api_key,
|
||||||
|
description="API key for the LLM. If not provided, a secure key will be generated automatically.",
|
||||||
|
)
|
||||||
|
|
||||||
tools: Optional[List[ToolConfig]] = Field(
|
tools: Optional[List[ToolConfig]] = Field(
|
||||||
default=None, description="List of available tools"
|
default=None, description="List of available tools"
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,16 @@ from src.schemas.agent_config import LLMConfig
|
|||||||
|
|
||||||
class ClientBase(BaseModel):
|
class ClientBase(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
email: Optional[EmailStr] = None
|
email: Optional[str] = None
|
||||||
|
|
||||||
|
@validator("email")
|
||||||
|
def validate_email(cls, v):
|
||||||
|
if v is None:
|
||||||
|
return v
|
||||||
|
email_regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
|
||||||
|
if not re.match(email_regex, v):
|
||||||
|
raise ValueError("Invalid email format")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
class ClientCreate(ClientBase):
|
class ClientCreate(ClientBase):
|
||||||
@ -120,17 +129,28 @@ class Agent(AgentBase):
|
|||||||
client_id: UUID
|
client_id: UUID
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: Optional[datetime] = None
|
updated_at: Optional[datetime] = None
|
||||||
|
agent_card_url: Optional[str] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class ToolConfig(BaseModel):
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
tags: List[str] = Field(default_factory=list)
|
||||||
|
examples: List[str] = Field(default_factory=list)
|
||||||
|
inputModes: List[str] = Field(default_factory=list)
|
||||||
|
outputModes: List[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
class MCPServerBase(BaseModel):
|
class MCPServerBase(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
config_json: Dict[str, Any] = Field(default_factory=dict)
|
config_json: Dict[str, Any] = Field(default_factory=dict)
|
||||||
environments: Dict[str, Any] = Field(default_factory=dict)
|
environments: Dict[str, Any] = Field(default_factory=dict)
|
||||||
tools: List[str] = Field(default_factory=list)
|
tools: List[ToolConfig] = Field(default_factory=list)
|
||||||
type: str = Field(default="official")
|
type: str = Field(default="official")
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ from src.core.exceptions import AgentNotFoundError, InternalServerError
|
|||||||
from src.services.agent_service import get_agent
|
from src.services.agent_service import get_agent
|
||||||
from src.services.agent_builder import AgentBuilder
|
from src.services.agent_builder import AgentBuilder
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
logger = setup_logger(__name__)
|
logger = setup_logger(__name__)
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ async def run_agent(
|
|||||||
artifacts_service: InMemoryArtifactService,
|
artifacts_service: InMemoryArtifactService,
|
||||||
memory_service: InMemoryMemoryService,
|
memory_service: InMemoryMemoryService,
|
||||||
db: Session,
|
db: Session,
|
||||||
|
session_id: Optional[str] = None,
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
logger.info(f"Starting execution of agent {agent_id} for contact {contact_id}")
|
logger.info(f"Starting execution of agent {agent_id} for contact {contact_id}")
|
||||||
@ -45,13 +47,15 @@ async def run_agent(
|
|||||||
artifact_service=artifacts_service,
|
artifact_service=artifacts_service,
|
||||||
memory_service=memory_service,
|
memory_service=memory_service,
|
||||||
)
|
)
|
||||||
session_id = contact_id + "_" + agent_id
|
adk_session_id = contact_id + "_" + agent_id
|
||||||
|
if session_id is None:
|
||||||
|
session_id = adk_session_id
|
||||||
|
|
||||||
logger.info(f"Searching session for contact {contact_id}")
|
logger.info(f"Searching session for contact {contact_id}")
|
||||||
session = session_service.get_session(
|
session = session_service.get_session(
|
||||||
app_name=agent_id,
|
app_name=agent_id,
|
||||||
user_id=contact_id,
|
user_id=contact_id,
|
||||||
session_id=session_id,
|
session_id=adk_session_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
if session is None:
|
if session is None:
|
||||||
@ -59,7 +63,7 @@ async def run_agent(
|
|||||||
session = session_service.create_session(
|
session = session_service.create_session(
|
||||||
app_name=agent_id,
|
app_name=agent_id,
|
||||||
user_id=contact_id,
|
user_id=contact_id,
|
||||||
session_id=session_id,
|
session_id=adk_session_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
content = Content(role="user", parts=[Part(text=message)])
|
content = Content(role="user", parts=[Part(text=message)])
|
||||||
@ -69,7 +73,7 @@ async def run_agent(
|
|||||||
try:
|
try:
|
||||||
for event in agent_runner.run(
|
for event in agent_runner.run(
|
||||||
user_id=contact_id,
|
user_id=contact_id,
|
||||||
session_id=session_id,
|
session_id=adk_session_id,
|
||||||
new_message=content,
|
new_message=content,
|
||||||
):
|
):
|
||||||
if event.is_final_response() and event.content and event.content.parts:
|
if event.is_final_response() and event.content and event.content.parts:
|
||||||
@ -79,7 +83,7 @@ async def run_agent(
|
|||||||
completed_session = session_service.get_session(
|
completed_session = session_service.get_session(
|
||||||
app_name=agent_id,
|
app_name=agent_id,
|
||||||
user_id=contact_id,
|
user_id=contact_id,
|
||||||
session_id=session_id,
|
session_id=adk_session_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
memory_service.add_session_to_memory(completed_session)
|
memory_service.add_session_to_memory(completed_session)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import os
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
@ -27,6 +28,7 @@ def get_agent(db: Session, agent_id: uuid.UUID) -> Optional[Agent]:
|
|||||||
if not agent:
|
if not agent:
|
||||||
logger.warning(f"Agent not found: {agent_id}")
|
logger.warning(f"Agent not found: {agent_id}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return agent
|
return agent
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
logger.error(f"Error searching for agent {agent_id}: {str(e)}")
|
logger.error(f"Error searching for agent {agent_id}: {str(e)}")
|
||||||
@ -47,7 +49,11 @@ def get_agents_by_client(
|
|||||||
try:
|
try:
|
||||||
query = db.query(Agent).filter(Agent.client_id == client_id)
|
query = db.query(Agent).filter(Agent.client_id == client_id)
|
||||||
|
|
||||||
return query.offset(skip).limit(limit).all()
|
agents = query.offset(skip).limit(limit).all()
|
||||||
|
|
||||||
|
# A propriedade virtual agent_card_url será automaticamente incluída
|
||||||
|
# quando os agentes forem serializados para JSON
|
||||||
|
return agents
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
logger.error(f"Error searching for client agents {client_id}: {str(e)}")
|
logger.error(f"Error searching for client agents {client_id}: {str(e)}")
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@ -139,6 +145,9 @@ def create_agent(db: Session, agent: AgentCreate) -> Agent:
|
|||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_agent)
|
db.refresh(db_agent)
|
||||||
logger.info(f"Agent created successfully: {db_agent.id}")
|
logger.info(f"Agent created successfully: {db_agent.id}")
|
||||||
|
|
||||||
|
# A propriedade virtual agent_card_url será automaticamente incluída
|
||||||
|
# quando o agente for serializado para JSON
|
||||||
return db_agent
|
return db_agent
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
db.rollback()
|
db.rollback()
|
||||||
|
@ -2,7 +2,7 @@ from sqlalchemy.orm import Session
|
|||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from src.models.models import MCPServer
|
from src.models.models import MCPServer
|
||||||
from src.schemas.schemas import MCPServerCreate
|
from src.schemas.schemas import MCPServerCreate, ToolConfig
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
import uuid
|
import uuid
|
||||||
import logging
|
import logging
|
||||||
@ -41,7 +41,11 @@ def get_mcp_servers(db: Session, skip: int = 0, limit: int = 100) -> List[MCPSer
|
|||||||
def create_mcp_server(db: Session, server: MCPServerCreate) -> MCPServer:
|
def create_mcp_server(db: Session, server: MCPServerCreate) -> MCPServer:
|
||||||
"""Create a new MCP server"""
|
"""Create a new MCP server"""
|
||||||
try:
|
try:
|
||||||
db_server = MCPServer(**server.model_dump())
|
# Convert tools to JSON serializable format
|
||||||
|
server_data = server.model_dump()
|
||||||
|
server_data["tools"] = [tool.model_dump() for tool in server.tools]
|
||||||
|
|
||||||
|
db_server = MCPServer(**server_data)
|
||||||
db.add(db_server)
|
db.add(db_server)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(db_server)
|
db.refresh(db_server)
|
||||||
@ -65,7 +69,11 @@ def update_mcp_server(
|
|||||||
if not db_server:
|
if not db_server:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
for key, value in server.model_dump().items():
|
# Convert tools to JSON serializable format
|
||||||
|
server_data = server.model_dump()
|
||||||
|
server_data["tools"] = [tool.model_dump() for tool in server.tools]
|
||||||
|
|
||||||
|
for key, value in server_data.items():
|
||||||
setattr(db_server, key, value)
|
setattr(db_server, key, value)
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
Loading…
Reference in New Issue
Block a user