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.
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 | |
|---|---|---|---|
| mihuman-0.1.0.tar.gz | 17.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mihuman-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
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
|