Files
odoo-ai/agent_service/routers/approval.py
Carlos Garcia e6c3d08990 Fix receipt parsing quality and approval endpoint
Receipt quality: replace LLM amount/date extraction with regex.
LLM was hallucinating 2021/2022 dates and returning '198.40 USD' strings.
Amounts now use deterministic regex (Total:/Grand Total:/Amount Due:).
Dates: filename timestamp > OCR regex > today (no LLM date guessing).
LLM only asked for vendor name + product category.

Approval: fix GET /approval/pending 500 by using correct column
name 'started_at' instead of 'created_at' (which does not exist).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 23:02:11 -04:00

90 lines
3.0 KiB
Python

from __future__ import annotations
import logging
from typing import Optional
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel
logger = logging.getLogger(__name__)
router = APIRouter(prefix='/approval', tags=['approval'])
class ApprovalRequest(BaseModel):
directive_id: str
approved: bool
approver_id: str
note: Optional[str] = None
class PendingApproval(BaseModel):
directive_id: str
agent: str
action: str
description: str
created_at: str
context: dict = {}
@router.get('/pending', response_model=list[PendingApproval])
async def list_pending():
from ..app_state import get_db_pool
pool = get_db_pool()
if pool is None:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='DB not ready')
async with pool.acquire(timeout=10) as conn:
rows = await conn.fetch(
'SELECT directive_id, agent_name, action_type, description, started_at, context_data '
'FROM ab_directive_log WHERE status = $1 ORDER BY started_at ASC',
'pending_approval',
)
return [
PendingApproval(
directive_id=str(r['directive_id']),
agent=r['agent_name'] or '',
action=r['action_type'] or '',
description=r['description'] or '',
created_at=str(r['started_at'] or ''),
context=r['context_data'] or {},
)
for r in rows
]
@router.post('/respond', status_code=status.HTTP_200_OK)
async def respond_approval(req: ApprovalRequest):
from ..app_state import get_db_pool, get_master_agent
pool = get_db_pool()
if pool is None:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail='DB not ready')
async with pool.acquire(timeout=10) as conn:
row = await conn.fetchrow(
'SELECT directive_id, status FROM ab_directive_log WHERE directive_id = $1',
req.directive_id,
)
if not row:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail='Directive not found')
if row['status'] != 'pending_approval':
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f'Directive is not pending approval (status={row["status"]})',
)
new_status = 'approved' if req.approved else 'rejected'
await conn.execute(
'UPDATE ab_directive_log SET status=$1, approver_id=$2, approval_note=$3, updated_at=NOW() '
'WHERE directive_id=$4',
new_status, req.approver_id, req.note, req.directive_id,
)
logger.info('Directive %s %s by %s', req.directive_id, new_status, req.approver_id)
if req.approved:
master = get_master_agent()
if master:
try:
await master.resume_directive(req.directive_id)
except Exception as exc:
logger.error('resume_directive failed %s: %s', req.directive_id, exc)
return {'directive_id': req.directive_id, 'status': new_status}