147 lines
5.2 KiB
Python
147 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
from pydantic import BaseModel, Field
|
|
|
|
from backend.oracle.action_service import oracle_action_service
|
|
from backend.oracle.natural_db_agent import natural_db_agent
|
|
from backend.oracle.persona_service import persona_service
|
|
from backend.services.mcp_registry import mcp_registry
|
|
from backend.services.nemoclaw_runtime import nemoclaw_runtime
|
|
from backend.services.runtime_llm_service import runtime_llm_service
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class WorkflowPreviewRequest(BaseModel):
|
|
prompt: str = Field(..., min_length=1, max_length=4096)
|
|
tenant_id: str = "tenant_velocity"
|
|
actor_role: str = "sales_director"
|
|
|
|
|
|
class MCPExecuteRequest(BaseModel):
|
|
tool_name: str = Field(..., min_length=1, max_length=128)
|
|
query: str = Field(..., min_length=1, max_length=1024)
|
|
|
|
|
|
class OracleWritebackRequest(BaseModel):
|
|
action_id: str
|
|
tenant_id: str = "tenant_velocity"
|
|
actor_id: str = "oracle_operator"
|
|
target_entity_type: str = Field(..., min_length=1, max_length=64)
|
|
target_entity_id: str = Field(..., min_length=1, max_length=128)
|
|
action_type: str = Field(default="lead_writeback", min_length=1, max_length=128)
|
|
writeback_payload: dict = Field(default_factory=dict)
|
|
|
|
|
|
class OracleQueryRequest(BaseModel):
|
|
prompt: str = Field(..., min_length=1, max_length=4096)
|
|
row_limit: int = Field(default=100, ge=1, le=500)
|
|
context: dict = Field(default_factory=dict)
|
|
|
|
|
|
@router.get("/health")
|
|
async def oracle_health() -> dict:
|
|
return {
|
|
"status": "ok",
|
|
"persona": await persona_service.health(),
|
|
"mcp_tools": mcp_registry.list_tools(),
|
|
"runtime_llm": await runtime_llm_service.list_providers(),
|
|
}
|
|
|
|
|
|
@router.get("/data-health")
|
|
async def oracle_data_health(request: Request) -> dict:
|
|
pool = getattr(request.app.state, "db_pool", None)
|
|
if pool is None:
|
|
raise HTTPException(status_code=503, detail="Database unavailable.")
|
|
async with pool.acquire() as conn:
|
|
data = await natural_db_agent.data_health(conn)
|
|
return {
|
|
"status": "ok",
|
|
"data": data,
|
|
}
|
|
|
|
|
|
@router.get("/schema-catalog")
|
|
async def oracle_schema_catalog(request: Request) -> dict:
|
|
pool = getattr(request.app.state, "db_pool", None)
|
|
if pool is None:
|
|
raise HTTPException(status_code=503, detail="Database unavailable.")
|
|
async with pool.acquire() as conn:
|
|
catalog = await natural_db_agent.schema_catalog(conn)
|
|
return {"status": "ok", "data": catalog}
|
|
|
|
|
|
@router.post("/query")
|
|
async def oracle_query(request: Request, payload: OracleQueryRequest) -> dict:
|
|
pool = getattr(request.app.state, "db_pool", None)
|
|
if pool is None:
|
|
raise HTTPException(status_code=503, detail="Database unavailable.")
|
|
async with pool.acquire() as conn:
|
|
result = await natural_db_agent.execute_prompt(payload.prompt, row_limit=payload.row_limit, conn=conn)
|
|
return {"status": "ok", "data": result.as_dict()}
|
|
|
|
|
|
@router.get("/mcp/tools")
|
|
async def oracle_mcp_tools() -> dict:
|
|
return {"status": "ok", "data": mcp_registry.list_tools()}
|
|
|
|
|
|
@router.post("/mcp/execute")
|
|
async def oracle_mcp_execute(request: Request, payload: MCPExecuteRequest) -> dict:
|
|
pool = getattr(request.app.state, "db_pool", None)
|
|
result = await mcp_registry.execute(payload.tool_name, payload.query, crm_pool=pool)
|
|
return {"status": "ok", "data": result}
|
|
|
|
|
|
@router.post("/workflow/preview")
|
|
async def workflow_preview(payload: WorkflowPreviewRequest) -> dict:
|
|
persona_plan = await persona_service.plan_for_prompt(
|
|
prompt=payload.prompt,
|
|
tenant_id=payload.tenant_id,
|
|
actor_role=payload.actor_role,
|
|
)
|
|
return {
|
|
"status": "ok",
|
|
"data": {
|
|
"persona_plan": persona_plan,
|
|
"workflow": nemoclaw_runtime.build_workflow_dispatch(
|
|
prompt=payload.prompt,
|
|
tenant_id=payload.tenant_id,
|
|
actor_role=payload.actor_role,
|
|
component_templates=persona_plan["recommendedTemplates"],
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
@router.get("/actions")
|
|
async def list_oracle_actions(status: str | None = None, limit: int = 50) -> dict:
|
|
actions = await oracle_action_service.list_actions(status=status, limit=limit)
|
|
return {"status": "ok", "data": actions, "meta": {"count": len(actions)}}
|
|
|
|
|
|
@router.get("/actions/{action_id}")
|
|
async def get_oracle_action(action_id: str) -> dict:
|
|
action = await oracle_action_service.get_action(action_id)
|
|
if not action:
|
|
raise HTTPException(status_code=404, detail=f"Oracle action '{action_id}' not found.")
|
|
return {"status": "ok", "data": action}
|
|
|
|
|
|
@router.post("/actions/writeback")
|
|
async def apply_oracle_writeback(request: Request, payload: OracleWritebackRequest) -> dict:
|
|
result = await oracle_action_service.apply_writeback(payload.model_dump())
|
|
if hasattr(request.app.state, "broadcast_crm_event"):
|
|
await request.app.state.broadcast_crm_event(
|
|
{
|
|
"type": "oracle_writeback",
|
|
"entity": payload.target_entity_type,
|
|
"entity_id": payload.target_entity_id,
|
|
"action_id": payload.action_id,
|
|
"payload": result["resultPayload"],
|
|
}
|
|
)
|
|
return {"status": "ok", "data": result}
|