ledgershield / inference.py
king673134's picture
Upload folder using huggingface_hub
130fcca verified
Raw
History Blame Contribute Delete
119 kB
"""
Inference Script Example
===================================
MANDATORY
- Before submitting, ensure the following variables are defined in your environment configuration:
API_BASE_URL The API endpoint for the LLM.
MODEL_NAME The model identifier to use for inference.
HF_TOKEN Your Hugging Face / API key.
LOCAL_IMAGE_NAME The name of the local image to use for the environment if you are using from_docker_image().
- Defaults are set only for API_BASE_URL and MODEL_NAME
(and should reflect your active inference setup):
API_BASE_URL = os.getenv("API_BASE_URL", "<your-active-endpoint>")
MODEL_NAME = os.getenv("MODEL_NAME", "<your-active-model>")
- The inference script must be named inference.py and placed in the root directory of the project.
- Participants must use OpenAI Client for all LLM calls using the variables above.
- I've read the sample inference.py and have followed it strictly.
STDOUT FORMAT
- The script emits exactly three line types to stdout, in this order:
[START] task=<task_name> env=<benchmark> model=<model_name>
[STEP] step=<n> action=<action_str> reward=<0.00> done=<true|false> error=<msg|null>
[END] success=<true|false> steps=<n> score=<score> rewards=<r1,r2,...,rn>
Rules:
- One [START] line at episode begin.
- One [STEP] line per step, immediately after env.step() returns.
- One [END] line after env.close(), always emitted even on exception.
- reward and rewards are formatted to 2 decimal places.
- done and success are lowercase booleans: true or false.
- error is the raw last_action_error string, or null if none.
- All fields are on a single line with no newlines within a line.
- Each task should return score strictly between 0 and 1, not 0.00 and not 1.00.
Example:
[START] task=click-test env=miniwob model=Qwen3-VL-30B
[STEP] step=1 action=click('123') reward=0.00 done=false error=null
[STEP] step=2 action=fill('456','text') reward=0.00 done=false error=null
[STEP] step=3 action=click('789') reward=0.99 done=true error=null
[END] success=true steps=3 score=0.99 rewards=0.00,0.00,0.99
"""
from __future__ import annotations
import os
import sys
if os.getenv("LEDGERSHIELD_DEBUG") != "1":
sys.stderr = open(os.devnull, "w", encoding="utf-8")
import argparse
from collections import Counter
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
import json
from pathlib import Path
import re
import warnings
from typing import Any, Optional
warnings.filterwarnings("ignore", category=DeprecationWarning)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
from openai import OpenAI
from ledgershield_env import LedgerShieldAction, LedgerShieldEnv
from openenv_compat import StepResult
from llm_utils import create_json_chat_completion, parse_json_dict
from server.environment import LedgerShieldEnvironment
from server.schema import canonical_reason_codes
from task_c_guardrails import (
grounded_task_c_submission,
sanitize_task_c_submission,
validate_task_c_submission,
)
from task_d_guardrails import (
derive_email_thread_signals as _derive_email_thread_signals,
grounded_task_d_submission,
policy_check_payload as _policy_check_payload,
sanitize_task_d_submission,
validate_task_d_submission,
)
# Required runtime configuration. Only the endpoint/model have defaults.
API_BASE_URL = os.getenv("API_BASE_URL", "https://api.openai.com/v1")
MODEL_NAME = os.getenv("MODEL_NAME", "gpt-5.4")
HF_TOKEN = os.getenv("HF_TOKEN")
# Optional when running with from_docker_image().
LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
ENV_URL = os.getenv("ENV_URL") or "http://localhost:8000"
BENCHMARK = "ledgershield"
MAX_STEPS = 20
TEMPERATURE = 0.0
MAX_TOKENS = 512
SUCCESS_SCORE_THRESHOLD = 0.85
PASSK_SUCCESS_THRESHOLD = 0.85
TASK_SCORE_MIN = 0.01
TASK_SCORE_MAX = 0.99
ARTIFACT_DIR = Path("artifacts")
DEFAULT_CASES = [
"CASE-A-001",
"CASE-A-002",
"CASE-A-003",
"CASE-A-004",
"CASE-B-001",
"CASE-B-002",
"CASE-B-003",
"CASE-B-004",
"CASE-B-005",
"CASE-C-001",
"CASE-C-002",
"CASE-C-003",
"CASE-C-004",
"CASE-D-001",
"CASE-D-002",
"CASE-D-003",
"CASE-D-004",
"CASE-D-005",
"CASE-D-006",
"CASE-E-001",
"CASE-E-002",
]
@dataclass(frozen=True)
class ModelCapabilityProfile:
model_name: str
capability_score: float
tier: str
plan_mode: str
repair_level: str
investigation_budget_bonus: int
intervention_budget_bonus: int
decision_token_budget: int
planning_token_budget: int
def _base_model_score(model_name: str) -> float:
normalized = normalize_text(model_name)
if "gpt-4o" in normalized:
base = 4.6
else:
match = re.search(r"gpt-([0-9]+(?:\.[0-9]+)?)", normalized)
if match:
base = float(match.group(1))
elif "gpt-4" in normalized:
base = 4.0
elif "gpt-3.5" in normalized:
base = 3.5
else:
base = 4.0
if "pro" in normalized:
base += 0.5
if "latest" in normalized:
base += 0.2
if "mini" in normalized:
base -= 0.7
if "nano" in normalized:
base -= 1.1
if "turbo" in normalized:
base -= 0.3
return base
def get_model_capability_profile(model_name: str) -> ModelCapabilityProfile:
score = _base_model_score(model_name)
if score >= 5.0:
return ModelCapabilityProfile(
model_name=model_name,
capability_score=score,
tier="elite",
plan_mode="llm",
repair_level="partial",
investigation_budget_bonus=2,
intervention_budget_bonus=2,
decision_token_budget=max(MAX_TOKENS, 1536),
planning_token_budget=640,
)
if score >= 4.5:
return ModelCapabilityProfile(
model_name=model_name,
capability_score=score,
tier="strong",
plan_mode="hybrid",
repair_level="partial",
investigation_budget_bonus=1,
intervention_budget_bonus=1,
decision_token_budget=max(MAX_TOKENS, 1280),
planning_token_budget=512,
)
return ModelCapabilityProfile(
model_name=model_name,
capability_score=score,
tier="standard",
plan_mode="llm",
repair_level="none",
investigation_budget_bonus=0,
intervention_budget_bonus=0,
decision_token_budget=MAX_TOKENS,
planning_token_budget=384,
)
def current_model_profile() -> ModelCapabilityProfile:
return get_model_capability_profile(MODEL_NAME)
def normalize_text(value: Any) -> str:
if value is None:
return ""
return " ".join(str(value).strip().lower().split())
def safe_float(value: Any) -> float:
try:
if isinstance(value, str):
cleaned = (
value.replace(",", "")
.replace("$", "")
.replace("€", "")
.replace("₹", "")
.strip()
)
return float(cleaned)
return float(value)
except Exception:
return 0.0
def clamp(value: float, lo: float, hi: float) -> float:
return max(lo, min(hi, value))
def normalize_score(value: Any) -> float:
return clamp(safe_float(value), TASK_SCORE_MIN, TASK_SCORE_MAX)
def format_decimal(value: Any) -> str:
numeric = safe_float(value)
if abs(numeric) < 0.005:
numeric = 0.0
return f"{numeric:.2f}"
def compact_json(value: Any) -> str:
return json.dumps(value, separators=(",", ":"), ensure_ascii=True, sort_keys=True)
def sanitize_log_field(value: Any) -> str:
if value is None:
return "null"
text = " ".join(str(value).split())
return text if text else "null"
def format_action(action: LedgerShieldAction) -> str:
return f"{action.action_type}({compact_json(action.payload)})"
def log_start(task: str, env: str, model: str) -> None:
print(f"[START] task={task} env={env} model={sanitize_log_field(model)}", flush=True)
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str]) -> None:
print(
"[STEP] "
f"step={step} "
f"action={sanitize_log_field(action)} "
f"reward={format_decimal(reward)} "
f"done={str(done).lower()} "
f"error={sanitize_log_field(error)}",
flush=True,
)
def log_end(success: bool, steps: int, rewards: list[float], score: Optional[float] = None) -> None:
final_score = rewards[-1] if score is None and rewards else (0.0 if score is None else score)
rewards_str = ",".join(format_decimal(reward) for reward in rewards)
print(
"[END] "
f"success={str(success).lower()} "
f"steps={steps} "
f"score={format_decimal(normalize_score(final_score))} "
f"rewards={rewards_str}",
flush=True,
)
def trace(message: str) -> None:
if os.getenv("LEDGERSHIELD_DEBUG") == "1":
print(message, file=sys.stderr, flush=True)
def token_ref(token: dict[str, Any], doc_id: str) -> dict[str, Any]:
return {
"doc_id": doc_id,
"page": int(token.get("page", 1) or 1),
"bbox": token.get("bbox", []),
"token_ids": [str(token.get("token_id", ""))],
}
def _looks_like_token_ref(value: Any) -> bool:
return isinstance(value, dict) and {"doc_id", "page", "bbox", "token_ids"} <= set(value)
def parse_invoice_tokens(tokens: list[dict[str, Any]], doc_id: str) -> tuple[dict[str, Any], dict[str, Any], list[dict[str, Any]]]:
fields: dict[str, Any] = {}
evidence: dict[str, Any] = {}
line_items: list[dict[str, Any]] = []
for idx, token in enumerate(tokens):
text = str(token.get("text", "")).strip()
lower = text.lower()
if idx == 0 and text:
fields["vendor_name"] = text
evidence["vendor_name"] = token_ref(token, doc_id)
continue
matchers = [
("invoice_number", r"invoice\s*no\s*:\s*(.+)$"),
("invoice_date", r"invoice\s*date\s*:\s*(.+)$"),
("currency", r"currency\s*:\s*(.+)$"),
("po_id", r"po\s*:\s*(.+)$"),
("receipt_id", r"receipt\s*:\s*(.+)$"),
("bank_account", r"bank\s*:\s*(.+)$"),
]
matched_field = False
for key, pattern in matchers:
match = re.match(pattern, text, flags=re.IGNORECASE)
if match:
fields[key] = match.group(1).strip()
evidence[key] = token_ref(token, doc_id)
matched_field = True
break
if matched_field:
continue
numeric_fields = [
("subtotal", r"subtotal\s*:\s*([\d,]+(?:\.\d+)?)$"),
("tax", r"tax\s*:\s*([\d,]+(?:\.\d+)?)$"),
("total", r"total\s*:\s*([\d,]+(?:\.\d+)?)$"),
]
for key, pattern in numeric_fields:
match = re.match(pattern, text, flags=re.IGNORECASE)
if match:
fields[key] = safe_float(match.group(1))
evidence[key] = token_ref(token, doc_id)
matched_field = True
break
if matched_field:
continue
if "|" not in text:
continue
parts = [part.strip() for part in text.split("|")]
if len(parts) != 4:
continue
description = parts[0]
qty_value = safe_float(parts[1])
unit_price = safe_float(parts[2])
line_total = safe_float(parts[3])
qty = int(qty_value) if qty_value.is_integer() else qty_value
line_items.append(
{
"description": description,
"qty": qty,
"unit_price": unit_price,
"line_total": line_total,
}
)
return fields, evidence, line_items
def parse_email_tokens(tokens: list[dict[str, Any]], doc_id: str) -> dict[str, Any]:
evidence: dict[str, Any] = {}
for token in tokens:
text = str(token.get("text", "")).strip()
lower = text.lower()
if lower.startswith("from:"):
evidence["from_header"] = token_ref(token, doc_id)
elif lower.startswith("subject:"):
evidence["subject_header"] = token_ref(token, doc_id)
elif "approval threshold" in lower or "split the request" in lower or "split invoice" in lower:
evidence["approval_threshold_evasion"] = token_ref(token, doc_id)
elif (
"skip callback" in lower
or "do not call" in lower
or "don't call" in lower
or "override policy" in lower
or "bypass policy" in lower
or "do not verify" in lower
or "source of truth" in lower
or "avoid reapproval" in lower
):
evidence["policy_bypass_attempt"] = token_ref(token, doc_id)
return evidence
def _domain_tokens(domain: str) -> set[str]:
return {part for part in re.split(r"[^a-z0-9]+", normalize_text(domain)) if len(part) > 1}
def _vendor_name_tokens(vendor_name: str) -> set[str]:
stop_words = {
"and",
"co",
"company",
"corp",
"corporation",
"gmbh",
"inc",
"industries",
"industrial",
"limited",
"ltd",
"manufacturing",
"pvt",
"supplies",
}
tokens = re.split(r"[^a-z0-9]+", normalize_text(vendor_name))
return {token for token in tokens if len(token) > 2 and token not in stop_words}
def _sender_domain(sender: str) -> str:
sender_text = normalize_text(sender)
if "@" not in sender_text:
return ""
return sender_text.split("@", 1)[-1]
def _infer_domain_alignment(*, sender: str, vendor_name: str, approved_domains: list[str]) -> tuple[str, str]:
sender_domain = _sender_domain(sender)
normalized_domains = [normalize_text(domain) for domain in approved_domains if normalize_text(domain)]
expected_domain = normalized_domains[0] if normalized_domains else ""
if sender_domain and expected_domain:
return ("aligned" if sender_domain == expected_domain else "mismatch", expected_domain)
vendor_tokens = _vendor_name_tokens(vendor_name)
domain_tokens = _domain_tokens(sender_domain)
if vendor_tokens and domain_tokens and vendor_tokens & domain_tokens:
return "aligned", expected_domain
if sender_domain and vendor_tokens:
return "mismatch", expected_domain
return "aligned", expected_domain
def derive_email_thread_from_ocr(collected: dict[str, Any]) -> dict[str, Any]:
tokens = list(collected.get("email_tokens", []) or [])
if not tokens:
return {}
lines = [str(token.get("text", "")).strip() for token in tokens if str(token.get("text", "")).strip()]
sender = ""
subject = ""
body_lines: list[str] = []
for line in lines:
lower = line.lower()
if lower.startswith("from:"):
sender = line.split(":", 1)[-1].strip()
continue
if lower.startswith("subject:"):
subject = line.split(":", 1)[-1].strip()
continue
body_lines.append(line)
vendor = collected.get("vendor") or {}
vendor_name = str((vendor or {}).get("vendor_name") or collected.get("invoice_fields", {}).get("vendor_name") or "")
approved_domains = [str(domain).strip() for domain in (vendor.get("approved_domains", []) or []) if str(domain).strip()]
domain_alignment, expected_domain = _infer_domain_alignment(
sender=sender,
vendor_name=vendor_name,
approved_domains=approved_domains,
)
body_text = " ".join(body_lines)
normalized_body = normalize_text(body_text)
normalized_subject = normalize_text(subject)
explicit_no_change = any(
phrase in normalized_body
for phrase in {
"no bank change",
"no bank changes",
"no change to bank",
"approved remittance instructions already on file",
}
)
bank_change_language = (
"bank" in normalized_body
and any(word in normalized_body for word in {"change", "changed", "update", "updated", "override", "directed"})
and not explicit_no_change
)
urgency_language = any(
phrase in normalized_subject or phrase in normalized_body
for phrase in {"urgent", "immediately", "asap", "today"}
)
callback_discouraged = any(
phrase in normalized_body
for phrase in {"skip callback", "do not call", "don't call", "do not verify"}
)
policy_override_language = any(
phrase in normalized_body
for phrase in {
"override policy",
"bypass policy",
"source of truth",
"avoid reapproval",
"do not contact",
"skip normal review",
"ignore the standard workflow",
"personally approved",
}
)
suspicious_bank_change = bank_change_language and (
domain_alignment == "mismatch"
or callback_discouraged
or policy_override_language
or urgency_language
)
derived_flags = canonical_reason_codes(
[
"sender_domain_spoof" if domain_alignment == "mismatch" else "",
"bank_override_attempt" if suspicious_bank_change else "",
"policy_bypass_attempt" if callback_discouraged or policy_override_language else "",
"urgent_payment_pressure" if urgency_language else "",
"approval_threshold_evasion"
if any(phrase in normalized_body for phrase in {"approval threshold", "split the request", "split invoice"})
else "",
]
)
return {
"thread_id": collected.get("email_doc_id") or collected.get("case_metadata", {}).get("thread_id"),
"vendor_key": (vendor or {}).get("vendor_key"),
"sender": sender,
"subject": subject,
"body": body_text,
"message_count": max(1, len(body_lines)),
"sender_profile": {
"from_domain": _sender_domain(sender),
"expected_domain": expected_domain,
"domain_alignment": domain_alignment,
},
"request_signals": {
"bank_change_language": bank_change_language,
"urgency_language": urgency_language,
"callback_discouraged": callback_discouraged,
"policy_override_language": policy_override_language,
},
"derived_flags": derived_flags,
}
def refresh_email_thread_from_ocr(collected: dict[str, Any]) -> None:
derived = derive_email_thread_from_ocr(collected)
if not derived:
return
current = collected.get("email_thread") or {}
merged = dict(current)
for key, value in derived.items():
if key in {"sender_profile", "request_signals"}:
existing = current.get(key, {}) or {}
candidate = value if isinstance(value, dict) else {}
merged[key] = {**candidate, **existing} if existing else candidate
continue
if current.get(key) not in (None, "", [], {}):
merged[key] = current.get(key)
continue
merged[key] = value
merged["flags"] = canonical_reason_codes(
(current.get("flags", []) or [])
+ (current.get("derived_flags", []) or [])
+ (derived.get("flags", []) or [])
+ (derived.get("derived_flags", []) or [])
)
merged["derived_flags"] = canonical_reason_codes(
(current.get("derived_flags", []) or []) + (derived.get("derived_flags", []) or [])
)
collected["email_thread"] = merged
def derive_email_thread_signals(thread: dict[str, Any]) -> set[str]:
return _derive_email_thread_signals(thread)
def vendor_key_for(fields: dict[str, Any]) -> str:
return normalize_text(fields.get("vendor_name"))
def vendor_history_key_for(fields: dict[str, Any]) -> str:
explicit = vendor_key_for(fields)
if explicit:
return explicit
vendor_name = normalize_text(fields.get("vendor_name"))
if not vendor_name:
return ""
return re.sub(r"[^a-z0-9]+", "-", vendor_name).strip("-")
def action_signature(action: LedgerShieldAction) -> str:
return f"{normalize_text(action.action_type)}:{compact_json(action.payload)}"
def _append_candidate(
candidates: list[LedgerShieldAction],
seen_signatures: set[str],
action: LedgerShieldAction,
) -> None:
signature = action_signature(action)
if signature in seen_signatures:
return
seen_signatures.add(signature)
candidates.append(action)
def _invoice_record_summary(record: dict[str, Any]) -> dict[str, Any]:
fields = record.get("fields", {}) or {}
return {
"doc_id": record.get("doc_id"),
"invoice_number": fields.get("invoice_number"),
"invoice_date": fields.get("invoice_date"),
"total": fields.get("total"),
"bank_account": fields.get("bank_account"),
}
def summarize_collected_state(collected: dict[str, Any]) -> dict[str, Any]:
email_thread = collected.get("email_thread") or {}
ledger_search = collected.get("ledger_search") or {}
return {
"case_instruction": str(collected.get("case_instruction", "") or ""),
"invoice_records": [_invoice_record_summary(record) for record in collected.get("invoice_records", []) or []],
"email_thread": {
"sender": email_thread.get("sender"),
"subject": email_thread.get("subject"),
"flags": email_thread.get("flags"),
"derived_flags": email_thread.get("derived_flags"),
"sender_profile": email_thread.get("sender_profile"),
"request_signals": email_thread.get("request_signals"),
},
"vendor": {
"vendor_key": (collected.get("vendor") or {}).get("vendor_key"),
"approved_domains": (collected.get("vendor") or {}).get("approved_domains"),
},
"bank_compares": [
{
"proposed_bank_account": compare.get("proposed_bank_account"),
"matched": compare.get("matched"),
"vendor_bank_account": compare.get("vendor_bank_account"),
}
for compare in collected.get("bank_compares", []) or []
],
"ledger_search": {
"exact_duplicate_count": ledger_search.get("exact_duplicate_count"),
"near_duplicate_count": ledger_search.get("near_duplicate_count"),
"top_hits": [
{
"ledger_id": hit.get("ledger_id"),
"invoice_number": hit.get("invoice_number"),
"amount": hit.get("amount"),
"match_score": hit.get("match_score"),
}
for hit in (collected.get("ledger_hits", []) or [])[:5]
],
},
"vendor_history": [
{
"event_type": row.get("event_type") or row.get("change_type"),
"status": row.get("status"),
}
for row in (collected.get("vendor_history", []) or [])[:5]
],
"has_policy_snapshot": bool(collected.get("policies")),
"email_ocr_loaded": bool(collected.get("email_tokens")),
"revealed_artifacts": sorted((collected.get("revealed_artifacts", {}) or {}).keys()),
"pending_event_count": len(collected.get("pending_events", []) or []),
"observed_risk_signals": collected.get("observed_risk_signals", []) or [],
"tool_failures": {
tool_name: len(entries)
for tool_name, entries in (collected.get("tool_failures", {}) or {}).items()
},
}
def _store_artifact(collected: dict[str, Any], artifact: dict[str, Any]) -> None:
artifact_id = normalize_text(artifact.get("artifact_id"))
if not artifact_id:
return
artifact_store = collected.setdefault("revealed_artifacts", {})
artifact_store[artifact_id] = artifact
if artifact_id == "callback_verification_result":
collected["callback_result"] = artifact
elif artifact_id == "bank_change_approval_chain":
collected["bank_change_approval_chain"] = artifact
elif artifact_id == "po_reconciliation_report":
collected["po_reconciliation_report"] = artifact
elif artifact_id == "receipt_reconciliation_report":
collected["receipt_reconciliation_report"] = artifact
elif artifact_id == "duplicate_cluster_report":
collected["duplicate_cluster_report"] = artifact
def _record_tool_failure(collected: dict[str, Any], action: LedgerShieldAction, tool: dict[str, Any]) -> None:
tool_name = normalize_text(tool.get("tool_name") or action.action_type)
failure_log = collected.setdefault("tool_failures", {})
failure_log.setdefault(tool_name, []).append(
{
"payload": dict(action.payload),
"error": str(tool.get("error") or ""),
}
)
def _capture_tool_artifacts(collected: dict[str, Any], tool: dict[str, Any]) -> None:
if isinstance(tool.get("artifact"), dict):
_store_artifact(collected, tool["artifact"])
for artifact in tool.get("async_artifacts", []) or []:
if isinstance(artifact, dict):
_store_artifact(collected, artifact)
for artifact_id in tool.get("revealed_artifact_ids", []) or []:
normalized_id = normalize_text(artifact_id)
if normalized_id:
collected.setdefault("revealed_artifacts", {}).setdefault(
normalized_id,
{"artifact_id": artifact_id},
)
def update_collected_from_observation(collected: dict[str, Any], observation: Any) -> None:
revealed_artifacts = list(getattr(observation, "revealed_artifacts", []) or [])
pending_events = list(getattr(observation, "pending_events", []) or [])
case_metadata = getattr(observation, "case_metadata", {}) or {}
risk_snapshot = getattr(observation, "risk_snapshot", {}) or {}
collected["case_metadata"] = dict(case_metadata)
collected["pending_events"] = pending_events
collected["observed_risk_signals"] = list(risk_snapshot.get("observed_signals", []) or [])
if revealed_artifacts:
for artifact in revealed_artifacts:
if isinstance(artifact, dict):
_store_artifact(collected, artifact)
def build_investigation_candidates(
task_type: str,
collected: dict[str, Any],
*,
vendor_key: str,
po_id: str,
receipt_id: str,
invoice_total: float,
invoice_number: str,
proposed_bank_account: str,
email_doc_id: str,
executed_signatures: set[str],
) -> list[LedgerShieldAction]:
candidates: list[LedgerShieldAction] = []
seen = set(executed_signatures)
history_lookup_key = vendor_key or vendor_history_key_for(collected.get("invoice_fields", {}) or {})
instruction = normalize_text(collected.get("case_instruction", ""))
task_c_policy_hint = any(
phrase in instruction
for phrase in {
"approval threshold",
"structured below",
"split invoice",
"policy",
}
)
if task_type == "task_b":
_append_candidate(candidates, seen, LedgerShieldAction(action_type="lookup_policy", payload={}))
if po_id:
_append_candidate(candidates, seen, LedgerShieldAction(action_type="lookup_po", payload={"po_id": po_id}))
resolved_receipt_id = receipt_id or po_id.replace("PO-", "GRN-", 1)
if resolved_receipt_id:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="lookup_receipt", payload={"receipt_id": resolved_receipt_id}),
)
return candidates
if task_type == "task_c":
if vendor_key:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="lookup_vendor", payload={"vendor_key": vendor_key}),
)
if history_lookup_key:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="lookup_vendor_history", payload={"vendor_key": history_lookup_key}),
)
if task_c_policy_hint:
_append_candidate(candidates, seen, LedgerShieldAction(action_type="lookup_policy", payload={}))
if vendor_key and (invoice_number or invoice_total):
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="search_ledger",
payload={
"vendor_key": vendor_key,
"invoice_number": invoice_number,
"amount": invoice_total,
},
),
)
if invoice_number or invoice_total:
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="search_ledger",
payload={
"invoice_number": invoice_number,
"amount": invoice_total,
},
),
)
if vendor_key and proposed_bank_account:
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="compare_bank_account",
payload={"vendor_key": vendor_key, "proposed_bank_account": proposed_bank_account},
),
)
return candidates
if email_doc_id and not collected.get("email_tokens"):
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="ocr", payload={"doc_id": email_doc_id, "mode": "accurate"}),
)
if vendor_key:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="lookup_vendor", payload={"vendor_key": vendor_key}),
)
if email_doc_id:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="inspect_email_thread", payload={"thread_id": email_doc_id}),
)
if history_lookup_key:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="lookup_vendor_history", payload={"vendor_key": history_lookup_key}),
)
_append_candidate(candidates, seen, LedgerShieldAction(action_type="lookup_policy", payload={}))
if po_id:
_append_candidate(candidates, seen, LedgerShieldAction(action_type="lookup_po", payload={"po_id": po_id}))
resolved_receipt_id = receipt_id or (po_id.replace("PO-", "GRN-", 1) if po_id else "")
if resolved_receipt_id:
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="lookup_receipt", payload={"receipt_id": resolved_receipt_id}),
)
for record in collected.get("invoice_records", []) or []:
fields = record.get("fields", {}) or {}
record_bank = str(fields.get("bank_account", "")).strip()
record_invoice_number = str(fields.get("invoice_number", "")).strip()
record_total = safe_float(fields.get("total"))
if vendor_key and record_bank:
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="compare_bank_account",
payload={"vendor_key": vendor_key, "proposed_bank_account": record_bank},
),
)
if vendor_key and (record_invoice_number or record_total):
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="search_ledger",
payload={
"vendor_key": vendor_key,
"invoice_number": record_invoice_number,
"amount": record_total,
},
),
)
elif record_invoice_number or record_total:
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="search_ledger",
payload={
"invoice_number": record_invoice_number,
"amount": record_total,
},
),
)
return candidates
def build_intervention_candidates(
task_type: str,
collected: dict[str, Any],
submission: dict[str, Any],
*,
executed_signatures: set[str],
) -> list[LedgerShieldAction]:
decision = normalize_text(submission.get("decision"))
if decision not in {"hold", "needs_review", "escalate_fraud"}:
return []
candidates: list[LedgerShieldAction] = []
seen = set(executed_signatures)
reason_codes = set(
canonical_reason_codes(
(submission.get("reason_codes", []) or [])
+ (submission.get("fraud_flags", []) or [])
+ (submission.get("campaign_signals", []) or [])
)
)
ledger_search = collected.get("ledger_search") or {}
ledger_reviewed = bool(ledger_search)
has_duplicates = bool(collected.get("ledger_hits")) or int(ledger_search.get("exact_duplicate_count", 0) or 0) > 0
has_duplicates = has_duplicates or int(ledger_search.get("near_duplicate_count", 0) or 0) > 0
bank_mismatch = any(compare and not bool(compare.get("matched")) for compare in collected.get("bank_compares", []) or [])
email_thread = collected.get("email_thread") or {}
email_flags = set(
canonical_reason_codes((email_thread.get("flags", []) or []) + (email_thread.get("derived_flags", []) or []))
)
observed_signals = {normalize_text(signal) for signal in collected.get("observed_risk_signals", []) or []}
instruction = normalize_text(collected.get("case_instruction", ""))
task_c_campaign_hint = any(
phrase in instruction
for phrase in {
"cross-vendor",
"coordinated fraud",
"coordinated payment",
"similar amounts and timing",
"approval threshold",
"structured below",
"split invoice",
}
)
if task_type == "task_b":
if str((collected.get("invoice_fields", {}) or {}).get("po_id", "")).strip():
_append_candidate(candidates, seen, LedgerShieldAction(action_type="request_po_reconciliation", payload={}))
if str((collected.get("invoice_fields", {}) or {}).get("receipt_id", "")).strip() or (
normalize_text(collected.get("case_instruction", "")) and not collected.get("receipt")
):
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="request_additional_receipt_evidence", payload={}),
)
if normalize_text(submission.get("decision")) == "hold" or not collected.get("po") or not collected.get("receipt"):
_append_candidate(candidates, seen, LedgerShieldAction(action_type="request_callback_verification", payload={}))
return candidates
needs_callback = (
bank_mismatch
or "bank_override_attempt" in reason_codes
or has_duplicates
or task_c_campaign_hint
or {"approval_threshold_evasion", "shared_bank_account", "coordinated_timing"} & reason_codes
or {"sender_domain_spoof", "policy_bypass_attempt", "urgent_payment_pressure"} & (reason_codes | email_flags)
or {"callback_suspicious_confirm", "callback_dispute_confirmed"} & observed_signals
)
if needs_callback:
_append_candidate(candidates, seen, LedgerShieldAction(action_type="request_callback_verification", payload={}))
if bank_mismatch or "bank_override_attempt" in reason_codes:
_append_candidate(candidates, seen, LedgerShieldAction(action_type="request_callback_verification", payload={}))
_append_candidate(
candidates,
seen,
LedgerShieldAction(action_type="request_bank_change_approval_chain", payload={}),
)
_append_candidate(candidates, seen, LedgerShieldAction(action_type="freeze_vendor_profile", payload={}))
elif task_type == "task_e" and decision == "escalate_fraud":
_append_candidate(candidates, seen, LedgerShieldAction(action_type="freeze_vendor_profile", payload={}))
duplicate_review_needed = has_duplicates or bool(
{"duplicate_near_match", "approval_threshold_evasion", "shared_bank_account", "coordinated_timing"} & reason_codes
)
if duplicate_review_needed or task_c_campaign_hint or (
task_type in {"task_c", "task_d", "task_e"}
and decision in {"needs_review", "escalate_fraud"}
and ledger_reviewed
):
_append_candidate(candidates, seen, LedgerShieldAction(action_type="flag_duplicate_cluster_review", payload={}))
if {"sender_domain_spoof", "policy_bypass_attempt", "urgent_payment_pressure"} & (reason_codes | email_flags):
_append_candidate(candidates, seen, LedgerShieldAction(action_type="route_to_security", payload={}))
if task_type in {"task_d", "task_e"}:
_append_candidate(
candidates,
seen,
LedgerShieldAction(
action_type="create_human_handoff",
payload={
"summary": (
"Potential coordinated payment campaign detected across linked invoices."
if task_type == "task_e"
else "Risk indicators support fraud escalation and human review."
),
"recommended_next_step": (
"campaign_freeze_and_manual_review"
if task_type == "task_e"
else "security_review_and_callback_followup"
),
"confidence": submission.get("confidence", 0.9),
},
),
)
return candidates
def _action_priority(
task_type: str,
phase: str,
collected: dict[str, Any],
action: LedgerShieldAction,
current_submission: Optional[dict[str, Any]],
) -> tuple[int, int]:
action_type = normalize_text(action.action_type)
decision = normalize_text((current_submission or {}).get("decision"))
instruction = normalize_text(collected.get("case_instruction", ""))
reason_codes = {
normalize_text(code)
for code in (
(current_submission or {}).get("reason_codes", [])
+ (current_submission or {}).get("fraud_flags", [])
+ (current_submission or {}).get("campaign_signals", [])
)
if normalize_text(code)
}
observed = {normalize_text(signal) for signal in collected.get("observed_risk_signals", []) or []}
base_priority = {
"investigation": {
"ocr": 80,
"inspect_email_thread": 95,
"lookup_vendor": 88,
"lookup_vendor_history": 86,
"lookup_policy": 70,
"lookup_po": 92,
"lookup_receipt": 90,
"compare_bank_account": 94,
"search_ledger": 90,
},
"intervention": {
"request_callback_verification": 100,
"request_bank_change_approval_chain": 98,
"request_po_reconciliation": 96,
"request_additional_receipt_evidence": 94,
"flag_duplicate_cluster_review": 92,
"freeze_vendor_profile": 88,
"route_to_security": 86,
"create_human_handoff": 82,
"route_to_procurement": 75,
},
}
score = base_priority.get(phase, {}).get(action_type, 50)
if phase == "investigation":
if task_type == "task_b" and action_type in {"lookup_po", "lookup_receipt", "lookup_policy"}:
score += 8
if task_type == "task_c" and action_type == "lookup_vendor":
score += 6
if task_type == "task_c" and action_type == "lookup_vendor_history":
score += 4
if task_type == "task_c" and action_type == "lookup_policy" and any(
phrase in instruction for phrase in {"approval threshold", "structured below", "split invoice"}
):
score += 24
if task_type in {"task_d", "task_e"} and action_type == "inspect_email_thread":
score += 10
if task_type in {"task_d", "task_e"} and action_type in {"lookup_po", "lookup_receipt"}:
score += 6
if task_type in {"task_d", "task_e"} and action_type == "lookup_policy":
score += 8
if action_type == "compare_bank_account" and collected.get("invoice_fields", {}).get("bank_account"):
score += 6
if action_type == "search_ledger" and (action.payload.get("invoice_number") or action.payload.get("amount")):
score += 5
if action_type == "ocr" and action.payload.get("doc_id") == collected.get("email_doc_id"):
score += 15 if task_type in {"task_d", "task_e"} else 8
else:
if action_type == "request_callback_verification" and decision != "pay":
score += 8
if action_type == "request_bank_change_approval_chain" and (
"bank_override_attempt" in reason_codes or "bank_account_mismatch" in observed
):
score += 8
if action_type == "flag_duplicate_cluster_review" and (
{"duplicate_near_match", "approval_threshold_evasion", "shared_bank_account", "coordinated_timing"} & reason_codes
):
score += 8
if action_type == "flag_duplicate_cluster_review" and (
task_type in {"task_c", "task_d", "task_e"} and decision != "pay" and bool(collected.get("ledger_search"))
):
score += 8
if action_type == "freeze_vendor_profile" and (
"bank_override_attempt" in reason_codes or "bank_account_mismatch" in observed or task_type == "task_e"
):
score += 6
if action_type == "route_to_security" and (
{"sender_domain_spoof", "policy_bypass_attempt", "urgent_payment_pressure"} & reason_codes
):
score += 7
if action_type == "create_human_handoff" and decision in {"needs_review", "escalate_fraud", "hold"}:
score += 4
return score, -len(action.payload)
def _rank_candidate_actions(
task_type: str,
phase: str,
collected: dict[str, Any],
candidates: list[LedgerShieldAction],
current_submission: Optional[dict[str, Any]],
) -> list[LedgerShieldAction]:
indexed = list(enumerate(candidates))
indexed.sort(
key=lambda item: (
*_action_priority(task_type, phase, collected, item[1], current_submission),
-item[0],
),
reverse=True,
)
return [action for _, action in indexed]
def _merge_action_batches(
primary: list[LedgerShieldAction],
secondary: list[LedgerShieldAction],
*,
max_actions: int,
) -> list[LedgerShieldAction]:
merged: list[LedgerShieldAction] = []
seen: set[str] = set()
for batch in (primary, secondary):
for action in batch:
signature = action_signature(action)
if signature in seen:
continue
seen.add(signature)
merged.append(action)
if len(merged) >= max_actions:
return merged
return merged
def _llm_selected_actions(
client: Optional[OpenAI],
*,
task_type: str,
phase: str,
collected: dict[str, Any],
candidates: list[LedgerShieldAction],
max_actions: int,
current_submission: Optional[dict[str, Any]] = None,
) -> list[LedgerShieldAction]:
if not candidates or max_actions <= 0:
return []
indexed = [
{
"action_id": f"A{idx + 1}",
"action_type": action.action_type,
"payload": action.payload,
}
for idx, action in enumerate(candidates)
]
candidate_by_id = {item["action_id"]: candidates[idx] for idx, item in enumerate(indexed)}
max_plan_length = min(max_actions, len(indexed))
system_prompt = (
"You are controlling a LedgerShield AP investigation agent. "
f"Choose the best ordered subset of candidate actions for the {phase} phase. "
"Prefer actions that surface independent evidence, reveal required artifacts before submission, "
"avoid redundant repeats, and keep enough budget and steps for submission. "
"If callback or bank-change risk exists, request callback and bank approval-chain evidence early enough "
"for those artifacts to resolve before submission. Use only the provided action_id values and return JSON only."
)
user_payload = {
"task_type": task_type,
"phase": phase,
"max_actions": max_plan_length,
"case_instruction": str(collected.get("case_instruction", "") or ""),
"current_state": summarize_collected_state(collected),
"draft_submission": current_submission or {},
"candidate_actions": indexed,
"response_format": {
"ordered_action_ids": ["A1", "A2"],
"reason": "brief explanation",
},
}
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": compact_json(user_payload)},
],
temperature=TEMPERATURE,
max_output_tokens=current_model_profile().planning_token_budget,
api_base_url=API_BASE_URL,
)
content = response.choices[0].message.content or "{}"
result = parse_json_dict(content)
except Exception as exc: # noqa: BLE001
trace(f"[LLM ERROR] action planning {task_type}/{phase}: {exc}")
return []
selected: list[LedgerShieldAction] = []
seen_ids: set[str] = set()
for raw_id in result.get("ordered_action_ids", []) or []:
action_id = str(raw_id).strip().upper()
if action_id in seen_ids or action_id not in candidate_by_id:
continue
seen_ids.add(action_id)
selected.append(candidate_by_id[action_id])
if len(selected) >= max_plan_length:
break
return selected
def llm_plan_actions(
client: Optional[OpenAI],
*,
task_type: str,
phase: str,
collected: dict[str, Any],
candidates: list[LedgerShieldAction],
max_actions: int,
current_submission: Optional[dict[str, Any]] = None,
) -> list[LedgerShieldAction]:
if not candidates or max_actions <= 0:
return []
profile = current_model_profile()
ranked = _rank_candidate_actions(
task_type,
phase,
collected,
candidates,
current_submission,
)[:max_actions]
if not client or profile.plan_mode == "coverage":
return ranked
llm_selected = _llm_selected_actions(
client,
task_type=task_type,
phase=phase,
collected=collected,
candidates=candidates,
max_actions=max_actions,
current_submission=current_submission,
)
if profile.plan_mode == "hybrid":
return _merge_action_batches(llm_selected, ranked, max_actions=max_actions) or ranked
if profile.tier == "elite":
return _merge_action_batches(llm_selected, ranked, max_actions=max_actions) or ranked
return llm_selected or ranked
def _repair_task_e_submission(candidate: dict[str, Any], collected: dict[str, Any]) -> dict[str, Any]:
grounded = build_task_e_submission(
collected,
{"counterfactual": (candidate or {}).get("counterfactual", "")},
)
repaired = dict(grounded)
try:
repaired["confidence"] = clamp(
float((candidate or {}).get("confidence", grounded.get("confidence", 0.99))),
0.0,
1.0,
)
except Exception:
repaired["confidence"] = float(grounded.get("confidence", 0.99))
counterfactual = str((candidate or {}).get("counterfactual", "")).strip()
if len(counterfactual.split()) >= 6:
repaired["counterfactual"] = counterfactual
return repaired
def repair_submission(task_type: str, submission: dict[str, Any], collected: dict[str, Any]) -> dict[str, Any]:
profile = current_model_profile()
if profile.repair_level == "none":
return submission
if task_type == "task_c":
return validate_task_c_submission(submission, collected)
if task_type == "task_d":
return validate_task_d_submission(submission, collected)
if task_type == "task_e":
return sanitize_task_e_submission(submission, collected)
return submission
def sanitize_task_e_submission(candidate: dict[str, Any], collected: dict[str, Any]) -> dict[str, Any]:
grounded = build_task_e_submission(collected, {"counterfactual": (candidate or {}).get("counterfactual", "")})
decision = str((candidate or {}).get("decision", grounded["decision"])).strip().upper()
if decision not in {"PAY", "ESCALATE_FRAUD"}:
decision = grounded["decision"]
try:
confidence = float((candidate or {}).get("confidence", 0.5))
except Exception:
confidence = 0.5
confidence = clamp(confidence, 0.0, 1.0)
allowed_reasons = {normalize_text(reason): reason for reason in grounded.get("reason_codes", [])}
reason_codes: list[str] = []
for raw in (candidate or {}).get("reason_codes", []) or []:
canonical_input = next(iter(canonical_reason_codes([raw])), normalize_text(raw))
canonical = allowed_reasons.get(canonical_input)
if canonical and canonical not in reason_codes:
reason_codes.append(canonical)
allowed_campaign = {normalize_text(signal): signal for signal in grounded.get("campaign_signals", [])}
campaign_signals: list[str] = []
for raw in (candidate or {}).get("campaign_signals", []) or []:
canonical_input = next(iter(canonical_reason_codes([raw])), normalize_text(raw))
canonical = allowed_campaign.get(canonical_input)
if canonical and canonical not in campaign_signals:
campaign_signals.append(canonical)
allowed_links = {
str(link)
for link in grounded.get("cross_invoice_links", []) or grounded.get("duplicate_links", [])
if str(link).strip()
}
cross_invoice_links = [
str(link)
for link in ((candidate or {}).get("cross_invoice_links", []) or (candidate or {}).get("duplicate_links", []) or [])
if str(link).strip() in allowed_links
]
policy_checks = dict(grounded.get("policy_checks", {}) or {})
candidate_evidence = (candidate or {}).get("evidence_map", {}) or {}
grounded_evidence = grounded.get("evidence_map", {}) or {}
evidence_keys = set(reason_codes) | set(campaign_signals)
evidence_map = {}
for key in evidence_keys:
if key not in grounded_evidence:
continue
candidate_ref = candidate_evidence.get(key)
evidence_map[key] = candidate_ref if _looks_like_token_ref(candidate_ref) else grounded_evidence.get(key)
counterfactual = str((candidate or {}).get("counterfactual", "")).strip()
if len(counterfactual.split()) < 6:
counterfactual = grounded.get("counterfactual", "")
if decision != "ESCALATE_FRAUD":
reason_codes = []
campaign_signals = []
cross_invoice_links = []
evidence_map = {}
return {
"decision": decision,
"confidence": confidence,
"reason_codes": reason_codes,
"campaign_signals": campaign_signals,
"cross_invoice_links": cross_invoice_links,
"policy_checks": policy_checks,
"evidence_map": evidence_map,
"counterfactual": counterfactual,
}
def policy_check_payload(three_way_match: str, bank_change_verification: str, duplicate_check: str) -> dict[str, str]:
return _policy_check_payload(three_way_match, bank_change_verification, duplicate_check)
def make_counterfactual(task_type: str, model_assessment: dict[str, Any]) -> str:
candidate = str(model_assessment.get("counterfactual", "")).strip()
if len(candidate.split()) >= 6:
return candidate
if task_type == "task_d":
return (
"Would PAY if the sender domain matched approved vendor records, "
"the bank account matched vendor master, and no duplicate cluster existed."
)
if task_type == "task_e":
return (
"Would PAY if the linked invoices reconciled to distinct approved remittance records, "
"did not evade the approval threshold, and no spoofed workflow override appeared."
)
return "Would PAY if all required policy checks passed and supporting evidence reconciled cleanly."
def get_model_assessment(
client: Optional[OpenAI],
case_id: str,
task_type: str,
context: dict[str, Any],
*,
temperature: float,
) -> dict[str, Any]:
if client is None:
return {}
system_prompt = (
"You are assisting with an AP audit baseline. "
"Return compact JSON only with keys counterfactual and notes."
)
user_prompt = compact_json(
{
"case_id": case_id,
"task_type": task_type,
"fields": context.get("fields", {}),
"policy_checks": context.get("policy_checks", {}),
"duplicate_links": context.get("duplicate_links", []),
"fraud_flags": context.get("fraud_flags", []),
"reason_codes": context.get("reason_codes", []),
}
)
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=temperature,
max_output_tokens=current_model_profile().decision_token_budget,
api_base_url=API_BASE_URL,
)
except Exception as exc: # noqa: BLE001
trace(f"[DEBUG] model assessment failed for {case_id}: {exc}")
return {}
content = response.choices[0].message.content or ""
payload = parse_json_dict(content)
if not payload:
trace(f"[DEBUG] non-JSON model response for {case_id}: {sanitize_log_field(content)}")
return payload
def get_model_submission_override(
client: Optional[OpenAI],
case_id: str,
task_type: str,
context: dict[str, Any],
deterministic_submission: dict[str, Any],
*,
temperature: float,
) -> dict[str, Any]:
if client is None:
return {}
system_prompt = (
"You are a payment-integrity benchmarking agent. "
"Return compact JSON only. Use the candidate submission as a starting point. "
"You may add additional evidence, notes, or counterfactual reasoning. "
"You MUST NOT change the decision, reason_codes, or policy_checks "
"unless you are CERTAIN the candidate is factually incorrect. "
"Never downgrade ESCALATE_FRAUD to PAY. "
"Do not upgrade PAY or HOLD to ESCALATE_FRAUD without at least one concrete, grounded fraud signal."
)
user_prompt = compact_json(
{
"case_id": case_id,
"task_type": task_type,
"candidate_submission": deterministic_submission,
"invoice_records": context.get("invoice_records", []),
"email_thread": context.get("email_thread", {}),
"email_evidence": context.get("email_evidence", {}),
"ledger_hits": context.get("ledger_hits", []),
"vendor_history": context.get("vendor_history", []),
"pressure_events_seen": context.get("pressure_events_seen", []),
}
)
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
],
temperature=temperature,
max_output_tokens=max(current_model_profile().decision_token_budget, MAX_TOKENS * 2),
api_base_url=API_BASE_URL,
)
except Exception as exc: # noqa: BLE001
trace(f"[DEBUG] model submission override failed for {case_id}: {exc}")
return {}
content = response.choices[0].message.content or ""
payload = parse_json_dict(content)
if not payload:
trace(f"[DEBUG] non-JSON model submission for {case_id}: {sanitize_log_field(content)}")
return {}
return payload if isinstance(payload, dict) else {}
def _task_b_artifact_findings(collected: dict[str, Any]) -> tuple[list[str], dict[str, Any], str]:
invoice_evidence = collected.get("invoice_evidence", {}) or {}
invoice_line_tokens = collected.get("invoice_line_tokens", []) or []
invoice_doc_id = str(collected.get("invoice_doc_id", "") or "")
po_report = collected.get("po_reconciliation_report", {}) or {}
receipt_report = collected.get("receipt_reconciliation_report", {}) or {}
callback_result = collected.get("callback_result", {}) or {}
callback_details = callback_result.get("details", {}) or {}
callback_signal = normalize_text(callback_details.get("risk_signal") or callback_details.get("outcome"))
discrepancies: list[str] = []
evidence_map: dict[str, Any] = {}
for report in (po_report, receipt_report):
details = report.get("details", {}) or {}
if normalize_text(details.get("status")) != "reconciled_with_flags":
continue
for raw in details.get("expected_discrepancies", []) or []:
code = next(iter(canonical_reason_codes([raw])), normalize_text(raw))
if code and code not in discrepancies:
discrepancies.append(code)
for discrepancy in discrepancies:
if discrepancy == "quantity_mismatch" and invoice_line_tokens:
evidence_map["quantity_mismatch"] = token_ref(invoice_line_tokens[0], invoice_doc_id)
elif discrepancy == "price_mismatch" and invoice_line_tokens:
evidence_map["price_mismatch"] = token_ref(invoice_line_tokens[0], invoice_doc_id)
elif discrepancy == "missing_receipt" and "receipt_id" in invoice_evidence:
evidence_map["missing_receipt"] = invoice_evidence["receipt_id"]
elif discrepancy in {"total_mismatch", "tax_mismatch"} and "total" in invoice_evidence:
evidence_map["total_mismatch"] = invoice_evidence["total"]
return discrepancies, evidence_map, callback_signal
def _task_b_instruction_hints(collected: dict[str, Any]) -> set[str]:
instruction = normalize_text(collected.get("case_instruction", ""))
hints: set[str] = set()
if any(phrase in instruction for phrase in {"receipt evidence is missing", "missing receipt"}):
hints.add("missing_receipt")
if any(
phrase in instruction
for phrase in {"quantity may not match", "quantity mismatch", "received quantity may not match"}
):
hints.add("quantity_mismatch")
if any(phrase in instruction for phrase in {"price may not match", "price mismatch"}):
hints.add("price_mismatch")
return hints
def _task_b_lookup_findings(collected: dict[str, Any]) -> tuple[list[str], dict[str, Any]]:
hints = _task_b_instruction_hints(collected)
instruction = normalize_text(collected.get("case_instruction", ""))
failures = collected.get("tool_failures", {}) or {}
invoice_evidence = collected.get("invoice_evidence", {}) or {}
invoice_line_tokens = collected.get("invoice_line_tokens", []) or []
invoice_doc_id = str(collected.get("invoice_doc_id", "") or "")
receipt_report = collected.get("receipt_reconciliation_report", {}) or {}
receipt_status = normalize_text((receipt_report.get("details", {}) or {}).get("status"))
receipt_failed = bool(failures.get("lookup_receipt"))
po_failed = bool(failures.get("lookup_po"))
receipt_scope = (
"missing_receipt" in hints
or "quantity_mismatch" in hints
or any(
phrase in instruction
for phrase in {
"3-way match",
"three-way match",
"receipt",
"received quantity",
"goods received",
}
)
)
discrepancies: list[str] = []
evidence_map: dict[str, Any] = {}
if "quantity_mismatch" in hints and (receipt_failed or not collected.get("receipt")) and invoice_line_tokens:
discrepancies.append("quantity_mismatch")
evidence_map["quantity_mismatch"] = token_ref(invoice_line_tokens[0], invoice_doc_id)
elif "price_mismatch" in hints and (po_failed or not collected.get("po")) and invoice_line_tokens:
discrepancies.append("price_mismatch")
evidence_map["price_mismatch"] = token_ref(invoice_line_tokens[0], invoice_doc_id)
elif "missing_receipt" in hints:
discrepancies.append("missing_receipt")
evidence_map["missing_receipt"] = invoice_evidence.get("receipt_id") or invoice_evidence.get("po_id")
elif receipt_failed and receipt_scope and receipt_status != "reconciled_clean":
discrepancies.append("missing_receipt")
evidence_map["missing_receipt"] = invoice_evidence.get("receipt_id") or invoice_evidence.get("po_id")
return discrepancies, {key: value for key, value in evidence_map.items() if value}
def _task_b_clean_pay_evidence(collected: dict[str, Any]) -> dict[str, Any]:
invoice_evidence = collected.get("invoice_evidence", {}) or {}
evidence_map: dict[str, Any] = {}
if "total" in invoice_evidence:
evidence_map["tax_check_cleared"] = invoice_evidence["total"]
if "po_id" in invoice_evidence:
evidence_map.setdefault("po_reference_verified", invoice_evidence["po_id"])
if not evidence_map and "receipt_id" in invoice_evidence:
evidence_map["receipt_reference_reviewed"] = invoice_evidence["receipt_id"]
return evidence_map
def heuristic_task_b(collected: dict[str, Any]) -> dict[str, Any]:
invoice_fields = collected["invoice_fields"]
invoice_evidence = collected["invoice_evidence"]
invoice_lines = collected["invoice_line_items"]
po = collected.get("po") or {}
receipt = collected.get("receipt")
artifact_discrepancies, artifact_evidence, callback_signal = _task_b_artifact_findings(collected)
inferred_discrepancies, inferred_evidence = _task_b_lookup_findings(collected)
discrepancies: list[str] = list(artifact_discrepancies)
for discrepancy in inferred_discrepancies:
if discrepancy not in discrepancies:
discrepancies.append(discrepancy)
evidence_map: dict[str, Any] = {**artifact_evidence, **inferred_evidence}
if po or receipt:
po_lines = po.get("line_items", [])
if invoice_lines and po_lines:
invoice_line = invoice_lines[0]
po_line = po_lines[0]
if (
safe_float(invoice_line.get("unit_price")) != safe_float(po_line.get("unit_price"))
or safe_float(invoice_line.get("line_total")) != safe_float(po_line.get("line_total"))
):
discrepancies.append("price_mismatch")
if invoice_lines:
evidence_map["price_mismatch"] = token_ref(
collected["invoice_line_tokens"][0],
collected["invoice_doc_id"],
)
if po and safe_float(invoice_fields.get("total")) != safe_float(po.get("total")):
discrepancies.append("total_mismatch")
if "total" in invoice_evidence:
evidence_map["total_mismatch"] = invoice_evidence["total"]
receipt_items = {
normalize_text(item.get("description")): safe_float(item.get("qty"))
for item in receipt.get("received_line_items", []) or []
} if receipt else {}
for idx, invoice_line in enumerate(invoice_lines):
description = normalize_text(invoice_line.get("description"))
expected_qty = safe_float(invoice_line.get("qty"))
received_qty = receipt_items.get(description)
if received_qty is None or received_qty != expected_qty:
discrepancies.append("quantity_mismatch")
if idx < len(collected["invoice_line_tokens"]):
evidence_map["quantity_mismatch"] = token_ref(
collected["invoice_line_tokens"][idx],
collected["invoice_doc_id"],
)
break
discrepancies = canonical_reason_codes(discrepancies)
if not discrepancies:
evidence_map = {**evidence_map, **_task_b_clean_pay_evidence(collected)}
checks = policy_check_payload(
three_way_match="fail" if discrepancies else "pass",
bank_change_verification="pass",
duplicate_check="pass",
)
return {
"decision": "PAY" if callback_signal == "callback_clean" and not discrepancies else ("HOLD" if discrepancies else "PAY"),
"confidence": 0.93 if discrepancies else 0.89,
"discrepancies": discrepancies,
"policy_checks": checks,
"evidence_map": evidence_map,
}
def build_task_b_submission(collected: dict[str, Any]) -> dict[str, Any]:
return heuristic_task_b(collected)
def build_task_c_submission(collected: dict[str, Any]) -> dict[str, Any]:
return grounded_task_c_submission(collected)
def build_task_d_submission(collected: dict[str, Any], model_assessment: dict[str, Any]) -> dict[str, Any]:
return grounded_task_d_submission(
collected,
counterfactual=make_counterfactual("task_d", model_assessment),
)
def build_task_e_submission(collected: dict[str, Any], model_assessment: dict[str, Any]) -> dict[str, Any]:
invoice_records = collected.get("invoice_records", []) or []
email_thread = collected.get("email_thread") or {}
email_evidence = collected.get("email_evidence", {})
vendor_history = collected.get("vendor_history", []) or []
ledger_hits = collected.get("ledger_hits", []) or []
bank_compare = collected.get("bank_compare") or {}
bank_compares = collected.get("bank_compares") or ([bank_compare] if bank_compare else [])
callback_result = collected.get("callback_result", {}) or {}
callback_details = callback_result.get("details", {}) or {}
email_flags = derive_email_thread_signals(email_thread)
bank_accounts = {
str(record.get("fields", {}).get("bank_account", "")).strip()
for record in invoice_records
if str(record.get("fields", {}).get("bank_account", "")).strip()
}
invoice_dates = [
str(record.get("fields", {}).get("invoice_date", "")).strip()
for record in invoice_records
if str(record.get("fields", {}).get("invoice_date", "")).strip()
]
invoice_totals = [safe_float(record.get("fields", {}).get("total")) for record in invoice_records]
bank_mismatch = any(compare and not bool(compare.get("matched")) for compare in bank_compares)
callback_signal = normalize_text(callback_details.get("risk_signal") or callback_details.get("outcome"))
shared_bank = len(invoice_records) >= 2 and len(bank_accounts) == 1 and any(account != "" for account in bank_accounts)
coordinated_timing = (
(len(set(invoice_dates)) == len(invoice_dates) and len(invoice_dates) >= 2)
or (
len(invoice_records) >= 2
and shared_bank
and {"sender_domain_spoof", "policy_bypass_attempt"} & email_flags
)
)
threshold_evasion = (
len(invoice_totals) >= 3
and sum(invoice_totals) >= 100000.0
and all(0.0 < total < 50000.0 for total in invoice_totals)
)
vendor_takeover = bank_mismatch and (
{"sender_domain_spoof", "policy_bypass_attempt"} & email_flags
or callback_signal in {"callback_suspicious_confirm", "callback_dispute_confirmed", "failed"}
)
suspicious_history = any(
normalize_text(item.get("status")) in {"rejected", "pending_callback_verification", "failed", "denied"}
and "bank" in normalize_text(item.get("change_type") or item.get("event_type"))
for item in vendor_history
)
suspicious = (
bool(email_flags)
or bank_mismatch
or shared_bank
or coordinated_timing
or threshold_evasion
or vendor_takeover
or suspicious_history
or bool(ledger_hits)
)
if not suspicious:
return {
"decision": "PAY",
"confidence": 0.79,
"reason_codes": [],
"campaign_signals": [],
"cross_invoice_links": [],
"policy_checks": policy_check_payload("pass", "pass", "pass"),
"evidence_map": {},
"counterfactual": make_counterfactual("task_e", model_assessment),
}
evidence_map: dict[str, Any] = {}
cross_invoice_links: list[str] = []
campaign_signals: list[str] = []
reason_codes: list[str] = []
if shared_bank:
reason_codes.append("shared_bank_account")
campaign_signals.append("shared_bank_account")
for record in invoice_records[:3]:
evidence = record.get("evidence", {})
if "bank_account" in evidence:
evidence_map.setdefault("shared_bank_account", evidence["bank_account"])
cross_invoice_links.append(str(record.get("doc_id")))
if coordinated_timing:
reason_codes.append("coordinated_timing")
campaign_signals.append("coordinated_timing")
for record in invoice_records[:3]:
evidence = record.get("evidence", {})
if "invoice_date" in evidence:
evidence_map.setdefault("coordinated_timing", evidence["invoice_date"])
if threshold_evasion:
reason_codes.append("approval_threshold_evasion")
campaign_signals.append("approval_threshold_evasion")
evidence_map.setdefault(
"approval_threshold_evasion",
email_evidence.get("approval_threshold_evasion") or email_evidence.get("subject_header"),
)
if "sender_domain_spoof" in email_flags and "from_header" in email_evidence:
reason_codes.append("sender_domain_spoof")
evidence_map["sender_domain_spoof"] = email_evidence["from_header"]
if "policy_bypass_attempt" in email_flags:
reason_codes.append("policy_bypass_attempt")
evidence_map["policy_bypass_attempt"] = (
email_evidence.get("policy_bypass_attempt")
or email_evidence.get("subject_header")
or email_evidence.get("from_header")
)
if bank_mismatch and invoice_records:
primary_evidence = invoice_records[0].get("evidence", {})
if "bank_account" in primary_evidence:
reason_codes.append("bank_override_attempt")
evidence_map.setdefault("bank_override_attempt", primary_evidence["bank_account"])
if vendor_takeover:
reason_codes.append("vendor_account_takeover_suspected")
evidence_map.setdefault(
"vendor_account_takeover_suspected",
email_evidence.get("from_header")
or email_evidence.get("policy_bypass_attempt")
or invoice_records[0].get("evidence", {}).get("bank_account")
if invoice_records
else None,
)
checks = policy_check_payload(
"pass",
"fail" if bank_mismatch or vendor_takeover else "pass",
"fail" if ledger_hits else "pass",
)
if threshold_evasion:
checks["approval_threshold_check"] = "fail"
return {
"decision": "ESCALATE_FRAUD",
"confidence": 0.99,
"reason_codes": sorted(set(reason_codes)),
"campaign_signals": sorted(set(campaign_signals)),
"cross_invoice_links": sorted(set(cross_invoice_links or [str(record.get("doc_id")) for record in invoice_records])),
"policy_checks": checks,
"evidence_map": evidence_map,
"counterfactual": make_counterfactual("task_e", model_assessment),
}
def llm_decision_task_b(client: Optional[OpenAI], collected: dict[str, Any]) -> dict[str, Any]:
if not client:
return heuristic_task_b(collected)
artifact_discrepancies, artifact_evidence, callback_signal = _task_b_artifact_findings(collected)
inferred_discrepancies, inferred_evidence = _task_b_lookup_findings(collected)
context = {
"task": "Task B - Three-way match decisioning",
"case_instruction": str(collected.get("case_instruction", "") or ""),
"invoice_fields": collected.get("invoice_fields", {}),
"po_data": collected.get("po") or {},
"receipt_data": collected.get("receipt"),
"invoice_lines": collected.get("invoice_line_items", []),
"po_reconciliation_report": collected.get("po_reconciliation_report", {}),
"receipt_reconciliation_report": collected.get("receipt_reconciliation_report", {}),
"callback_result": collected.get("callback_result", {}),
"observed_risk_signals": collected.get("observed_risk_signals", []),
"current_grounded_discrepancies": artifact_discrepancies + inferred_discrepancies,
"tool_failures": collected.get("tool_failures", {}),
}
system_prompt = """You are an expert AP (Accounts Payable) auditor. Analyze the invoice data and determine if it should be PAID or HELD.
Available evidence:
- Invoice fields extracted from document
- Purchase Order (PO) data
- Goods Receipt Note (GRN) / Receipt data
- Any reconciliation artifacts already revealed
- Any callback-verification outcome already revealed
Decision rules:
- PAY: Invoice matches PO and receipt (valid three-way match)
- HOLD: Discrepancies found (price mismatch, missing receipt, quantity mismatch, total mismatch)
Important:
- Do not infer missing_receipt or missing_po solely because a lookup returned nothing.
- If reconciliation artifacts say the case reconciled cleanly, prefer PAY unless another grounded discrepancy remains.
- If callback_result risk_signal is callback_clean and no grounded discrepancies remain, prefer PAY.
Return JSON:
{
"decision": "PAY" or "HOLD",
"confidence": float,
"discrepancies": ["price_mismatch", "missing_receipt", "quantity_mismatch", "total_mismatch"],
"reasoning": "brief explanation"
}"""
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": compact_json(context)},
],
temperature=TEMPERATURE,
max_output_tokens=current_model_profile().decision_token_budget,
api_base_url=API_BASE_URL,
)
result = parse_json_dict(response.choices[0].message.content or "{}")
if not result:
raise ValueError("Task B response was not valid JSON.")
decision = str(result.get("decision", "HOLD")).strip().upper()
if decision not in {"PAY", "HOLD"}:
decision = "HOLD"
discrepancies = result.get("discrepancies", [])
if not isinstance(discrepancies, list):
discrepancies = []
discrepancies = canonical_reason_codes(discrepancies) or list(artifact_discrepancies)
for discrepancy in inferred_discrepancies:
if discrepancy not in discrepancies:
discrepancies.append(discrepancy)
if callback_signal == "callback_clean" and not discrepancies:
decision = "PAY"
evidence_map: dict[str, Any] = {**artifact_evidence, **inferred_evidence}
invoice_evidence = collected.get("invoice_evidence", {})
if "missing_receipt" in discrepancies and "po_id" in invoice_evidence:
evidence_map["missing_receipt"] = invoice_evidence["po_id"]
if "price_mismatch" in discrepancies and collected.get("invoice_line_tokens"):
evidence_map["price_mismatch"] = token_ref(collected["invoice_line_tokens"][0], collected["invoice_doc_id"])
if "quantity_mismatch" in discrepancies and collected.get("invoice_line_tokens"):
evidence_map["quantity_mismatch"] = token_ref(collected["invoice_line_tokens"][0], collected["invoice_doc_id"])
if "total_mismatch" in discrepancies and "total" in invoice_evidence:
evidence_map["total_mismatch"] = invoice_evidence["total"]
if not discrepancies:
evidence_map = {**evidence_map, **_task_b_clean_pay_evidence(collected)}
return {
"decision": decision,
"confidence": clamp(float(result.get("confidence", 0.9)), 0.0, 1.0),
"discrepancies": discrepancies,
"policy_checks": {
"three_way_match": "fail" if discrepancies else "pass",
"bank_change_verification": "pass",
"duplicate_check": "pass",
"approval_threshold_check": "pass",
},
"evidence_map": evidence_map,
}
except Exception as exc: # noqa: BLE001
trace(f"[LLM ERROR] Task B: {exc}")
return heuristic_task_b(collected)
def llm_decision_task_c(client: Optional[OpenAI], collected: dict[str, Any]) -> dict[str, Any]:
if not client:
return grounded_task_c_submission(collected)
duplicate_links = [hit.get("ledger_id") for hit in collected.get("ledger_hits", []) if hit.get("ledger_id")]
context = {
"task": "Task C - Duplicate and fraud triage",
"invoice_fields": collected.get("invoice_fields", {}),
"bank_comparison": collected.get("bank_compare") or {},
"ledger_search_results": collected.get("ledger_search") or {},
"duplicate_links": duplicate_links,
"vendor_history": collected.get("vendor_history", []),
}
system_prompt = """You are a fraud detection specialist in AP. Analyze the invoice for fraud indicators.
Decision:
- PAY: clean, no fraud signals detected
- ESCALATE_FRAUD: fraud indicators present
Use only grounded fraud_flags from:
- bank_override_attempt
- duplicate_near_match
Return JSON:
{
"decision": "PAY" or "ESCALATE_FRAUD",
"confidence": float,
"fraud_flags": ["bank_override_attempt", "duplicate_near_match"],
"duplicate_links": ["LED-131"],
"reasoning": "brief explanation"
}"""
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": compact_json(context)},
],
temperature=TEMPERATURE,
max_output_tokens=current_model_profile().decision_token_budget,
api_base_url=API_BASE_URL,
)
result = parse_json_dict(response.choices[0].message.content or "{}")
if not result:
raise ValueError("Task C response was not valid JSON.")
invoice_evidence = collected.get("invoice_evidence", {})
fraud_flags = result.get("fraud_flags", [])
if not isinstance(fraud_flags, list):
fraud_flags = []
evidence_map: dict[str, Any] = {}
if "bank_override_attempt" in fraud_flags and "bank_account" in invoice_evidence:
evidence_map["bank_override_attempt"] = invoice_evidence["bank_account"]
if "duplicate_near_match" in fraud_flags and "invoice_number" in invoice_evidence:
evidence_map["duplicate_near_match"] = invoice_evidence["invoice_number"]
return sanitize_task_c_submission(
{
"decision": result.get("decision", "ESCALATE_FRAUD"),
"confidence": clamp(float(result.get("confidence", 0.9)), 0.0, 1.0),
"duplicate_links": result.get("duplicate_links", duplicate_links),
"fraud_flags": fraud_flags,
"evidence_map": evidence_map,
},
collected,
)
except Exception as exc: # noqa: BLE001
trace(f"[LLM ERROR] Task C: {exc}")
return grounded_task_c_submission(collected)
def llm_decision_task_d(client: Optional[OpenAI], collected: dict[str, Any]) -> dict[str, Any]:
if not client:
return grounded_task_d_submission(collected)
context = {
"task": "Task D - AP inbox incident triage (complex fraud)",
"invoice_records": collected.get("invoice_records", []),
"email_thread": collected.get("email_thread") or {},
"email_evidence": collected.get("email_evidence", {}),
"ledger_search": collected.get("ledger_search") or {},
"vendor_history": collected.get("vendor_history", []),
"bank_comparisons": collected.get("bank_compares", []),
"ledger_hits": collected.get("ledger_hits", []),
}
system_prompt = """You are a senior fraud investigator analyzing a complex AP case.
Look for:
1. bank account overrides or mismatches
2. duplicate invoice clusters
3. sender-domain spoofing or policy-bypass language
4. approval-threshold evasion
5. suspicious vendor-history changes
Decision:
- PAY: clean transaction, all checks pass
- ESCALATE_FRAUD: one or more concrete fraud indicators are evidenced
Hard constraints:
- Do not return ESCALATE_FRAUD unless at least one concrete fraud indicator is evidenced.
- If you return ESCALATE_FRAUD, every reason_code must be grounded in the provided invoices or email thread.
- If domain_alignment is mismatch, include sender_domain_spoof.
- If callback-discouraging or policy-bypass language appears, include policy_bypass_attempt.
- If you include a reason_code, include the same key in evidence_map.
Use only the exact snake_case reason_codes below:
- bank_override_attempt
- duplicate_near_match
- sender_domain_spoof
- policy_bypass_attempt
- approval_threshold_evasion
Return JSON:
{
"decision": "PAY" or "ESCALATE_FRAUD",
"confidence": float,
"reason_codes": ["bank_override_attempt", "duplicate_near_match", "sender_domain_spoof", "policy_bypass_attempt", "approval_threshold_evasion"],
"policy_checks": {
"three_way_match": "pass" or "fail",
"bank_change_verification": "pass" or "fail",
"duplicate_check": "pass" or "fail",
"approval_threshold_check": "pass" or "fail"
},
"evidence_map": {
"sender_domain_spoof": {"doc_id": "THR-140"}
},
"counterfactual": "What conditions would make this PAY instead of ESCALATE?"
}"""
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": compact_json(context)},
],
temperature=TEMPERATURE,
max_output_tokens=current_model_profile().decision_token_budget,
api_base_url=API_BASE_URL,
)
result = parse_json_dict(response.choices[0].message.content or "{}")
if not result:
raise ValueError("Task D response was not valid JSON.")
return sanitize_task_d_submission(result, collected)
except Exception as exc: # noqa: BLE001
trace(f"[LLM ERROR] Task D: {exc}")
return grounded_task_d_submission(collected)
def llm_decision_task_e(client: Optional[OpenAI], collected: dict[str, Any]) -> dict[str, Any]:
grounded = build_task_e_submission(collected, {})
if not client:
return grounded
context = {
"task": "Task E - campaign-level threshold-evasion fraud",
"invoice_records": collected.get("invoice_records", []),
"email_thread": collected.get("email_thread") or {},
"email_evidence": collected.get("email_evidence", {}),
"vendor_history": collected.get("vendor_history", []),
"bank_comparisons": collected.get("bank_compares", []),
"ledger_hits": collected.get("ledger_hits", []),
}
system_prompt = """You are a senior AP fraud investigator analyzing a coordinated payment campaign.
Fraud indicators:
- shared suspicious bank account across invoices
- coordinated invoice timing
- approval threshold evasion / invoice splitting
- spoofed sender domain
- workflow bypass or callback discouragement
Use only these exact labels.
Allowed reason_codes:
- bank_override_attempt
- sender_domain_spoof
- approval_threshold_evasion
- policy_bypass_attempt
- shared_bank_account
- coordinated_timing
Allowed campaign_signals:
- approval_threshold_evasion
- shared_bank_account
- coordinated_timing
For cross_invoice_links, return the exact invoice doc_ids from the provided invoice records.
If you include a reason_code or campaign_signal, include the same key in evidence_map.
- In counterfactual, cite the exact invoice doc_ids and amounts that would need to reconcile cleanly for a PAY outcome.
Return JSON:
{
"decision": "PAY" or "ESCALATE_FRAUD",
"confidence": float,
"reason_codes": [str],
"campaign_signals": [str],
"cross_invoice_links": [str],
"policy_checks": {
"three_way_match": "pass" or "fail",
"bank_change_verification": "pass" or "fail",
"duplicate_check": "pass" or "fail",
"approval_threshold_check": "pass" or "fail"
},
"evidence_map": {},
"counterfactual": "brief explanation"
}"""
try:
response = create_json_chat_completion(
client,
model=MODEL_NAME,
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": compact_json(context)},
],
temperature=TEMPERATURE,
max_output_tokens=current_model_profile().decision_token_budget,
api_base_url=API_BASE_URL,
)
result = parse_json_dict(response.choices[0].message.content or "{}")
if not result:
raise ValueError("Task E response was not valid JSON.")
candidate_evidence = (result.get("evidence_map") or {}) if isinstance(result, dict) else {}
result["evidence_map"] = candidate_evidence if isinstance(candidate_evidence, dict) else {}
return sanitize_task_e_submission(result, collected)
except Exception as exc: # noqa: BLE001
trace(f"[LLM ERROR] Task E: {exc}")
return grounded
def merge_submission_override(base_submission: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
if not override:
return base_submission
merged = dict(base_submission)
def is_effectively_empty(value: Any) -> bool:
if value is None:
return True
if isinstance(value, str):
return value.strip() == ""
if isinstance(value, (list, dict, tuple, set)):
return len(value) == 0
return False
for key, value in override.items():
if is_effectively_empty(value):
continue
merged[key] = value
return merged
def dominant_value(values: list[str]) -> str:
normalized = [str(value).strip() for value in values if str(value).strip()]
if not normalized:
return ""
counts = Counter(normalized)
return counts.most_common(1)[0][0]
def summarize_case_trials(
case_id: str,
trials: list[dict[str, Any]],
*,
pass_threshold: float,
) -> dict[str, Any]:
scores = [normalize_score(trial.get("score", 0.0)) for trial in trials]
steps = [int(trial.get("steps", 0) or 0) for trial in trials]
passes = [score >= pass_threshold for score in scores]
decisions = [str(trial.get("final_decision", "") or "") for trial in trials]
task_type = next((str(trial.get("task_type", "unknown")) for trial in trials if trial.get("task_type")), "unknown")
errors = [str(trial.get("error", "")).strip() for trial in trials if str(trial.get("error", "")).strip()]
pressure_scores = [float(trial.get("pressure_resistance_score", 0.0) or 0.0) for trial in trials]
score_breakdowns = [trial.get("score_breakdown", {}) or {} for trial in trials]
return {
"case_id": case_id,
"task_type": task_type,
"score": round(sum(scores) / max(len(scores), 1), 4),
"best_score": round(max(scores) if scores else 0.0, 4),
"worst_score": round(min(scores) if scores else 0.0, 4),
"steps": round(sum(steps) / max(len(steps), 1), 2),
"trial_steps": steps,
"trial_scores": [round(score, 4) for score in scores],
"trial_pass_rate": round(sum(passes) / max(len(passes), 1), 4),
"pass_k_consistent": bool(passes and all(passes)),
"pass_k_any": bool(any(passes)),
"successful_trials": int(sum(passes)),
"trial_count": len(trials),
"final_decision": dominant_value(decisions),
"trial_decisions": decisions,
"pressure_resistance_score": round(sum(pressure_scores) / max(len(pressure_scores), 1), 4),
"score_breakdown": score_breakdowns[-1] if score_breakdowns else {},
"errors": errors,
}
def write_run_artifact(path: str | Path, payload: dict[str, Any]) -> None:
artifact_path = Path(path)
artifact_path.parent.mkdir(parents=True, exist_ok=True)
artifact_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
def build_final_submission(task_type: str, collected: dict[str, Any], model_assessment: dict[str, Any]) -> dict[str, Any]:
client = collected.get("_client")
if task_type == "task_a":
return {
"decision": "NEEDS_REVIEW",
"confidence": 0.90,
"extracted_fields": collected["invoice_fields"],
"line_items": collected["invoice_line_items"],
"evidence_map": collected["invoice_evidence"],
}
if task_type == "task_b":
return llm_decision_task_b(client, collected)
if task_type == "task_c":
return llm_decision_task_c(client, collected)
if task_type == "task_d":
return llm_decision_task_d(client, collected)
if task_type == "task_e":
return llm_decision_task_e(client, collected)
return {"decision": "NEEDS_REVIEW", "confidence": 0.50}
class LocalLedgerShieldEnv:
def __init__(self, db: dict[str, Any] | None = None) -> None:
self._env = LedgerShieldEnvironment(db=db)
def reset(self, seed: int | None = None, case_id: str | None = None) -> StepResult[Any]:
observation = self._env.reset(seed=seed, case_id=case_id)
return StepResult(
observation=observation,
reward=float(self._env._last_reward),
done=bool(self._env._last_done),
info=dict(self._env._last_info),
)
def step(self, action: LedgerShieldAction) -> StepResult[Any]:
observation = self._env.step(action)
return StepResult(
observation=observation,
reward=float(self._env._last_reward),
done=bool(self._env._last_done),
info=dict(self._env._last_info),
)
def close(self) -> None:
return None
def perform_step(
env: Any,
step_no: int,
rewards: list[float],
action: LedgerShieldAction,
*,
emit_logs: bool = True,
collected: dict[str, Any] | None = None,
) -> tuple[Any, int]:
result = env.step(action)
reward = float(result.reward or 0.0)
rewards.append(reward)
# Phase 4.4: Zero-Shot PPO Hooks
if collected is not None and hasattr(result, "info"):
rl_data = result.info.get("rl_data_plane")
if rl_data:
collected.setdefault("rl_trace", []).append({
"step": step_no,
"action": action.action_type,
"rl_data_plane": rl_data
})
# The validator expects the raw last_action_error only, or null when absent.
error = getattr(result.observation, "last_action_error", None)
if emit_logs:
log_step(
step=step_no,
action=format_action(action),
reward=reward,
done=bool(result.done),
error=error,
)
return result, step_no + 1
def capture_invoice_data(collected: dict[str, Any], tool_result: dict[str, Any]) -> None:
doc_id = str(tool_result.get("doc_id", ""))
tokens = list(tool_result.get("tokens", []) or [])
fields, evidence, line_items = parse_invoice_tokens(tokens, doc_id)
record = {
"doc_id": doc_id,
"tokens": tokens,
"fields": fields,
"evidence": evidence,
"line_items": line_items,
"line_tokens": [token for token in tokens if "|" in str(token.get("text", ""))],
}
collected.setdefault("invoice_records", []).append(record)
collected["invoice_doc_id"] = doc_id
collected["invoice_tokens"] = tokens
collected["invoice_fields"] = fields
collected["invoice_evidence"] = evidence
collected["invoice_line_items"] = line_items
collected["invoice_line_tokens"] = record["line_tokens"]
def capture_email_data(collected: dict[str, Any], tool_result: dict[str, Any]) -> None:
doc_id = str(tool_result.get("doc_id", ""))
tokens = list(tool_result.get("tokens", []) or [])
collected["email_doc_id"] = doc_id
collected["email_tokens"] = tokens
collected["email_evidence"] = parse_email_tokens(tokens, doc_id)
def update_collected_from_tool_result(
collected: dict[str, Any],
action: LedgerShieldAction,
tool: dict[str, Any],
*,
email_doc_id: str,
) -> None:
tool_name = tool.get("tool_name")
_capture_tool_artifacts(collected, tool)
if not tool.get("success", False):
_record_tool_failure(collected, action, tool)
if tool_name == "lookup_vendor" and tool.get("success"):
collected["vendor"] = tool.get("vendor") or {}
refresh_email_thread_from_ocr(collected)
elif tool_name == "lookup_po" and tool.get("success"):
collected["po"] = tool.get("po")
elif tool_name == "lookup_receipt" and tool.get("success"):
collected["receipt"] = tool.get("receipt")
elif tool_name == "search_ledger" and tool.get("success"):
hits = list(tool.get("hits", []) or [])
existing_ids = {row.get("ledger_id") for row in collected["ledger_hits"]}
for hit in hits:
if hit.get("ledger_id") not in existing_ids:
collected["ledger_hits"].append(hit)
existing_ids.add(hit.get("ledger_id"))
invoice_key = normalize_text(action.payload.get("invoice_number"))
if invoice_key:
collected["ledger_queries"][invoice_key] = tool
collected["ledger_search"] = tool
elif tool_name == "lookup_vendor_history" and tool.get("success"):
collected["vendor_history"] = list(tool.get("history", []) or [])
elif tool_name == "inspect_email_thread" and tool.get("success"):
collected["email_thread"] = tool.get("thread") or {}
refresh_email_thread_from_ocr(collected)
elif tool_name == "compare_bank_account" and tool.get("success"):
collected["bank_compare"] = tool
collected["bank_compares"].append(tool)
elif tool_name == "lookup_policy" and tool.get("success"):
collected["policies"] = list(tool.get("policies", []) or [])
elif tool_name == "ocr" and tool.get("success") and tool.get("doc_id") == email_doc_id:
capture_email_data(collected, tool)
refresh_email_thread_from_ocr(collected)
def run_episode_with_env(
env: Any,
case_id: str,
client: Optional[OpenAI],
*,
temperature: float = TEMPERATURE,
emit_logs: bool = True,
) -> dict[str, Any]:
rewards: list[float] = []
steps_taken = 0
final_score = 0.0
success = False
task_type = "unknown"
final_decision = ""
score_breakdown: dict[str, Any] = {}
pressure_resistance = 0.0
if emit_logs:
log_start(task=case_id, env=BENCHMARK, model=MODEL_NAME)
try:
reset_result = env.reset(case_id=case_id)
observation = reset_result.observation
task_type = observation.task_type
max_steps = int(getattr(observation, "max_steps", MAX_STEPS) or MAX_STEPS)
step_no = 1
profile = current_model_profile()
step_limit = min(int(getattr(observation, "max_steps", MAX_STEPS) or MAX_STEPS), MAX_STEPS)
collected: dict[str, Any] = {
"invoice_doc_id": "",
"invoice_tokens": [],
"invoice_fields": {},
"invoice_evidence": {},
"invoice_line_items": [],
"invoice_line_tokens": [],
"invoice_records": [],
"email_doc_id": "",
"email_tokens": [],
"email_evidence": {},
"po": None,
"receipt": None,
"ledger_hits": [],
"ledger_queries": {},
"ledger_search": {},
"vendor_history": [],
"email_thread": {},
"bank_compare": None,
"bank_compares": [],
"vendor": {},
"revealed_artifacts": {},
"pending_events": [],
"observed_risk_signals": [],
"callback_result": {},
"bank_change_approval_chain": {},
"po_reconciliation_report": {},
"receipt_reconciliation_report": {},
"duplicate_cluster_report": {},
"tool_failures": {},
"case_instruction": str(getattr(observation, "instruction", "") or ""),
"case_metadata": dict(getattr(observation, "case_metadata", {}) or {}),
"_client": client,
}
update_collected_from_observation(collected, observation)
executed_signatures: set[str] = set()
invoice_doc_ids: list[str] = []
email_doc_id = ""
for doc in observation.visible_documents:
doc_type = normalize_text(doc.get("doc_type"))
if doc_type == "invoice":
invoice_doc_ids.append(str(doc.get("doc_id")))
if doc_type == "email" and not email_doc_id:
email_doc_id = str(doc.get("doc_id"))
if not invoice_doc_ids:
raise RuntimeError(f"No visible invoice document for {case_id}")
for invoice_doc_id in invoice_doc_ids:
invoice_ocr_action = LedgerShieldAction(
action_type="ocr",
payload={"doc_id": invoice_doc_id, "mode": "accurate"},
)
executed_signatures.add(action_signature(invoice_ocr_action))
ocr_invoice_result, step_no = perform_step(
env,
step_no,
rewards,
invoice_ocr_action,
emit_logs=emit_logs,
)
steps_taken = step_no - 1
capture_invoice_data(collected, ocr_invoice_result.observation.last_tool_result)
if task_type not in {"task_d", "task_e"}:
break
if task_type == "task_a":
invoice_doc_id = invoice_doc_ids[0]
zoom_result, step_no = perform_step(
env,
step_no,
rewards,
LedgerShieldAction(
action_type="zoom",
payload={"doc_id": invoice_doc_id, "bbox": [0, 0, 400, 400]},
),
emit_logs=emit_logs,
)
steps_taken = step_no - 1
if zoom_result.done:
final_score = normalize_score(zoom_result.info.get("final_score", rewards[-1] if rewards else 0.0))
success = final_score >= SUCCESS_SCORE_THRESHOLD
submit_payload = build_final_submission(task_type, collected, {})
final_result, step_no = perform_step(
env,
step_no,
rewards,
LedgerShieldAction(action_type="submit_decision", payload=submit_payload),
emit_logs=emit_logs,
)
steps_taken = step_no - 1
final_score = normalize_score(final_result.info.get("final_score", final_result.reward or 0.0))
success = final_score >= SUCCESS_SCORE_THRESHOLD
final_decision = str((final_result.observation.last_tool_result or {}).get("decision", submit_payload.get("decision", "")))
score_breakdown = dict(final_result.info.get("score_breakdown", {}) or {})
pressure_resistance = float(final_result.info.get("pressure_resistance_score", 0.0) or 0.0)
return {
"case_id": case_id,
"task_type": task_type,
"score": final_score,
"steps": steps_taken,
"final_decision": final_decision,
"score_breakdown": score_breakdown,
"pressure_resistance_score": pressure_resistance,
}
invoice_fields = collected["invoice_fields"]
vendor_key = vendor_key_for(invoice_fields)
if not vendor_key:
vendor_key = normalize_text((collected.get("email_thread") or {}).get("vendor_key"))
po_id = str(invoice_fields.get("po_id", "")).strip()
receipt_id = str(invoice_fields.get("receipt_id", "")).strip()
invoice_total = safe_float(invoice_fields.get("total"))
invoice_number = str(invoice_fields.get("invoice_number", "")).strip()
proposed_bank_account = str(invoice_fields.get("bank_account", "")).strip()
def execute_action_batch(actions: list[LedgerShieldAction]) -> bool:
nonlocal step_no, steps_taken, final_score, success, final_decision, score_breakdown, pressure_resistance
for action in actions:
if step_no > step_limit:
break
executed_signatures.add(action_signature(action))
result, step_no = perform_step(env, step_no, rewards, action, emit_logs=emit_logs, collected=collected)
steps_taken = step_no - 1
tool = result.observation.last_tool_result or {}
update_collected_from_tool_result(collected, action, tool, email_doc_id=email_doc_id)
update_collected_from_observation(collected, result.observation)
if result.done:
final_score = normalize_score(result.info.get("final_score", result.reward or 0.0))
success = final_score >= SUCCESS_SCORE_THRESHOLD
final_decision = str(tool.get("decision", ""))
score_breakdown = dict(result.info.get("score_breakdown", {}) or {})
pressure_resistance = float(result.info.get("pressure_resistance_score", 0.0) or 0.0)
return True
return False
def next_pending_artifact_action() -> LedgerShieldAction | None:
pending_ids = {
normalize_text(event.get("artifact_id"))
for event in collected.get("pending_events", []) or []
if normalize_text(event.get("artifact_id"))
}
high_value_pending = {
"task_b": {
"callback_verification_result",
"po_reconciliation_report",
"receipt_reconciliation_report",
},
"task_c": {
"callback_verification_result",
"bank_change_approval_chain",
"duplicate_cluster_report",
},
"task_d": {
"callback_verification_result",
"bank_change_approval_chain",
"duplicate_cluster_report",
},
"task_e": {
"callback_verification_result",
"bank_change_approval_chain",
"duplicate_cluster_report",
},
}.get(task_type, set())
if not (pending_ids & high_value_pending):
return None
if task_type == "task_c":
pending_vendor_key = normalize_text((collected.get("vendor") or {}).get("vendor_key")) or vendor_key_for(
collected.get("invoice_fields", {}) or {}
)
if pending_vendor_key:
return LedgerShieldAction(
action_type="lookup_vendor",
payload={"vendor_key": pending_vendor_key},
)
payload: dict[str, Any] = {}
if invoice_total:
payload["amount"] = invoice_total
if invoice_number:
payload["invoice_number"] = invoice_number
if payload:
return LedgerShieldAction(action_type="search_ledger", payload=payload)
return LedgerShieldAction(action_type="lookup_policy", payload={})
def drain_pending_artifacts() -> bool:
nonlocal step_no, steps_taken, final_score, success, final_decision, score_breakdown, pressure_resistance
wait_budget = 2 if profile.tier == "elite" else (1 if profile.tier == "strong" else 0)
while wait_budget > 0 and step_no < step_limit:
wait_action = next_pending_artifact_action()
if wait_action is None:
break
executed_signatures.add(action_signature(wait_action))
result, step_no = perform_step(env, step_no, rewards, wait_action, emit_logs=emit_logs, collected=collected)
steps_taken = step_no - 1
tool = result.observation.last_tool_result or {}
update_collected_from_tool_result(collected, wait_action, tool, email_doc_id=email_doc_id)
update_collected_from_observation(collected, result.observation)
wait_budget -= 1
if result.done:
final_score = normalize_score(result.info.get("final_score", result.reward or 0.0))
success = final_score >= SUCCESS_SCORE_THRESHOLD
final_decision = str(tool.get("decision", ""))
score_breakdown = dict(result.info.get("score_breakdown", {}) or {})
pressure_resistance = float(result.info.get("pressure_resistance_score", 0.0) or 0.0)
return True
return False
remaining_action_slots = max(0, step_limit - step_no + 1)
investigation_budget = {
"task_b": 3,
"task_c": 3,
"task_d": 6,
"task_e": 8,
}.get(task_type, 0) + profile.investigation_budget_bonus
investigation_candidates = build_investigation_candidates(
task_type,
collected,
vendor_key=vendor_key,
po_id=po_id,
receipt_id=receipt_id,
invoice_total=invoice_total,
invoice_number=invoice_number,
proposed_bank_account=proposed_bank_account,
email_doc_id=email_doc_id,
executed_signatures=executed_signatures,
)
planned_investigation = llm_plan_actions(
client,
task_type=task_type,
phase="investigation",
collected=collected,
candidates=investigation_candidates,
max_actions=min(investigation_budget, remaining_action_slots),
)
if execute_action_batch(planned_investigation):
return {
"case_id": case_id,
"task_type": task_type,
"score": final_score,
"steps": steps_taken,
"final_decision": final_decision,
"score_breakdown": score_breakdown,
"pressure_resistance_score": pressure_resistance,
"rl_trace": collected.get("rl_trace", []),
}
submit_payload = repair_submission(task_type, build_final_submission(task_type, collected, {}), collected)
remaining_action_slots = max(0, step_limit - step_no + 1)
intervention_budget = {
"task_b": 3,
"task_c": 5,
"task_d": 5,
"task_e": 6,
}.get(task_type, 0) + profile.intervention_budget_bonus
intervention_candidates = build_intervention_candidates(
task_type,
collected,
submit_payload,
executed_signatures=executed_signatures,
)
planned_interventions = llm_plan_actions(
client,
task_type=task_type,
phase="intervention",
collected=collected,
candidates=intervention_candidates,
max_actions=min(intervention_budget, remaining_action_slots),
current_submission=submit_payload,
)
if execute_action_batch(planned_interventions):
return {
"case_id": case_id,
"task_type": task_type,
"score": final_score,
"steps": steps_taken,
"final_decision": final_decision,
"score_breakdown": score_breakdown,
"pressure_resistance_score": pressure_resistance,
"rl_trace": collected.get("rl_trace", []),
}
if drain_pending_artifacts():
return {
"case_id": case_id,
"task_type": task_type,
"score": final_score,
"steps": steps_taken,
"final_decision": final_decision,
"score_breakdown": score_breakdown,
"pressure_resistance_score": pressure_resistance,
"rl_trace": collected.get("rl_trace", []),
}
submit_payload = repair_submission(task_type, build_final_submission(task_type, collected, {}), collected)
if step_no <= step_limit:
final_result, step_no = perform_step(
env,
step_no,
rewards,
LedgerShieldAction(action_type="submit_decision", payload=submit_payload),
emit_logs=emit_logs,
)
steps_taken = step_no - 1
final_score = normalize_score(final_result.info.get("final_score", final_result.reward or 0.0))
success = final_score >= SUCCESS_SCORE_THRESHOLD
last_tool = final_result.observation.last_tool_result or {}
final_decision = str(last_tool.get("decision", submit_payload.get("decision", "")))
score_breakdown = dict(final_result.info.get("score_breakdown", {}) or {})
pressure_resistance = float(final_result.info.get("pressure_resistance_score", 0.0) or 0.0)
else:
final_score = normalize_score(rewards[-1] if rewards else 0.0)
success = False
return {
"case_id": case_id,
"task_type": task_type,
"score": final_score,
"steps": steps_taken,
"final_decision": final_decision,
"score_breakdown": score_breakdown,
"pressure_resistance_score": pressure_resistance,
"rl_trace": collected.get("rl_trace", []),
}
except Exception as exc: # noqa: BLE001
trace(f"[ERROR] episode failed for {case_id}: {exc}")
return {
"case_id": case_id,
"task_type": task_type,
"score": 0.0,
"steps": steps_taken,
"final_decision": "",
"score_breakdown": {},
"pressure_resistance_score": 0.0,
"error": str(exc),
"rl_trace": [],
}
finally:
try:
env.close()
except Exception as exc: # noqa: BLE001
trace(f"[DEBUG] env.close failed for {case_id}: {exc}")
if emit_logs:
log_end(success=success, steps=steps_taken, rewards=rewards, score=final_score)
def run_episode(
env_url: str,
case_id: str,
client: Optional[OpenAI],
*,
temperature: float = TEMPERATURE,
emit_logs: bool = True,
) -> dict[str, Any]:
env = LedgerShieldEnv(base_url=env_url)
return run_episode_with_env(
env=env,
case_id=case_id,
client=client,
temperature=temperature,
emit_logs=emit_logs,
)
def build_openai_client() -> Optional[OpenAI]:
if not HF_TOKEN:
trace("[DEBUG] HF_TOKEN not set; running heuristic-only baseline.")
return None
try:
return OpenAI(base_url=API_BASE_URL, api_key=HF_TOKEN)
except Exception as exc: # noqa: BLE001
trace(f"[DEBUG] failed to initialize OpenAI client: {exc}")
return None
def run_baseline_inference(
env_url: str,
cases: list[str],
*,
temperature: float = TEMPERATURE,
pass_k: int = 1,
pass_threshold: float = PASSK_SUCCESS_THRESHOLD,
emit_logs: bool = True,
) -> dict[str, Any]:
client = build_openai_client()
results: list[dict[str, Any]] = []
total_trial_successes = 0
total_trials = 0
for case_id in cases:
trials = [
run_episode(
env_url=env_url,
case_id=case_id,
client=client,
temperature=temperature,
emit_logs=emit_logs,
)
for _ in range(max(1, int(pass_k)))
]
summary = summarize_case_trials(case_id, trials, pass_threshold=pass_threshold)
results.append(summary)
total_trial_successes += int(summary.get("successful_trials", 0) or 0)
total_trials += int(summary.get("trial_count", 0) or 0)
if emit_logs and pass_k > 1:
trace(
"[PASSK] "
f"case={case_id} "
f"k={int(pass_k)} "
f"trial_pass_rate={float(summary.get('trial_pass_rate', 0.0)):.3f} "
f"consistent_pass={str(bool(summary.get('pass_k_consistent', False))).lower()}"
)
avg_score = sum(result.get("score", 0.0) for result in results) / max(len(results), 1)
consistent_pass_rate = sum(bool(result.get("pass_k_consistent", False)) for result in results) / max(len(results), 1)
any_pass_rate = sum(bool(result.get("pass_k_any", False)) for result in results) / max(len(results), 1)
trial_pass_rate = total_trial_successes / max(total_trials, 1)
trace(
"[SUMMARY] "
f"cases={len(results)} "
f"avg_score={avg_score:.4f} "
f"scores={compact_json({result['case_id']: result.get('score', 0.0) for result in results})}"
)
return {
"results": results,
"average_score": round(avg_score, 4),
"temperature": round(float(temperature), 4),
"pass_k": int(pass_k),
"pass_threshold": round(float(pass_threshold), 4),
"trial_pass_rate": round(trial_pass_rate, 4),
"consistent_pass_rate": round(consistent_pass_rate, 4),
"any_pass_rate": round(any_pass_rate, 4),
"model_profile": asdict(current_model_profile()),
}
def run_local_baseline(
cases: list[str],
*,
db: dict[str, Any] | None = None,
client: Optional[OpenAI] = None,
emit_logs: bool = False,
temperature: float = TEMPERATURE,
pass_k: int = 1,
pass_threshold: float = PASSK_SUCCESS_THRESHOLD,
) -> dict[str, Any]:
results: list[dict[str, Any]] = []
total_trial_successes = 0
total_trials = 0
for case_id in cases:
trials = [
run_episode_with_env(
env=LocalLedgerShieldEnv(db=db),
case_id=case_id,
client=client,
emit_logs=emit_logs,
temperature=temperature,
)
for _ in range(max(1, int(pass_k)))
]
summary = summarize_case_trials(case_id, trials, pass_threshold=pass_threshold)
results.append(summary)
total_trial_successes += int(summary.get("successful_trials", 0) or 0)
total_trials += int(summary.get("trial_count", 0) or 0)
avg_score = sum(result.get("score", 0.0) for result in results) / max(len(results), 1)
consistent_pass_rate = sum(bool(result.get("pass_k_consistent", False)) for result in results) / max(len(results), 1)
any_pass_rate = sum(bool(result.get("pass_k_any", False)) for result in results) / max(len(results), 1)
return {
"results": results,
"average_score": round(avg_score, 4),
"temperature": round(float(temperature), 4),
"pass_k": int(pass_k),
"pass_threshold": round(float(pass_threshold), 4),
"trial_pass_rate": round(total_trial_successes / max(total_trials, 1), 4),
"consistent_pass_rate": round(consistent_pass_rate, 4),
"any_pass_rate": round(any_pass_rate, 4),
"model_profile": asdict(current_model_profile()),
}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="LedgerShield baseline inference")
parser.add_argument("--api-url", default=API_BASE_URL)
parser.add_argument("--model", default=MODEL_NAME)
parser.add_argument("--token", default=HF_TOKEN)
parser.add_argument("--env-url", default=ENV_URL)
parser.add_argument("--cases", nargs="+", default=DEFAULT_CASES)
parser.add_argument("--temperature", type=float, default=TEMPERATURE)
parser.add_argument("--passK", type=int, default=1)
parser.add_argument("--pass-threshold", type=float, default=PASSK_SUCCESS_THRESHOLD)
parser.add_argument("--output-artifact", default="")
parser.add_argument("--no-logs", action="store_true")
return parser.parse_args()
def main() -> None:
global API_BASE_URL, MODEL_NAME, HF_TOKEN, ENV_URL
args = parse_args()
API_BASE_URL = args.api_url
MODEL_NAME = args.model
HF_TOKEN = args.token
ENV_URL = args.env_url
emit_logs = True
if args.no_logs:
trace("[DEBUG] Ignoring --no-logs to preserve benchmark stdout format.")
payload = run_baseline_inference(
env_url=args.env_url,
cases=args.cases,
temperature=float(args.temperature),
pass_k=max(1, int(args.passK)),
pass_threshold=float(args.pass_threshold),
emit_logs=emit_logs,
)
payload["generated_at"] = datetime.now(timezone.utc).isoformat()
payload["model"] = MODEL_NAME
payload["env_url"] = args.env_url
payload["api_base_url"] = API_BASE_URL
if args.output_artifact:
write_run_artifact(args.output_artifact, payload)
if __name__ == "__main__":
main()