Adiciona campo de ferramentas aos servidores MCP e implementa rotas para gerenciamento de sessões e ferramentas na API.

This commit is contained in:
Davidson Gomes 2025-04-28 13:54:44 -03:00
parent e98744b7a4
commit dbdb72ce0e
14 changed files with 457 additions and 124 deletions

View File

@ -0,0 +1,32 @@
"""add_tools_field_to_mcp_servers
Revision ID: 2d612b95d0ea
Revises: da8e7fb4da5d
Create Date: 2025-04-28 12:39:21.430144
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision: str = '2d612b95d0ea'
down_revision: Union[str, None] = 'da8e7fb4da5d'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('mcp_servers', sa.Column('tools', sa.JSON(), nullable=False, server_default='[]'))
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('mcp_servers', 'tools')
# ### end Alembic commands ###

View File

@ -7,11 +7,16 @@ from datetime import datetime
from src.config.database import get_db
from src.core.middleware import get_api_key
from src.schemas.schemas import (
Client, ClientCreate,
Contact, ContactCreate,
Agent, AgentCreate,
MCPServer, MCPServerCreate,
Tool, ToolCreate,
Client,
ClientCreate,
Contact,
ContactCreate,
Agent,
AgentCreate,
MCPServer,
MCPServerCreate,
Tool,
ToolCreate,
)
from src.services import (
client_service,
@ -26,7 +31,16 @@ from src.core.exceptions import AgentNotFoundError
from google.adk.artifacts.in_memory_artifact_service import InMemoryArtifactService
from google.adk.sessions import DatabaseSessionService
from google.adk.memory import InMemoryMemoryService
from google.adk.events import Event
from google.adk.sessions import Session as Adk_Session
from src.config.settings import settings
from src.services.session_service import (
get_session_events,
get_session_by_id,
delete_session,
get_sessions_by_agent,
get_sessions_by_client,
)
router = APIRouter()
@ -38,283 +52,394 @@ session_service = DatabaseSessionService(db_url=POSTGRES_CONNECTION_STRING)
artifacts_service = InMemoryArtifactService()
memory_service = InMemoryMemoryService()
@router.post("/chat", response_model=ChatResponse, responses={
400: {"model": ErrorResponse},
404: {"model": ErrorResponse},
500: {"model": ErrorResponse}
})
@router.post(
"/chat",
response_model=ChatResponse,
responses={
400: {"model": ErrorResponse},
404: {"model": ErrorResponse},
500: {"model": ErrorResponse},
},
)
async def chat(
request: ChatRequest,
request: ChatRequest,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
try:
final_response_text = await run_agent(
request.agent_id,
request.agent_id,
request.contact_id,
request.message,
session_service,
request.message,
session_service,
artifacts_service,
memory_service,
db
db,
)
return {
"response": final_response_text,
"status": "success",
"timestamp": datetime.now().isoformat()
"timestamp": datetime.now().isoformat(),
}
except AgentNotFoundError as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except Exception as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
)
# Rotas para Sessões
@router.get("/sessions/client/{client_id}", response_model=List[Adk_Session])
def get_client_sessions(
client_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key),
):
return get_sessions_by_client(db, client_id)
@router.get("/sessions/agent/{agent_id}", response_model=List[Adk_Session])
def get_agent_sessions(
agent_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key),
skip: int = 0,
limit: int = 100,
):
return get_sessions_by_agent(db, agent_id, skip, limit)
@router.get("/sessions/{session_id}", response_model=Adk_Session)
def get_session(
session_id: str,
api_key: str = Security(get_api_key),
):
return get_session_by_id(session_service, session_id)
@router.get(
"/sessions/{session_id}/messages",
response_model=List[Event],
)
async def get_agent_messages(
session_id: str,
api_key: str = Security(get_api_key),
):
return get_session_events(session_service, session_id)
@router.delete(
"/sessions/{session_id}",
status_code=status.HTTP_204_NO_CONTENT,
)
def remove_session(
session_id: str,
api_key: str = Security(get_api_key),
):
return delete_session(session_service, session_id)
# Rotas para Clientes
@router.post("/clients/", response_model=Client, status_code=status.HTTP_201_CREATED)
def create_client(
client: ClientCreate,
client: ClientCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return client_service.create_client(db, client)
@router.get("/clients/", response_model=List[Client])
def read_clients(
skip: int = 0,
limit: int = 100,
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return client_service.get_clients(db, skip, limit)
@router.get("/clients/{client_id}", response_model=Client)
def read_client(
client_id: uuid.UUID,
client_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_client = client_service.get_client(db, client_id)
if db_client is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cliente não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Cliente não encontrado"
)
return db_client
@router.put("/clients/{client_id}", response_model=Client)
def update_client(
client_id: uuid.UUID,
client: ClientCreate,
client_id: uuid.UUID,
client: ClientCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_client = client_service.update_client(db, client_id, client)
if db_client is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cliente não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Cliente não encontrado"
)
return db_client
@router.delete("/clients/{client_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_client(
client_id: uuid.UUID,
client_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
if not client_service.delete_client(db, client_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Cliente não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Cliente não encontrado"
)
# Rotas para Contatos
@router.post("/contacts/", response_model=Contact, status_code=status.HTTP_201_CREATED)
def create_contact(
contact: ContactCreate,
contact: ContactCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return contact_service.create_contact(db, contact)
@router.get("/contacts/{client_id}", response_model=List[Contact])
def read_contacts(
client_id: uuid.UUID,
skip: int = 0,
limit: int = 100,
client_id: uuid.UUID,
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return contact_service.get_contacts_by_client(db, client_id, skip, limit)
@router.get("/contact/{contact_id}", response_model=Contact)
def read_contact(
contact_id: uuid.UUID,
contact_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_contact = contact_service.get_contact(db, contact_id)
if db_contact is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Contato não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Contato não encontrado"
)
return db_contact
@router.put("/contact/{contact_id}", response_model=Contact)
def update_contact(
contact_id: uuid.UUID,
contact: ContactCreate,
contact_id: uuid.UUID,
contact: ContactCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_contact = contact_service.update_contact(db, contact_id, contact)
if db_contact is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Contato não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Contato não encontrado"
)
return db_contact
@router.delete("/contact/{contact_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_contact(
contact_id: uuid.UUID,
contact_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
if not contact_service.delete_contact(db, contact_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Contato não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Contato não encontrado"
)
# Rotas para Agentes
@router.post("/agents/", response_model=Agent, status_code=status.HTTP_201_CREATED)
def create_agent(
agent: AgentCreate,
agent: AgentCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return agent_service.create_agent(db, agent)
@router.get("/agents/{client_id}", response_model=List[Agent])
def read_agents(
client_id: uuid.UUID,
skip: int = 0,
limit: int = 100,
client_id: uuid.UUID,
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return agent_service.get_agents_by_client(db, client_id, skip, limit)
@router.get("/agent/{agent_id}", response_model=Agent)
def read_agent(
agent_id: uuid.UUID,
agent_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_agent = agent_service.get_agent(db, agent_id)
if db_agent is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Agente não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Agente não encontrado"
)
return db_agent
@router.put("/agent/{agent_id}", response_model=Agent)
async def update_agent(
agent_id: uuid.UUID,
agent_data: Dict[str, Any],
db: Session = Depends(get_db)
agent_id: uuid.UUID, agent_data: Dict[str, Any], db: Session = Depends(get_db)
):
"""Atualiza um agente existente"""
return await agent_service.update_agent(db, agent_id, agent_data)
@router.delete("/agent/{agent_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_agent(
agent_id: uuid.UUID,
agent_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
if not agent_service.delete_agent(db, agent_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Agente não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Agente não encontrado"
)
# Rotas para MCPServers
@router.post("/mcp-servers/", response_model=MCPServer, status_code=status.HTTP_201_CREATED)
@router.post(
"/mcp-servers/", response_model=MCPServer, status_code=status.HTTP_201_CREATED
)
def create_mcp_server(
server: MCPServerCreate,
server: MCPServerCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return mcp_server_service.create_mcp_server(db, server)
@router.get("/mcp-servers/", response_model=List[MCPServer])
def read_mcp_servers(
skip: int = 0,
limit: int = 100,
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return mcp_server_service.get_mcp_servers(db, skip, limit)
@router.get("/mcp-servers/{server_id}", response_model=MCPServer)
def read_mcp_server(
server_id: uuid.UUID,
server_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_server = mcp_server_service.get_mcp_server(db, server_id)
if db_server is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Servidor MCP não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Servidor MCP não encontrado"
)
return db_server
@router.put("/mcp-servers/{server_id}", response_model=MCPServer)
def update_mcp_server(
server_id: uuid.UUID,
server: MCPServerCreate,
server_id: uuid.UUID,
server: MCPServerCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_server = mcp_server_service.update_mcp_server(db, server_id, server)
if db_server is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Servidor MCP não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Servidor MCP não encontrado"
)
return db_server
@router.delete("/mcp-servers/{server_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_mcp_server(
server_id: uuid.UUID,
server_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
if not mcp_server_service.delete_mcp_server(db, server_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Servidor MCP não encontrado")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Servidor MCP não encontrado"
)
# Rotas para Tools
@router.post("/tools/", response_model=Tool, status_code=status.HTTP_201_CREATED)
def create_tool(
tool: ToolCreate,
tool: ToolCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return tool_service.create_tool(db, tool)
@router.get("/tools/", response_model=List[Tool])
def read_tools(
skip: int = 0,
limit: int = 100,
skip: int = 0,
limit: int = 100,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
return tool_service.get_tools(db, skip, limit)
@router.get("/tools/{tool_id}", response_model=Tool)
def read_tool(
tool_id: uuid.UUID,
tool_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_tool = tool_service.get_tool(db, tool_id)
if db_tool is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Ferramenta não encontrada")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Ferramenta não encontrada"
)
return db_tool
@router.put("/tools/{tool_id}", response_model=Tool)
def update_tool(
tool_id: uuid.UUID,
tool: ToolCreate,
tool_id: uuid.UUID,
tool: ToolCreate,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
db_tool = tool_service.update_tool(db, tool_id, tool)
if db_tool is None:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Ferramenta não encontrada")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Ferramenta não encontrada"
)
return db_tool
@router.delete("/tools/{tool_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_tool(
tool_id: uuid.UUID,
tool_id: uuid.UUID,
db: Session = Depends(get_db),
api_key: str = Security(get_api_key)
api_key: str = Security(get_api_key),
):
if not tool_service.delete_tool(db, tool_id):
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Ferramenta não encontrada")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Ferramenta não encontrada"
)

View File

@ -18,14 +18,6 @@ class Settings(BaseSettings):
"postgresql://postgres:root@localhost:5432/evo_ai"
)
# Configurações do OpenAI
OPENAI_API_KEY: Optional[str] = os.getenv("OPENAI_API_KEY")
# Configurações da aplicação
APP_NAME: str = "app"
USER_ID: str = "user_1"
SESSION_ID: str = "session_001"
# Configurações de logging
LOG_LEVEL: str = os.getenv("LOG_LEVEL", "INFO")
LOG_DIR: str = "logs"

View File

@ -79,6 +79,7 @@ class MCPServer(Base):
description = Column(Text, nullable=True)
config_json = Column(JSON, nullable=False, default={})
environments = Column(JSON, nullable=False, default={})
tools = Column(JSON, nullable=False, default=[])
type = Column(String, nullable=False, default="official")
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
@ -96,4 +97,16 @@ class Tool(Base):
config_json = Column(JSON, nullable=False, default={})
environments = Column(JSON, nullable=False, default={})
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class Session(Base):
__tablename__ = "sessions"
# A diretiva abaixo faz com que o Alembic ignore esta tabela nas migrações
__table_args__ = {'extend_existing': True, 'info': {'skip_autogenerate': True}}
id = Column(String, primary_key=True)
app_name = Column(String)
user_id = Column(String)
state = Column(JSON)
create_time = Column(DateTime(timezone=True))
update_time = Column(DateTime(timezone=True))

View File

@ -14,6 +14,7 @@ class MCPServerConfig(BaseModel):
"""Configuração de um servidor MCP"""
id: UUID
envs: Dict[str, str] = Field(default_factory=dict, description="Variáveis de ambiente do servidor")
tools: List[str] = Field(default_factory=list, description="Lista de ferramentas do servidor")
class Config:
from_attributes = True

View File

@ -109,6 +109,7 @@ class MCPServerBase(BaseModel):
description: Optional[str] = None
config_json: Dict[str, Any] = Field(default_factory=dict)
environments: Dict[str, Any] = Field(default_factory=dict)
tools: List[str] = Field(default_factory=list)
type: str = Field(default="official")
class MCPServerCreate(MCPServerBase):

View File

@ -48,7 +48,7 @@ async def run_agent(
logger.info("Configurando Runner")
agent_runner = Runner(
agent=root_agent,
app_name=get_root_agent.name,
app_name=agent_id,
session_service=session_service,
artifact_service=artifacts_service,
memory_service=memory_service,
@ -57,7 +57,7 @@ async def run_agent(
logger.info(f"Buscando sessão para contato {contact_id}")
session = session_service.get_session(
app_name=root_agent.name,
app_name=agent_id,
user_id=contact_id,
session_id=session_id,
)
@ -65,7 +65,7 @@ async def run_agent(
if session is None:
logger.info(f"Criando nova sessão para contato {contact_id}")
session = session_service.create_session(
app_name=root_agent.name,
app_name=agent_id,
user_id=contact_id,
session_id=session_id,
)
@ -85,7 +85,7 @@ async def run_agent(
logger.info(f"Resposta final recebida: {final_response_text}")
completed_session = session_service.get_session(
app_name=root_agent.name,
app_name=agent_id,
user_id=contact_id,
session_id=session_id,
)

View File

@ -66,29 +66,29 @@ def create_agent(db: Session, agent: AgentCreate) -> Agent:
"""Cria um novo agente"""
try:
# Validação adicional de sub-agentes
if agent.type != 'llm':
if agent.type != "llm":
if not isinstance(agent.config, dict):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Configuração inválida: deve ser um objeto com sub_agents"
detail="Configuração inválida: deve ser um objeto com sub_agents",
)
if 'sub_agents' not in agent.config:
if "sub_agents" not in agent.config:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Configuração inválida: sub_agents é obrigatório para agentes do tipo sequential, parallel ou loop"
detail="Configuração inválida: sub_agents é obrigatório para agentes do tipo sequential, parallel ou loop",
)
if not agent.config['sub_agents']:
if not agent.config["sub_agents"]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Configuração inválida: sub_agents não pode estar vazio"
detail="Configuração inválida: sub_agents não pode estar vazio",
)
if not validate_sub_agents(db, agent.config['sub_agents']):
if not validate_sub_agents(db, agent.config["sub_agents"]):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Um ou mais sub-agentes não existem"
detail="Um ou mais sub-agentes não existem",
)
# Processa a configuração antes de criar o agente
@ -114,9 +114,13 @@ def create_agent(db: Session, agent: AgentCreate) -> Agent:
detail=f"Variável de ambiente '{env_key}' não fornecida para o servidor MCP {mcp_server.name}",
)
# Adiciona o servidor processado
# Adiciona o servidor processado com suas ferramentas
processed_servers.append(
{"id": str(server["id"]), "envs": server["envs"]}
{
"id": str(server["id"]),
"envs": server["envs"],
"tools": server["tools"],
}
)
config["mcp_servers"] = processed_servers
@ -147,7 +151,7 @@ def create_agent(db: Session, agent: AgentCreate) -> Agent:
logger.error(f"Erro ao criar agente: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Erro ao criar agente"
detail="Erro ao criar agente",
)
@ -186,7 +190,11 @@ async def update_agent(
# Adiciona o servidor processado
processed_servers.append(
{"id": str(server["id"]), "envs": server["envs"]}
{
"id": str(server["id"]),
"envs": server["envs"],
"tools": server["tools"],
}
)
config["mcp_servers"] = processed_servers

View File

@ -74,6 +74,15 @@ class MCPService:
logger.warning(f"Removidas {removed_count} ferramentas incompatíveis.")
return filtered_tools
def _filter_tools_by_agent(self, tools: List[Any], agent_tools: List[str]) -> List[Any]:
"""Filtra ferramentas compatíveis com o agente."""
filtered_tools = []
for tool in tools:
logger.info(f"Ferramenta: {tool.name}")
if tool.name in agent_tools:
filtered_tools.append(tool)
return filtered_tools
async def build_tools(self, mcp_config: Dict[str, Any], db: Session) -> Tuple[List[Any], AsyncExitStack]:
"""Constrói uma lista de ferramentas a partir de múltiplos servidores MCP."""
@ -109,6 +118,10 @@ class MCPService:
if tools and exit_stack:
# Filtra ferramentas incompatíveis
filtered_tools = self._filter_incompatible_tools(tools)
# Filtra ferramentas compatíveis com o agente
agent_tools = server.get('tools', [])
filtered_tools = self._filter_tools_by_agent(filtered_tools, agent_tools)
self.tools.extend(filtered_tools)
# Registra o exit_stack com o AsyncExitStack

View File

@ -0,0 +1,148 @@
from google.adk.sessions import DatabaseSessionService
from sqlalchemy.orm import Session
from src.models.models import Session as SessionModel
from google.adk.events import Event
from google.adk.sessions import Session as SessionADK
from typing import Optional, List
from fastapi import HTTPException, status
from sqlalchemy.exc import SQLAlchemyError
from src.services.agent_service import get_agent, get_agents_by_client
from src.services.contact_service import get_contact
import uuid
import logging
logger = logging.getLogger(__name__)
def get_sessions_by_client(
db: Session,
client_id: uuid.UUID,
) -> List[SessionModel]:
"""Busca sessões de um cliente com paginação"""
try:
agents_by_client = get_agents_by_client(db, client_id)
sessions = []
for agent in agents_by_client:
sessions.extend(get_sessions_by_agent(db, agent.id))
return sessions
except SQLAlchemyError as e:
logger.error(f"Erro ao buscar sessões do cliente {client_id}: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Erro ao buscar sessões",
)
def get_sessions_by_agent(
db: Session,
agent_id: uuid.UUID,
skip: int = 0,
limit: int = 100,
) -> List[SessionModel]:
"""Busca sessões de um agente com paginação"""
try:
agent_id_str = str(agent_id)
query = db.query(SessionModel).filter(SessionModel.app_name == agent_id_str)
return query.offset(skip).limit(limit).all()
except SQLAlchemyError as e:
logger.error(f"Erro ao buscar sessões do agente {agent_id_str}: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Erro ao buscar sessões",
)
def get_session_by_id(
session_service: DatabaseSessionService, session_id: str
) -> Optional[SessionADK]:
"""Busca uma sessão pelo ID"""
try:
if not session_id or "_" not in session_id:
logger.error(f"ID de sessão inválido: {session_id}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="ID de sessão inválido. Formato esperado: app_name_user_id",
)
parts = session_id.split("_", 1)
if len(parts) != 2:
logger.error(f"Formato de ID de sessão inválido: {session_id}")
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Formato de ID de sessão inválido. Formato esperado: app_name_user_id",
)
user_id, app_name = parts
session = session_service.get_session(
app_name=app_name,
user_id=user_id,
session_id=session_id,
)
if session is None:
logger.error(f"Sessão não encontrada: {session_id}")
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Sessão não encontrada: {session_id}",
)
return session
except Exception as e:
logger.error(f"Erro ao buscar sessão {session_id}: {str(e)}")
if isinstance(e, HTTPException):
raise e
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao buscar sessão: {str(e)}",
)
def delete_session(session_service: DatabaseSessionService, session_id: str) -> None:
"""Deleta uma sessão pelo ID"""
try:
session = get_session_by_id(session_service, session_id)
# Se chegou aqui, a sessão existe (get_session_by_id já valida)
session_service.delete_session(
app_name=session.app_name,
user_id=session.user_id,
session_id=session_id,
)
return None
except HTTPException:
# Repassa exceções HTTP do get_session_by_id
raise
except Exception as e:
logger.error(f"Erro ao deletar sessão {session_id}: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao deletar sessão: {str(e)}",
)
def get_session_events(
session_service: DatabaseSessionService, session_id: str
) -> List[Event]:
"""Busca os eventos de uma sessão pelo ID"""
try:
session = get_session_by_id(session_service, session_id)
# Se chegou aqui, a sessão existe (get_session_by_id já valida)
if not hasattr(session, 'events') or session.events is None:
return []
return session.events
except HTTPException:
# Repassa exceções HTTP do get_session_by_id
raise
except Exception as e:
logger.error(f"Erro ao buscar eventos da sessão {session_id}: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Erro ao buscar eventos da sessão: {str(e)}",
)