Output contracts and language policies for vLLM, SGLang, LiteLLM, and compatible LLM APIs.
Project description
LangFence
LangFence puts a small, explicit contract around LLM output.
It compiles format constraints into the strongest request fields a provider supports, adds language guidance when useful, and validates the returned text locally. The goal is not to pretend natural language can be perfectly constrained. The goal is to make format and language expectations visible, testable, and easy to use with local or OpenAI-compatible serving stacks.
What It Supports
- vLLM and SGLang, out of the box
- LiteLLM and generic OpenAI-compatible
/chat/completionsendpoints - Anthropic-compatible
/messagesendpoints - Python API, CLI, and an optional OpenAI-compatible sidecar proxy
- JSON Schema, regex, choice, grammar/EBNF, and structural tag contracts
- Language include/exclude policies, checked after generation
- Redacted CLI/service diagnostics by default
Install
pip install langfence
For local development:
pip install -e ".[dev,all]"
Python API
For most Python code, start with the LangFence facade:
from langfence import LangFence, LanguagePolicy
fence = LangFence(language=LanguagePolicy(include=["zh"], exclude=["en"], min_confidence=0.2))
result = fence.validate("这是一个中文回答。")
assert result.valid
Use compile_request when you only want the provider payload:
from langfence import JsonSchemaConstraint, LanguagePolicy, OutputContract, compile_request
contract = OutputContract(
format=JsonSchemaConstraint(
name="answer",
schema={
"type": "object",
"properties": {"answer": {"type": "string"}, "language": {"enum": ["zh"]}},
"required": ["answer", "language"],
"additionalProperties": False,
},
),
language=LanguagePolicy(include=["zh"], exclude=["en"], min_confidence=0.2),
)
compiled = compile_request(
"vllm",
[{"role": "user", "content": "用中文解释 constrained decoding。"}],
contract,
base_payload={"model": "Qwen/Qwen2.5-7B-Instruct"},
)
Use LangFenceClient when you want request, retry, extraction, and validation in one
place:
from langfence import LangFence, RegexConstraint
fence = LangFence(format=RegexConstraint(r"^(approved|rejected)$"))
client = fence.client(
provider="openai-compatible",
base_url="http://localhost:8000/v1",
model="local-model",
max_retries=1,
)
result = client.chat([{"role": "user", "content": "Return approved or rejected."}])
assert result.ok
print(result.text)
Provider names accepted by the client:
vllmsglangopenaioropenai-compatiblelitellmanthropicoranthropic-compatible
For Anthropic-compatible endpoints, use the Messages API base URL:
import os
client = fence.client(
provider="anthropic",
base_url="https://api.anthropic.com/v1",
model="claude-compatible",
api_key=os.environ["ANTHROPIC_API_KEY"],
)
CLI
Compile a request payload:
langfence compile --provider vllm --contract examples/contract.zh.yaml
langfence compile --provider sglang --contract examples/contract.zh.yaml
Call a model and validate its output:
langfence chat \
--provider vllm \
--base-url http://localhost:8000/v1 \
--model local-model \
--contract examples/contract.zh.yaml \
--prompt "用中文回答。"
Use --api-key-env OPENAI_API_KEY or --api-key-env ANTHROPIC_API_KEY to read keys
from the environment. CLI output redacts model text by default; add
--show-sensitive only in a trusted local shell.
Validate saved output:
langfence validate --contract examples/contract.zh.yaml --input output.txt
Run the optional OpenAI-compatible proxy:
pip install "langfence[service]"
langfence proxy --provider vllm --base-url http://localhost:8000/v1
Provider Behavior
| Provider | Format handling | Language handling |
|---|---|---|
| vLLM | Uses vLLM structured output fields where available | Prompt guidance + local validation |
| SGLang | Uses SGLang regex/JSON/EBNF/structural fields where available | Prompt guidance + local validation |
| OpenAI-compatible | Uses standard JSON Schema response_format; other formats are validated after generation |
Prompt guidance + local validation |
| LiteLLM | Same portable OpenAI-compatible behavior | Prompt guidance + local validation |
| Anthropic-compatible | Uses Messages system guidance; validation runs after generation |
Prompt guidance + local validation |
LangFence does not log prompts, outputs, or provider responses. HTTP error bodies are hidden by default because providers may echo request content.
LangFence validates text returned to the caller. It cannot inspect or constrain hidden
reasoning traces or internal chain-of-thought that a provider does not expose. If a
local reasoning model returns a leading visible block such as <think>...</think>,
LangFence strips that block before validation by default and checks the final answer.
This keeps reasoning-model output quality intact: no extra constrained decoding or
prompt pressure is added just to control the language of private reasoning.
Development
pytest
ruff check .
mypy src/langfence
See docs/architecture.md for design notes and docs/privacy.md for privacy rules.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file langfence-0.1.3.tar.gz.
File metadata
- Download URL: langfence-0.1.3.tar.gz
- Upload date:
- Size: 24.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96405f5ee523bafc2050c1b51414ccfb893bcf9b8822025368156f23c22bf324
|
|
| MD5 |
3f679b85c4c242d7edae1863dbcc21b8
|
|
| BLAKE2b-256 |
b188931286cf0623315fcfe461319bd9c59bcbcbf65bfaa4594c64184265d098
|
Provenance
The following attestation bundles were made for langfence-0.1.3.tar.gz:
Publisher:
publish.yml on jianbo27/langfence
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
langfence-0.1.3.tar.gz -
Subject digest:
96405f5ee523bafc2050c1b51414ccfb893bcf9b8822025368156f23c22bf324 - Sigstore transparency entry: 2092780273
- Sigstore integration time:
-
Permalink:
jianbo27/langfence@7a89b284bff60725259c8ce2d4db796bb4b84415 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/jianbo27
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7a89b284bff60725259c8ce2d4db796bb4b84415 -
Trigger Event:
release
-
Statement type:
File details
Details for the file langfence-0.1.3-py3-none-any.whl.
File metadata
- Download URL: langfence-0.1.3-py3-none-any.whl
- Upload date:
- Size: 34.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b5f6f89a149524d9c66afa0729cbe8485e8b9334db62603c2de2ec82f52b7ed6
|
|
| MD5 |
fc3d3639fc0b21a941d52c150f46cdc2
|
|
| BLAKE2b-256 |
53b473eacc19c8a7f515a9c246ed4ae6336528a3ae9b80ccdf0da7fe2ac5d6cc
|
Provenance
The following attestation bundles were made for langfence-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on jianbo27/langfence
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
langfence-0.1.3-py3-none-any.whl -
Subject digest:
b5f6f89a149524d9c66afa0729cbe8485e8b9334db62603c2de2ec82f52b7ed6 - Sigstore transparency entry: 2092780452
- Sigstore integration time:
-
Permalink:
jianbo27/langfence@7a89b284bff60725259c8ce2d4db796bb4b84415 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/jianbo27
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7a89b284bff60725259c8ce2d4db796bb4b84415 -
Trigger Event:
release
-
Statement type: