Adiciona campo de ferramentas aos servidores MCP e implementa rotas para gerenciamento de sessões e ferramentas na API.
This commit is contained in:
parent
e98744b7a4
commit
dbdb72ce0e
@ -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 ###
|
Binary file not shown.
@ -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"
|
||||
)
|
||||
|
@ -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"
|
||||
|
@ -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))
|
Binary file not shown.
@ -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
|
||||
|
@ -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):
|
||||
|
Binary file not shown.
Binary file not shown.
@ -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,
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
148
src/services/session_service.py
Normal file
148
src/services/session_service.py
Normal 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)}",
|
||||
)
|
Loading…
Reference in New Issue
Block a user