mihuman is a small, dependency-light Python library that rewrites AI-generated text so it reads like a human wrote it. Everything runs against a local Ollama server — no external API calls, no data leaves your box.
The library ships two things:
- A
humanize()function that streams a rewrite from Ollama and runs a deterministic post-processing pipeline over the output (em-dash stripping, AI-lexicon substitution, burstiness enforcement, optional error injection). - A
Configdataclass andload_config()helper so you can drive the whole thing from a TOML file instead of threading a dozen keyword arguments through your code.
Install
pip install mihuman
You also need Ollama running locally with at least one model pulled:
ollama pull mistral-nemo:12b # or llama3.1:8b, gemma2:9b, whatever you like
Quick start
from mihuman import humanize
result = humanize(
text="The utilization of innovative paradigms facilitates a robust framework...",
model="llama3.1:8b",
style="stealth",
intensity="aggressive",
)
print(result.text)
Driving it from a config file
Drop a mihuman.toml in your project (or at ~/.config/mihuman/config.toml):
model = "llama3.1:8b"
base_url = "http://localhost:11434"
style = "stealth"
intensity = "aggressive"
burstiness = true
error_level = 0
temperature = 0.85
top_p = 0.97
timeout = 300
# seed = 42
Then load it:
from mihuman import humanize, load_config
cfg = load_config() # auto-discovers
# cfg = load_config("path/to/mihuman.toml") # or pass an explicit path
result = humanize(text=my_text, **cfg.to_kwargs())
print(result.text)
Config search order (first match wins):
- Explicit path passed to
load_config(path=...) $MIHUMAN_CONFIGenv var./mihuman.toml(current working directory)$XDG_CONFIG_HOME/mihuman/config.toml(fallback:~/.config/mihuman/config.toml)
Unknown keys raise ConfigError so typos surface at load time, not months later.
An annotated starter config lives at examples/mihuman.toml.
Options
Styles
| Style | Notes |
|---|---|
stealth |
Anti-detection mode. Prioritizes perplexity and burstiness. |
humanize |
General natural writing. Clear, simple. |
casual |
Friendly, conversational. |
professional |
Executive-level direct prose. |
academic |
Q1-journal register. AWL vocabulary. |
creative |
Sensory, fresh comparisons. |
technical |
Precise but approachable; step-by-step. |
Intensity
| Level | Post-processing passes |
|---|---|
light |
preamble strip → em-dash removal → whitespace cleanup |
medium |
+ AI lexicon substitution → burstiness (optional) |
aggressive |
+ contraction forcing → sentence capitalization → burstiness |
Error injection
| Level | Effect |
|---|---|
0 (off) |
none |
1 (subtle) |
~35% chance to drop Oxford commas |
2 (natural) |
+ missed apostrophes (~1 per 180 words) |
3 (casual) |
+ light typos like "teh", "adn" (~1 per 500 words) |
Pipeline
Input text
│
▼
Smart chunking ~800-word, paragraph-aware chunks
│
▼
Ollama /api/chat streamed rewrite per chunk (system prompt = style overlay)
│
▼
Post-processing preamble strip → em-dash strip → AI-lexicon sub
→ contractions → capitalize → burstiness → whitespace
→ optional human-error injection
│
▼
Output
The LLM does the heavy rewriting; the post-processor cleans up AI-specific tells the model tends to leave behind.
Public API
from mihuman import (
humanize, HumanizeResult, OllamaError, list_models,
Config, load_config, ConfigError,
STYLES, DEFAULT_MODEL, DEFAULT_OLLAMA_URL,
postprocess, make_rng, build_system_prompt,
__version__,
)
humanize(text, *, style, model, intensity, base_url, temperature, top_p, timeout, burstiness, error_level, seed, writing_sample, on_progress, on_token, on_chunk_done) -> HumanizeResultlist_models(base_url=...) -> list[str]— enumerate installed Ollama modelspostprocess(text, intensity, rng, burstiness, error_level) -> str— run just the post-processor on already-rewritten text
Pass callbacks (on_token, on_chunk_done, on_progress) if you want to stream output into a UI.
Requirements
- Python 3.10+
- Ollama running locally (or somewhere reachable)
Runtime deps: none on Python 3.11+; tomli on 3.10 only.
License
MIT — see LICENSE.
Metadata
Release files for mihuman 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| mihuman-0.1.0.tar.gz | 17.6 kB | Without provenance | Details for mihuman-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| mihuman-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for mihuman-0.1.0-py3-none-any.whl |
Total release size: 37.2 kB
Release files / mihuman-0.1.0.tar.gz
| Download URL | mihuman-0.1.0.tar.gz |
|---|---|
| Size | 17.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d65d6eb986fb21685231802f7b8539afd4caf1c99743bed18fb722f05e6c2c0e
|
|
BLAKE2b-256 checksum How to use checksums |
de6c13c8f566183ead3f9b151ba177738f945da2db0a4ec65bec9fef96b6f92c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / mihuman-0.1.0-py3-none-any.whl
| Download URL | mihuman-0.1.0-py3-none-any.whl |
|---|---|
| Size | 19.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e30c88b24b0d154e60926d30536f77ac4fc3e3c2268bc8a98b059a5837a33030
|
|
BLAKE2b-256 checksum How to use checksums |
79521dce0a929d2ccf1a44c40a151aac55dca82f8b20cd263ef2fbe8316deadd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for mihuman 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.