A lightweight Model I/O normalization layer for OpenAI-compatible LLM calls.
Project description
llm-io-normalizer
llm-io-normalizer is a lightweight Model I/O normalization layer for OpenAI-compatible LLM calls.
It is built for applications that call both tested models and judge models and need a stable result contract instead of provider-specific response parsing.
It normalizes common LLM response differences such as:
message.contentvsmessage.reasoningdelta.contentvsdelta.reasoning/delta.reasoning_content<think>...</think>reasoning mixed into normal content- stream vs non-stream completion behavior
- successful HTTP responses that contain reasoning but no final answer
- final JSON extraction from model output
The public contract is intentionally small and stable:
result.answer_text
result.reasoning_text
result.ok
result.error_type
result.error_message
Business code should depend on these normalized fields instead of reading raw provider fields directly.
Install
After publishing to PyPI:
pip install llm-io-normalizer
From a local checkout:
pip install -e .
For development:
pip install -e ".[dev]"
Quick start
import asyncio
from llm_io_normalizer import LLMGateway, LLMRequest, LLMRole
async def main() -> None:
gateway = LLMGateway()
result = await gateway.generate(
LLMRequest(
role=LLMRole.JUDGE_MODEL,
model_name="your-model-name",
base_url="https://example.com/v1",
api_key="YOUR_API_KEY",
messages=[
{"role": "system", "content": "You are a strict JSON judge."},
{"role": "user", "content": "Return {\"result\": 2}."},
],
# Judge models should usually prefer stable non-stream output.
stream=False,
enable_thinking=False,
temperature=0,
max_tokens=1024,
)
)
result.require_ok()
print(result.answer_text)
asyncio.run(main())
Core concepts
Tested model calls
LLMRole.TESTED_MODEL is for the model being evaluated or observed.
Default behavior:
- streams by default unless
stream=Falseis provided - can collect native reasoning fields from compatible providers
- can split
<think>...</think>blocks out of the answer - returns clean
answer_textand separatereasoning_text - can retry without thinking when the provider returns no final answer
Judge model calls
LLMRole.JUDGE_MODEL is for scoring, evaluation, moderation, ranking, or structured judgment tasks.
Default behavior:
- uses non-stream mode by default unless
stream=Trueis provided - is designed for stable final output, especially JSON scoring results
- usually pairs well with
enable_thinking=Falseandtemperature=0 - marks the call as
ok=Falsewitherror_type="EMPTY_ANSWER"when the provider returns reasoning but no final answer
JSON output helper
from llm_io_normalizer.normalizers import extract_json_object
obj = extract_json_object('```json\n{"result": 2}\n```')
assert obj == {"result": 2}
Examples
The examples/ directory contains runnable examples for:
- tested-model calls
- judge-model calls
- a tested-model → judge-model evaluation pipeline
Use environment variables for provider credentials and endpoints when running examples:
export LLM_BASE_URL="https://your-provider.example/v1"
export LLM_API_KEY="your-api-key"
export LLM_MODEL="your-model-name"
python examples/judge_model.py
Development
pip install -e ".[dev]"
ruff check .
pytest
python -m build
Release
Recommended PyPI release mode is Trusted Publishing from GitHub Actions.
Configure the PyPI project to trust this repository workflow, then publish a GitHub release tag such as v0.1.0.
Scope
This package is intentionally not a full API gateway. It does not implement authentication, rate limiting, billing, routing dashboards, or multi-tenant governance. Those can be handled by an outer gateway such as Kong, APISIX, Envoy, Portkey, or other infrastructure.
llm-io-normalizer focuses on the reusable Python SDK layer:
- Model I/O normalization
- reasoning / answer separation
- stream / non-stream fallback
- tested-model and judge-model call policies
- unified result/error contract
- simple JSON object extraction from model output
Project details
Release history Release notifications | RSS feed
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 llm_io_normalizer-0.1.0.tar.gz.
File metadata
- Download URL: llm_io_normalizer-0.1.0.tar.gz
- Upload date:
- Size: 11.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc710325c2af656f4c9027a9aa7dfbda916d9b65e0ccd408d771bd5ff531409f
|
|
| MD5 |
f29257870810d999cc300fbdba256900
|
|
| BLAKE2b-256 |
d61af4a88bd19f4f28dbb14f3240823ac4c9c0a7654a21bd551063e7a38e17cf
|
Provenance
The following attestation bundles were made for llm_io_normalizer-0.1.0.tar.gz:
Publisher:
publish.yml on wanghesong2019/llm-io-normalizer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_io_normalizer-0.1.0.tar.gz -
Subject digest:
dc710325c2af656f4c9027a9aa7dfbda916d9b65e0ccd408d771bd5ff531409f - Sigstore transparency entry: 1528878329
- Sigstore integration time:
-
Permalink:
wanghesong2019/llm-io-normalizer@007652d1240f36bd5cf79a132a81e3fa2d9750e4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/wanghesong2019
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@007652d1240f36bd5cf79a132a81e3fa2d9750e4 -
Trigger Event:
release
-
Statement type:
File details
Details for the file llm_io_normalizer-0.1.0-py3-none-any.whl.
File metadata
- Download URL: llm_io_normalizer-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.7 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 |
dff798bed2bacb7c4450f84bad4d3eff48059e662b0f7225c618485e26ea44b3
|
|
| MD5 |
035349cf44427da10027e1ecea1563e3
|
|
| BLAKE2b-256 |
0d0365ff8175dbcdcce504efdbdb63b797d212bbd33372fe9aad4ac51c0e786e
|
Provenance
The following attestation bundles were made for llm_io_normalizer-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on wanghesong2019/llm-io-normalizer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
llm_io_normalizer-0.1.0-py3-none-any.whl -
Subject digest:
dff798bed2bacb7c4450f84bad4d3eff48059e662b0f7225c618485e26ea44b3 - Sigstore transparency entry: 1528878388
- Sigstore integration time:
-
Permalink:
wanghesong2019/llm-io-normalizer@007652d1240f36bd5cf79a132a81e3fa2d9750e4 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/wanghesong2019
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@007652d1240f36bd5cf79a132a81e3fa2d9750e4 -
Trigger Event:
release
-
Statement type: