CoolFace
Apppublic

SayujGupta2005/gst-reconciliation-env

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
App README

GST Reconciliation OpenEnv

A real-world OpenEnv benchmark for GST invoice reconciliation. The environment simulates a finance operations workflow where an AI agent receives OCR-like invoice text, extracts key fields, validates the correct GST tax regime, and flags deterministic arithmetic discrepancies before submission.

The benchmark is intentionally text-first rather than image-OCR-first. This is a deliberate design choice: it models a genuine back-office finance task while remaining fully deterministic, dependency-light, and reproducible on free open-source tooling — no proprietary OCR or paid APIs required.


Why this domain

GST invoice verification is a high-volume back-office task in Indian finance operations. Teams routinely check invoice metadata, taxable amounts, interstate vs intrastate tax logic (IGST vs CGST+SGST), and arithmetic consistency before accepting invoices into ERP or Input Tax Credit (ITC) workflows. Errors — whether accidental or fraudulent — have direct compliance and cash-flow consequences.

This environment turns that real process into a clean, deterministic agent benchmark with dense intermediate rewards and a meaningful episode-level objective.


Benchmark design

The environment implements the standard OpenEnv interface: typed Action, Observation, and State models, plus reset(), step(), and a state property.

Observation space

FieldTypeDescription
task_idstrStable identifier for the current task
difficulty`"easy" \"medium" \"hard"`Difficulty bucket
task_goalstrHuman-readable episode objective
document_textstrOCR-like raw invoice text
committed_fieldsDict[str, str]Fields already extracted and committed this episode
validation_feedback`str \None`Structured feedback from the last action
available_fieldsList[str]Field names relevant to the current task
calculator_result`float \None`Latest computed tax amount if calculate_tax was used
last_action_error`str \None`Error message from the previous action, if any
final_score`float \None`Episode-level score in [0.0, 1.0]; set only after termination
remaining_stepsintSteps remaining before auto-termination

Action space

The agent sends exactly one typed action per step:

`action_type`Required fieldsPurpose
extract_fieldfield_name, field_valuePropose a field extraction from the invoice
calculate_taxtaxable_value, rate_percentAsk the environment to compute the GST amount
flag_discrepancydiscrepancy_type, line_item, rationaleFormally flag a suspected invoice error
submit—Terminate the episode and trigger the final grader

Tasks

1. Basic extraction — easy

Goal: Extract the five mandatory invoice fields required by the workflow.

Required fields: supplier_gstin, invoice_number, invoice_date, taxable_value, total_invoice_value

Grader: Exact-match fraction over the five required fields. Score = correct / 5, in [0.0, 1.0].


2. Tax validation — medium

Goal: Identify the correct GST tax regime and validate the tax breakup.

Required reasoning:

  • —Parse taxable value and GST rate from invoice text
  • —Compare the first two digits of the supplier GSTIN with the place-of-supply state code
  • —If they match → intrastate → CGST_SGST; if they differ → interstate → IGST
  • —Extract the correct tax amounts for the determined regime

Grader: Weighted partial credit:

FieldWeight
taxable_value0.25
gst_rate0.15
place_of_supply_code0.15
correct_tax_type0.30
Correct tax amount(s) consistent with regime0.15

Maximum score: 1.0. A wrong correct_tax_type caps the score at 0.50.


3. Fraud detection — hard

Goal: Detect an arithmetic discrepancy in the invoice and formally flag it before submitting.

Required reasoning:

  • —Compute expected tax from taxable_value × gst_rate / 100
  • —Infer the correct tax regime from GSTIN state codes
  • —Compare expected_total_invoice_value with stated_total_invoice_value
  • —Call flag_discrepancy with the correct discrepancy_type, line_item, and a rationale that contains the key numeric evidence

Grader: Two independent components:

ComponentMax contribution
Field extraction (5 fields × 0.06 each)0.30
Discrepancy flag (type match + line item + rationale keywords)0.70

If the agent never calls flag_discrepancy, the episode score is capped at the extraction subtotal (≤ 0.30).


Reward shaping

The environment emits dense per-step rewards rather than a single sparse terminal signal:

EventReward
Every step taken−0.01 (efficiency penalty)
Correct field extraction+0.20
Wrong field extraction / hallucination−0.10
Duplicate field extraction−0.04
Field not present in ground truth−0.08
calculate_tax with correct result+0.08
calculate_tax with wrong result−0.03
calculate_tax (no ground-truth tax to compare)+0.02
Correct discrepancy flag (partial credit)up to +0.30
Completely wrong discrepancy flag−0.15
Action raises a validation error−0.15
submit (terminal)+ final_score from deterministic grader

This gives useful intermediate learning signal while preserving a meaningful episode-level objective.


Determinism

Each call to reset() advances a round-robin index over the scenario list in a fixed curriculum:

easy_001 → medium_001 → hard_001 → easy_001 → ...

Every run is fully reproducible and easy to validate locally before submission.


Project structure

