code editor: Agent Studio improvement iteration 2 for invoice-guard-studio-v1. Goal

This commit is contained in:
a2a-code-editor
2026-07-18 10:41:40 +00:00
parent 4d8daec055
commit 9bdbcf2421
2 changed files with 31 additions and 8 deletions

View File

@@ -44,6 +44,7 @@ MAX_INVOICES = 50
MAX_UPLOADS = 5
MAX_UPLOAD_BYTES = 256 * 1024
MAX_TEXT_FIELD_LENGTH = 180
AGENT_VERSION = "0.1.3"
ACCEPTED_MEDIA_TYPES = {"application/json", "text/json", "text/csv", "text/plain"}
DB_CONNECT_OPTIONS = "-c statement_timeout=5000 -c lock_timeout=3000 -c idle_in_transaction_session_timeout=10000"
@@ -81,7 +82,7 @@ class InvoiceGuardStudioV1(A2AAgent[InvoiceGuardStudioV1Config, PlatformUserAuth
"One-page InvoiceGuard app for deterministic duplicate invoice number "
"and stated-total mismatch review with user-scoped Postgres persistence."
)
version = "0.1.2"
version = AGENT_VERSION
config_model = InvoiceGuardStudioV1Config
auth_model = PlatformUserAuth
@@ -124,7 +125,7 @@ class InvoiceGuardStudioV1(A2AAgent[InvoiceGuardStudioV1Config, PlatformUserAuth
str,
Field(min_length=1, max_length=120, pattern=r"^[A-Za-z0-9_.:-]+$"),
],
invoices: Annotated[list[dict[str, Any]], Field(min_length=1, max_length=MAX_INVOICES)],
invoices: Annotated[list[InvoiceInput], Field(min_length=1, max_length=MAX_INVOICES)],
) -> dict[str, Any]:
tenant = _tenant_key(ctx)
result = _review_payload(case_id, invoices)
@@ -300,7 +301,7 @@ def _money_json(value: Decimal) -> float:
return float(value.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP))
def _review_payload(case_id: str, raw_invoices: list[dict[str, Any]]) -> dict[str, Any]:
def _review_payload(case_id: str, raw_invoices: list[InvoiceInput | dict[str, Any]]) -> dict[str, Any]:
try:
clean_case_id = _clean_case_id(case_id)
except ValueError as exc:
@@ -311,16 +312,29 @@ def _review_payload(case_id: str, raw_invoices: list[dict[str, Any]]) -> dict[st
return _validation_error("too_many_invoices", f"At most {MAX_INVOICES} invoices are allowed per review.", case_id=clean_case_id)
invoices: list[dict[str, Any]] = []
for index, item in enumerate(raw_invoices):
if not isinstance(item, dict):
for index, raw_item in enumerate(raw_invoices):
if isinstance(raw_item, InvoiceInput):
item = raw_item.model_dump()
elif isinstance(raw_item, dict):
try:
item = InvoiceInput.model_validate(raw_item).model_dump()
except ValidationError:
item = raw_item
else:
return _validation_error("invalid_invoice", f"Invoice at index {index} must be an object.", case_id=clean_case_id)
missing = [name for name in ("invoice_number", "vendor", "subtotal", "tax", "total") if item.get(name) in (None, "")]
invoice_number = str(item.get("invoice_number", "")).strip()
vendor = str(item.get("vendor", "")).strip()
if not invoice_number or not vendor:
missing.extend(name for name, value in (("invoice_number", invoice_number), ("vendor", vendor)) if not value and name not in missing)
if missing:
return _validation_error(
"incomplete_invoice",
f"Invoice at index {index} is missing required field(s): {', '.join(missing)}.",
case_id=clean_case_id,
)
if len(invoice_number) > 120 or len(vendor) > MAX_TEXT_FIELD_LENGTH:
return _validation_error("invalid_invoice", f"Invoice at index {index} exceeds text field length limits.", case_id=clean_case_id)
subtotal = _money(item.get("subtotal"))
tax = _money(item.get("tax"))
total = _money(item.get("total"))
@@ -329,8 +343,8 @@ def _review_payload(case_id: str, raw_invoices: list[dict[str, Any]]) -> dict[st
invoices.append(
{
"index": index,
"invoice_number": str(item["invoice_number"]).strip(),
"vendor": str(item["vendor"]).strip(),
"invoice_number": invoice_number,
"vendor": vendor,
"subtotal": _money_json(subtotal),
"tax": _money_json(tax),
"total": _money_json(total),
@@ -473,7 +487,7 @@ def _receipt_for(tenant: str, case: dict[str, Any], source: str, saved_at: str)
"receipt_id": f"invoice_guard:{case['case_id']}:{digest[:16]}",
"kind": "production_execution_receipt",
"agent": "invoice-guard-studio-v1",
"agent_version": "0.1.1",
"agent_version": AGENT_VERSION,
"skill": "review_invoices",
"tenant_hash": hashlib.sha256(tenant.encode("utf-8")).hexdigest()[:16],
"input_hash": digest,

View File

@@ -76,6 +76,15 @@ def test_incomplete_invoice_failure_fixture():
assert result["code"] == "incomplete_invoice"
assert "vendor" in result["message"] and "total" in result["message"]
blank = _review_payload("studio-invoice-invalid", [{"invoice_number": " ", "vendor": " ", "subtotal": 10, "tax": 1, "total": 11}])
assert blank["ok"] is False
assert blank["code"] == "incomplete_invoice"
assert "invoice_number" in blank["message"] and "vendor" in blank["message"]
oversized = _review_payload("studio-invoice-invalid", [{"invoice_number": "I" * 121, "vendor": "Acme", "subtotal": 10, "tax": 1, "total": 11}])
assert oversized["ok"] is False
assert oversized["code"] == "invalid_invoice"
def test_browser_upload_bridge_bounds_and_parses_json():
payload = json.dumps({"invoices": SUCCESS_INVOICES}).encode()