A minimal prototype that implements a Structural Constraint Gate for LLM outputs: a deterministic contract boundary between probabilistic generation and downstream application logic.
LLM outputs are probabilistic. Tokens are sampled; formatting and structure vary. Downstream systems often assume a fixed contract (e.g. JSON with required fields and types). Using raw LLM output as if it were a deterministic API response leads to parse failures, type errors, and undefined behavior.
A Structural Constraint Gate is a validation layer that accepts or rejects output strictly against a schema. Only well-formed, schema-compliant data crosses the boundary. Rejected output does not reach the rest of the system.
This is structural validation, not semantic safety filtering. The gate checks shape, types, required fields, and allowed values. It does not evaluate truthfulness, intent, or business rules. A structurally valid response can still be wrong or misleading.
- Validates raw LLM output (here, simulated as JSON strings) against a single fixed schema.
- Enforces strict structural rules: no type coercion (e.g. string or int to float is rejected), exact enum matching (case-sensitive), no extra fields.
- Returns either the validated structured object (accept) or a rejection with reason (reject). No silent correction, retries, or auto-fix.
- This project intentionally avoids semantic judgment, business rule validation, or auto-correction. It enforces structure only.
The prototype uses a simulated LLM client (no external API). The validation path is local and deterministic.
- Raw output (e.g. from an LLM) is passed as a string.
- The string is parsed as JSON. Parse failure → reject.
- The parsed object is validated against the schema. Validation failure → reject.
- Success yields a typed instance; failure yields an error reason.
Core API: validate(raw_output: str) → ValidationSuccess | ValidationFailure.
User Input
│
▼
Prompt Builder
│
▼
LLM
│
▼
┌─────────────────────────────────────────┐
│ Structural Gate │
│ (Deterministic Contract Boundary) │
│ Schema Validation Layer │
└─────────────────────────────────────────┘
│
▼
Accept / Reject
│
▼
Downstream System
The Structural Gate is the single place where output is checked against the contract. Nothing that fails validation reaches the downstream system.
SentimentResult — all fields required, no extra properties:
| Field | Type | Constraint |
|---|---|---|
| sentiment | enum | "positive" | "negative" | "neutral" |
| confidence | float | 0.0 ≤ value ≤ 1.0 (strict float; no int/string) |
| summary | string | minLength 5 |
{"sentiment": "positive", "confidence": 0.92, "summary": "Customer is happy"}- Malformed JSON:
{"sentiment": "positive" "confidence": 0.92, ...}→ reject (parse error). - Wrong type:
"confidence": "0.92"(string) or"confidence": 1(int) → reject (strict float). - Enum:
"sentiment": "Positive"or"positve"→ reject (exact enum only). - Extra field:
"reasoning": "..."→ reject (additional properties forbidden). - Missing field: no
summary→ reject.
from app.validator import validate
raw = '{"sentiment": "positive", "confidence": 0.92, "summary": "Customer is happy"}'
result = validate(raw)
if hasattr(result, "instance"):
# Accepted: use result.instance (typed SentimentResult)
print(result.instance.model_dump())
else:
# Rejected: result.reason describes the failure
print("Rejected:", result.reason)python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
uvicorn app.main:app --reloadServer: http://127.0.0.1:8000
POST /invoke body: {"mode": "VALID"}.
Modes include: VALID, ENUM_VIOLATION, EXTRA_FIELD, MISSING_FIELD, NUMERIC_BOUND_VIOLATION, MALFORMED_JSON, STRING_INSTEAD_OF_FLOAT, INT_INSTEAD_OF_FLOAT, ENUM_CASE_VARIATION, SEMANTICALLY_WRONG. Any other mode returns 400 (unknown mode).
- Validation passes: 200 with the validated JSON.
- Validation fails: 400 with
{"status": "rejected", "reason": "<message>"}.
# Accepted
curl -s -X POST http://127.0.0.1:8000/invoke -H "Content-Type: application/json" -d '{"mode": "VALID"}'
# Rejected (e.g. enum typo)
curl -s -X POST http://127.0.0.1:8000/invoke -H "Content-Type: application/json" -d '{"mode": "ENUM_VIOLATION"}'pip install -r requirements.txt
pytest tests/ -vThis prototype focuses exclusively on structural validation. Future exploration may include:
- Multi-schema support (contract per workflow).
- Structured output prompting integration.
- Typed client SDK generation from schema.
- Integration with execution workflows (e.g., reject → retry policy at application layer).
The gate itself will remain a deterministic boundary. Retry logic, fallback policy, and business rules belong outside this layer.
Contributions are welcome. See CONTRIBUTING.md for setup, tests, and pull request guidelines. Security issues: see SECURITY.md; do not report them as public issues.
MIT.