fix(approvals): accept top-level lead reasoning for create
This commit is contained in:
@@ -394,10 +394,13 @@ async def create_approval(
|
|||||||
_actor: ActorContext = ACTOR_DEP,
|
_actor: ActorContext = ACTOR_DEP,
|
||||||
) -> ApprovalRead:
|
) -> ApprovalRead:
|
||||||
"""Create an approval for a board."""
|
"""Create an approval for a board."""
|
||||||
|
payload_dict = payload.payload
|
||||||
|
if payload_dict is None and isinstance(payload.lead_reasoning, str) and payload.lead_reasoning.strip():
|
||||||
|
payload_dict = {"reason": payload.lead_reasoning.strip()}
|
||||||
task_ids = normalize_task_ids(
|
task_ids = normalize_task_ids(
|
||||||
task_id=payload.task_id,
|
task_id=payload.task_id,
|
||||||
task_ids=payload.task_ids,
|
task_ids=payload.task_ids,
|
||||||
payload=payload.payload,
|
payload=payload_dict,
|
||||||
)
|
)
|
||||||
task_id = task_ids[0] if task_ids else None
|
task_id = task_ids[0] if task_ids else None
|
||||||
if payload.status == "pending":
|
if payload.status == "pending":
|
||||||
@@ -411,7 +414,7 @@ async def create_approval(
|
|||||||
task_id=task_id,
|
task_id=task_id,
|
||||||
agent_id=payload.agent_id,
|
agent_id=payload.agent_id,
|
||||||
action_type=payload.action_type,
|
action_type=payload.action_type,
|
||||||
payload=payload.payload,
|
payload=payload_dict,
|
||||||
confidence=payload.confidence,
|
confidence=payload.confidence,
|
||||||
rubric_scores=payload.rubric_scores,
|
rubric_scores=payload.rubric_scores,
|
||||||
status=payload.status,
|
status=payload.status,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from datetime import datetime
|
|||||||
from typing import Literal, Self
|
from typing import Literal, Self
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from pydantic import model_validator
|
from pydantic import AliasChoices, Field as PydanticField, model_validator
|
||||||
from sqlmodel import Field, SQLModel
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
ApprovalStatus = Literal["pending", "approved", "rejected"]
|
ApprovalStatus = Literal["pending", "approved", "rejected"]
|
||||||
@@ -49,9 +49,18 @@ class ApprovalCreate(ApprovalBase):
|
|||||||
|
|
||||||
agent_id: UUID | None = None
|
agent_id: UUID | None = None
|
||||||
|
|
||||||
|
# Back-compat + ergonomics: some clients send lead reasoning as a top-level
|
||||||
|
# field (`reasoning` / `lead_reasoning`) rather than nesting under payload.reason.
|
||||||
|
lead_reasoning: str | None = PydanticField(
|
||||||
|
default=None,
|
||||||
|
validation_alias=AliasChoices("lead_reasoning", "reasoning", "leadReasoning"),
|
||||||
|
)
|
||||||
|
|
||||||
@model_validator(mode="after")
|
@model_validator(mode="after")
|
||||||
def validate_lead_reasoning(self) -> Self:
|
def validate_lead_reasoning(self) -> Self:
|
||||||
"""Ensure each approval request includes explicit lead reasoning."""
|
"""Ensure each approval request includes explicit lead reasoning."""
|
||||||
|
if isinstance(self.lead_reasoning, str) and self.lead_reasoning.strip():
|
||||||
|
return self
|
||||||
payload = self.payload
|
payload = self.payload
|
||||||
if isinstance(payload, dict):
|
if isinstance(payload, dict):
|
||||||
reason = payload.get("reason")
|
reason = payload.get("reason")
|
||||||
|
|||||||
@@ -38,6 +38,17 @@ def test_approval_create_requires_lead_reasoning() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_approval_create_accepts_top_level_reasoning_alias() -> None:
|
||||||
|
model = ApprovalCreate.model_validate(
|
||||||
|
{
|
||||||
|
"action_type": "task.update",
|
||||||
|
"confidence": 80,
|
||||||
|
"reasoning": "Lead says OK.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert model.lead_reasoning == "Lead says OK."
|
||||||
|
|
||||||
|
|
||||||
def test_approval_create_accepts_nested_decision_reason() -> None:
|
def test_approval_create_accepts_nested_decision_reason() -> None:
|
||||||
model = ApprovalCreate.model_validate(
|
model = ApprovalCreate.model_validate(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user