♻️ refactor(api-routes): reorganizar rotas em pacote app/api/routes
parent
5a5d7f547e
commit
f061657ad3
@ -1,161 +0,0 @@
|
|||||||
from typing import List, Dict, Any
|
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
|
|
||||||
from app.api.schemas import (
|
|
||||||
ChatRequest,
|
|
||||||
ChatResponse,
|
|
||||||
ConsultarEstoqueRequest,
|
|
||||||
ValidarClienteVendaRequest,
|
|
||||||
AvaliarVeiculoTrocaRequest,
|
|
||||||
AgendarRevisaoRequest,
|
|
||||||
CancelarPedidoRequest,
|
|
||||||
)
|
|
||||||
from app.db.database import SessionLocal
|
|
||||||
from app.services.orquestrador_service import OrquestradorService
|
|
||||||
from app.services.handlers import (
|
|
||||||
consultar_estoque,
|
|
||||||
validar_cliente_venda,
|
|
||||||
avaliar_veiculo_troca,
|
|
||||||
agendar_revisao,
|
|
||||||
cancelar_pedido,
|
|
||||||
)
|
|
||||||
|
|
||||||
router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
def get_db():
|
|
||||||
"""Fornece uma sessao de banco para a request e garante o fechamento."""
|
|
||||||
db = SessionLocal()
|
|
||||||
try:
|
|
||||||
yield db
|
|
||||||
finally:
|
|
||||||
db.close()
|
|
||||||
|
|
||||||
|
|
||||||
def _db_error_detail(exc: SQLAlchemyError) -> str:
|
|
||||||
"""Converte erros de banco em mensagens amigaveis para a API."""
|
|
||||||
text = str(exc).lower()
|
|
||||||
|
|
||||||
# Heuristica para identificar falhas no MySQL de tools.
|
|
||||||
tools_markers = ("tools", "tool")
|
|
||||||
if any(marker in text for marker in tools_markers):
|
|
||||||
return "Servico temporariamente indisponivel: banco MySQL (tools) inacessivel."
|
|
||||||
|
|
||||||
# Heuristica para identificar falhas no MySQL (base ficticia).
|
|
||||||
mysql_mock_markers = ("mock", "vehicles", "customers", "orders", "review_schedules")
|
|
||||||
if any(marker in text for marker in mysql_mock_markers):
|
|
||||||
return "Servico temporariamente indisponivel: banco MySQL (dados ficticios) inacessivel."
|
|
||||||
|
|
||||||
mysql_generic_markers = ("mysql", "pymysql", "(2003", "3306")
|
|
||||||
if any(marker in text for marker in mysql_generic_markers):
|
|
||||||
return "Servico temporariamente indisponivel: banco MySQL inacessivel."
|
|
||||||
|
|
||||||
return "Servico temporariamente indisponivel: erro de acesso ao banco de dados."
|
|
||||||
|
|
||||||
'''
|
|
||||||
# Removido momentaniamente para teste do Vertex IA
|
|
||||||
@router.post("/chat", response_model=ChatResponse)
|
|
||||||
async def chat(request: ChatRequest, db: Session = Depends(get_db)):
|
|
||||||
service = OrquestradorService(db)
|
|
||||||
result = await service.handle_message(
|
|
||||||
message=request.message,
|
|
||||||
user_id=request.user_id,
|
|
||||||
)
|
|
||||||
return ChatResponse(response=result)
|
|
||||||
'''
|
|
||||||
@router.post("/chat", response_model=ChatResponse)
|
|
||||||
async def chat(request: ChatRequest, db: Session = Depends(get_db)):
|
|
||||||
"""Processa mensagem do usuario via orquestrador e retorna resposta do chat."""
|
|
||||||
try:
|
|
||||||
service = OrquestradorService(db)
|
|
||||||
result = await service.handle_message(
|
|
||||||
message=request.message,
|
|
||||||
user_id=request.user_id,
|
|
||||||
)
|
|
||||||
return ChatResponse(response=result)
|
|
||||||
except SQLAlchemyError as exc:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=503,
|
|
||||||
detail=_db_error_detail(exc),
|
|
||||||
)
|
|
||||||
except ValueError as exc:
|
|
||||||
# Erros de configuracao de Vertex (regiao/projeto/modelo)
|
|
||||||
raise HTTPException(status_code=500, detail=f"Configuracao invalida do Vertex AI: {exc}")
|
|
||||||
except RuntimeError as exc:
|
|
||||||
raise HTTPException(status_code=503, detail=f"Falha temporaria no LLM/Vertex AI: {exc}")
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/mock/consultar-estoque")
|
|
||||||
async def consultar_estoque_endpoint(
|
|
||||||
body: ConsultarEstoqueRequest,
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""Consulta estoque de veiculos mock com filtros opcionais."""
|
|
||||||
try:
|
|
||||||
return await consultar_estoque(
|
|
||||||
preco_max=body.preco_max,
|
|
||||||
categoria=body.categoria,
|
|
||||||
ordenar_preco=body.ordenar_preco,
|
|
||||||
limite=body.limite,
|
|
||||||
)
|
|
||||||
except SQLAlchemyError as exc:
|
|
||||||
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/mock/validar-cliente-venda")
|
|
||||||
async def validar_cliente_venda_endpoint(
|
|
||||||
body: ValidarClienteVendaRequest,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""Valida elegibilidade de cliente para uma venda."""
|
|
||||||
try:
|
|
||||||
return await validar_cliente_venda(
|
|
||||||
cpf=body.cpf,
|
|
||||||
valor_veiculo=body.valor_veiculo,
|
|
||||||
)
|
|
||||||
except SQLAlchemyError as exc:
|
|
||||||
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/mock/avaliar-veiculo-troca")
|
|
||||||
async def avaliar_veiculo_troca_endpoint(
|
|
||||||
body: AvaliarVeiculoTrocaRequest,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""Avalia valor de troca de um veiculo com base em modelo, ano e quilometragem."""
|
|
||||||
return await avaliar_veiculo_troca(
|
|
||||||
modelo=body.modelo,
|
|
||||||
ano=body.ano,
|
|
||||||
km=body.km,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/mock/agendar-revisao")
|
|
||||||
async def agendar_revisao_endpoint(
|
|
||||||
body: AgendarRevisaoRequest,
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""Agenda revisao para uma placa em data/hora informada."""
|
|
||||||
try:
|
|
||||||
return await agendar_revisao(
|
|
||||||
placa=body.placa,
|
|
||||||
data_hora=body.data_hora,
|
|
||||||
user_id=body.user_id,
|
|
||||||
)
|
|
||||||
except SQLAlchemyError as exc:
|
|
||||||
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/mock/cancelar-pedido")
|
|
||||||
async def cancelar_pedido_endpoint(
|
|
||||||
body: CancelarPedidoRequest,
|
|
||||||
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""Cancela pedido de venda existente registrando o motivo."""
|
|
||||||
try:
|
|
||||||
return await cancelar_pedido(
|
|
||||||
numero_pedido=body.numero_pedido,
|
|
||||||
motivo=body.motivo,
|
|
||||||
user_id=body.user_id,
|
|
||||||
)
|
|
||||||
except SQLAlchemyError as exc:
|
|
||||||
raise HTTPException(status_code=503, detail=_db_error_detail(exc))
|
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from app.api.routes.chat import router as chat_router
|
||||||
|
from app.api.routes.mock import router as mock_router
|
||||||
|
from app.api.routes.tools import router as tool_router
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
router.include_router(chat_router)
|
||||||
|
router.include_router(mock_router)
|
||||||
|
|
||||||
|
__all__ = ["router", "tool_router"]
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.api.routes.dependencies import db_error_detail, get_db
|
||||||
|
from app.api.schemas import ChatRequest, ChatResponse
|
||||||
|
from app.services.orquestrador_service import OrquestradorService
|
||||||
|
|
||||||
|
router = APIRouter(tags=["Chat"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/chat", response_model=ChatResponse)
|
||||||
|
async def chat(request: ChatRequest, db: Session = Depends(get_db)):
|
||||||
|
"""Processa mensagem do usuario via orquestrador e retorna resposta do chat."""
|
||||||
|
try:
|
||||||
|
service = OrquestradorService(db)
|
||||||
|
result = await service.handle_message(
|
||||||
|
message=request.message,
|
||||||
|
user_id=request.user_id,
|
||||||
|
)
|
||||||
|
return ChatResponse(response=result)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=503,
|
||||||
|
detail=db_error_detail(exc),
|
||||||
|
)
|
||||||
|
except ValueError as exc:
|
||||||
|
# Erros de configuracao de Vertex (regiao/projeto/modelo)
|
||||||
|
raise HTTPException(status_code=500, detail=f"Configuracao invalida do Vertex AI: {exc}")
|
||||||
|
except RuntimeError as exc:
|
||||||
|
raise HTTPException(status_code=503, detail=f"Falha temporaria no LLM/Vertex AI: {exc}")
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
|
from app.db.database import SessionLocal
|
||||||
|
|
||||||
|
|
||||||
|
def get_db():
|
||||||
|
"""Fornece uma sessao de banco para a request e garante o fechamento."""
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
yield db
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
def db_error_detail(exc: SQLAlchemyError) -> str:
|
||||||
|
"""Converte erros de banco em mensagens amigaveis para a API."""
|
||||||
|
text = str(exc).lower()
|
||||||
|
|
||||||
|
# Heuristica para identificar falhas no MySQL de tools.
|
||||||
|
tools_markers = ("tools", "tool")
|
||||||
|
if any(marker in text for marker in tools_markers):
|
||||||
|
return "Servico temporariamente indisponivel: banco MySQL (tools) inacessivel."
|
||||||
|
|
||||||
|
# Heuristica para identificar falhas no MySQL (base ficticia).
|
||||||
|
mysql_mock_markers = ("mock", "vehicles", "customers", "orders", "review_schedules")
|
||||||
|
if any(marker in text for marker in mysql_mock_markers):
|
||||||
|
return "Servico temporariamente indisponivel: banco MySQL (dados ficticios) inacessivel."
|
||||||
|
|
||||||
|
mysql_generic_markers = ("mysql", "pymysql", "(2003", "3306")
|
||||||
|
if any(marker in text for marker in mysql_generic_markers):
|
||||||
|
return "Servico temporariamente indisponivel: banco MySQL inacessivel."
|
||||||
|
|
||||||
|
return "Servico temporariamente indisponivel: erro de acesso ao banco de dados."
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
|
from fastapi import APIRouter, HTTPException
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
|
from app.api.routes.dependencies import db_error_detail
|
||||||
|
from app.api.schemas import (
|
||||||
|
AgendarRevisaoRequest,
|
||||||
|
AvaliarVeiculoTrocaRequest,
|
||||||
|
CancelarPedidoRequest,
|
||||||
|
ConsultarEstoqueRequest,
|
||||||
|
RealizarPedidoRequest,
|
||||||
|
ValidarClienteVendaRequest,
|
||||||
|
)
|
||||||
|
from app.services.handlers import (
|
||||||
|
agendar_revisao,
|
||||||
|
avaliar_veiculo_troca,
|
||||||
|
cancelar_pedido,
|
||||||
|
consultar_estoque,
|
||||||
|
realizar_pedido,
|
||||||
|
validar_cliente_venda,
|
||||||
|
)
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/mock", tags=["Mock"])
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/consultar-estoque")
|
||||||
|
async def consultar_estoque_endpoint(
|
||||||
|
body: ConsultarEstoqueRequest,
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""Consulta estoque de veiculos mock com filtros opcionais."""
|
||||||
|
try:
|
||||||
|
return await consultar_estoque(
|
||||||
|
preco_max=body.preco_max,
|
||||||
|
categoria=body.categoria,
|
||||||
|
ordenar_preco=body.ordenar_preco,
|
||||||
|
limite=body.limite,
|
||||||
|
)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
raise HTTPException(status_code=503, detail=db_error_detail(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/validar-cliente-venda")
|
||||||
|
async def validar_cliente_venda_endpoint(
|
||||||
|
body: ValidarClienteVendaRequest,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Valida elegibilidade de cliente para uma venda."""
|
||||||
|
try:
|
||||||
|
return await validar_cliente_venda(
|
||||||
|
cpf=body.cpf,
|
||||||
|
valor_veiculo=body.valor_veiculo,
|
||||||
|
)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
raise HTTPException(status_code=503, detail=db_error_detail(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/avaliar-veiculo-troca")
|
||||||
|
async def avaliar_veiculo_troca_endpoint(
|
||||||
|
body: AvaliarVeiculoTrocaRequest,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Avalia valor de troca de um veiculo com base em modelo, ano e quilometragem."""
|
||||||
|
return await avaliar_veiculo_troca(
|
||||||
|
modelo=body.modelo,
|
||||||
|
ano=body.ano,
|
||||||
|
km=body.km,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/agendar-revisao")
|
||||||
|
async def agendar_revisao_endpoint(
|
||||||
|
body: AgendarRevisaoRequest,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Agenda revisao para uma placa em data/hora informada."""
|
||||||
|
try:
|
||||||
|
return await agendar_revisao(
|
||||||
|
placa=body.placa,
|
||||||
|
data_hora=body.data_hora,
|
||||||
|
user_id=body.user_id,
|
||||||
|
)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
raise HTTPException(status_code=503, detail=db_error_detail(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/cancelar-pedido")
|
||||||
|
async def cancelar_pedido_endpoint(
|
||||||
|
body: CancelarPedidoRequest,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Cancela pedido de venda existente registrando o motivo."""
|
||||||
|
try:
|
||||||
|
return await cancelar_pedido(
|
||||||
|
numero_pedido=body.numero_pedido,
|
||||||
|
motivo=body.motivo,
|
||||||
|
user_id=body.user_id,
|
||||||
|
)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
raise HTTPException(status_code=503, detail=db_error_detail(exc))
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/realizar-pedido")
|
||||||
|
async def realizar_pedido_endpoint(
|
||||||
|
body: RealizarPedidoRequest,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Cria um pedido de compra para cliente aprovado no valor informado."""
|
||||||
|
try:
|
||||||
|
return await realizar_pedido(
|
||||||
|
cpf=body.cpf,
|
||||||
|
valor_veiculo=body.valor_veiculo,
|
||||||
|
user_id=body.user_id,
|
||||||
|
)
|
||||||
|
except SQLAlchemyError as exc:
|
||||||
|
raise HTTPException(status_code=503, detail=db_error_detail(exc))
|
||||||
Loading…
Reference in New Issue