.
├── Dockerfile
├── README.md
├── client.py                         # Top-level re-export of GSTReconciliationEnv
├── inference.py                      # Baseline hybrid agent (heuristic + LLM fallback)
├── models.py                         # Top-level re-export of GSTAction / GSTObservation / GSTState
├── openenv.yaml                      # OpenEnv spec manifest
├── pyproject.toml                    # Package build config
├── requirements.txt                  # Pinned runtime dependencies
├── validate-submission.sh            # Local pre-submission validation helper
├── server
│   ├── __init__.py
│   ├── app.py                        # Thin shim — delegates to gst_reconciliation_env
│   └── environment.py                # Thin shim — delegates to gst_reconciliation_env
└── gst_reconciliation_env
    ├── __init__.py                   # Package exports
    ├── client.py                     # Typed OpenEnv HTTP client
    ├── models.py                     # GSTAction, GSTObservation, GSTState (Pydantic v2)
    └── server
        ├── __init__.py
        ├── app.py                    # FastAPI app via create_fastapi_app
        ├── environment.py            # GSTReconciliationEnvironment — step / reset / grading logic
        └── scenarios.py              # SCENARIOS list (one fixed scenario per difficulty level)

Setup

bash
python -m venv .venv
source .venv/bin/activate
pip install -e .

Start the server locally:

bash
uvicorn gst_reconciliation_env.server.app:app --host 0.0.0.0 --port 7860 --reload

Health / connectivity check:

bash
# GET the root page (always 200 OK)
curl http://localhost:7860/

# Or POST to /reset directly
curl -X POST http://localhost:7860/reset \
  -H "Content-Type: application/json" -d '{}'

Docker

Build and run:

bash
docker build -t gst_reconciliation:latest .
docker run --rm -p 7860:7860 gst_reconciliation:latest

OpenEnv validation

bash
openenv validate

Or use the bundled helper script, which pings a live Space, builds Docker locally, and runs openenv validate:

bash
bash validate-submission.sh https://<your-space>.hf.space .

Baseline inference

The baseline uses a hybrid deterministic agent: regex-based extraction and arithmetic run first; an OpenAI-compatible chat model is called only when the heuristic cannot determine the next action. This keeps runs cheap and reproducible while satisfying the requirement to use the OpenAI client.

Environment variables

VariableDefaultPurpose
ENV_BASE_URLhttp://localhost:7860HTTP base URL of the running environment server
LOCAL_IMAGE_NAME(unset)If set, attempt to spin up environment via Docker; falls back to ENV_BASE_URL automatically if Docker is unavailable
API_BASE_URLhttps://router.huggingface.co/v1OpenAI-compatible LLM endpoint
MODEL_NAMEQwen/Qwen2.5-72B-InstructChat model used for LLM fallback actions
HF_TOKEN / OPENAI_API_KEY / API_KEY(unset)API key for the LLM endpoint (checked in this order)

Run against a local server

bash
export ENV_BASE_URL=http://localhost:7860
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
export HF_TOKEN=your_token
python inference.py

Run against a local Docker image

bash
export LOCAL_IMAGE_NAME=gst_reconciliation:latest
export API_BASE_URL=https://router.huggingface.co/v1
export MODEL_NAME=Qwen/Qwen2.5-72B-Instruct
export HF_TOKEN=your_token
python inference.py
Note on Docker unavailability: If LOCAL_IMAGE_NAME is set but Docker is not reachable in the runtime (e.g. the OpenEnv validator sandbox), inference.py logs a warning and automatically falls back to the HTTP client pointed at ENV_BASE_URL. The process always exits with code 0 — no unhandled exceptions propagate to the validator.

Expected baseline scores

Because the heuristic agent uses deterministic regex patterns tuned to the included scenarios, it should score near ceiling on all three tasks when the environment is running correctly:

TaskExpected score
basic_extraction (easy)~1.000
tax_validation (medium)~0.850
fraud_detection (hard)~0.900

Exact scores depend on the OpenEnv runtime version and any future scenario additions.


Hugging Face Spaces deployment

bash
openenv push --repo-id <your-username>/gst-reconciliation-openenv

After deployment, confirm the Space is live:

bash
curl -X POST https://<your-space>.hf.space/reset \
  -H "Content-Type: application/json" -d '{}'

Free and open-source stack

  • —Python 3.10+
  • —FastAPI — async REST server
  • —Uvicorn — ASGI server
  • —Pydantic v2 — typed action / observation / state models
  • —openenv-core — OpenEnv server and client base classes
  • —OpenAI Python SDK — LLM fallback client (works with any OpenAI-compatible endpoint)
  • —Hugging Face Inference Router — free access to open models (Qwen, Llama, Mistral, etc.)

No proprietary OCR, paid APIs, or closed models are required at any point.


Known limitations

  • —Invoice text is synthetic OCR-like prose, not real scanned document output. A production version would pipe real OCR output through the same interface without changing the agent API.
  • —The current public benchmark exposes one fixed scenario per difficulty level for maximum reproducibility. A richer version should include randomised invoice generation, adversarial formatting noise, and multi-page documents.
  • —The heuristic agent uses regex patterns tuned to the three included scenarios; generalisation to unseen invoice layouts requires the LLM fallback path or a fine-tuned extraction model.