Skip to main content

Zero-dependency scrubbers for LLM chat output — strip leaked JSON state-op fences, backend scaffold blocks, and tool-preamble meta-narration from player-facing text

Project description

llm-scrub

Zero-dependency scrubbers for LLM chat output. Weak models leak backend scaffolding into the text shown to the user — JSON state-op fences, injected === … === context blocks, and English "tool preamble" meta-narration. llm-scrub strips that noise deterministically, using pure string/regex logic instead of relying on the model to obey a prompt.

  • No dependencies. Pure standard library (json, re).
  • Deterministic. No model call, no network — a plain str -> str transform.
  • Conservative. Tuned to under-strip rather than over-strip: legitimate fenced code blocks, JSON data, markdown tables, and === dividers survive.
  • Idempotent. scrub(scrub(x)) == scrub(x).

Install

pip install llm-scrub

Requires Python 3.10+.

Quickstart

from llm_scrub import scrub

raw = (
    "好,开始输出。\n\n"
    "=== 时间线检索锚点 ===\n"
    "当前时间:序章 · 站台\n\n"
    "林夕睁开眼睛,废墟在昏黄的天光下铺展开来。\n\n"
    '```json\n[{"op": "set", "path": "world.location", "value": "废墟"}]\n```\n'
    "Let me mark the anchors now."
)

print(scrub(raw))
# 林夕睁开眼睛,废墟在昏黄的天光下铺展开来。

scrub chains the three scrubbers in the canonical order. Call them individually when you only need one:

from llm_scrub import (
    strip_json_state_ops,
    strip_meta_tool_preamble,
    strip_leaked_scaffold,
)

strip_json_state_ops('战斗结束。\n```json\n{"op": "append", "path": "bag", "value": "刀"}\n```')
# '战斗结束。'

strip_meta_tool_preamble("他转身离去。\nLet me update the anchors now.")
# '他转身离去。'

strip_leaked_scaffold("她走进废墟。\n\n=== 相关原文片段 ===\n某段原文\n\n她捡起刀。")
# '她走进废墟。\n\n她捡起刀。'

API

Function Signature Removes
scrub scrub(text: str, *, extra_headers=None) -> str All three, chained in the canonical order.
strip_json_state_ops strip_json_state_ops(text: str) -> str JSON state-op fences — fenced (```json […] ), un-fenced bare [{"op":…}], and truncated/unclosed ops left by a cut-off response.
strip_meta_tool_preamble strip_meta_tool_preamble(text: str) -> str A leaked English "tool preamble" sentence at the tail (e.g. "Let me mark the anchors now.").
strip_leaked_scaffold strip_leaked_scaffold(text: str, *, extra_headers=None) -> str Whole === … === backend scaffold blocks (retrieval anchors, world-line summaries, etc.), plus a trailing "begin output" reasoning preamble.

LEAKED_SCAFFOLD_KEYS is the built-in tuple of scaffold header markers; pass your own via extra_headers to extend it.

What it strips

1. JSON state-op fences. Some pipelines let the model emit state changes as JSON. Those blocks must never reach the reader:

你推开门,走了进去。

```json
[{"op": "set", "path": "player.location", "value": "地窖"}]
```

你推开门,走了进去。 — including malformed and truncated (streamed-and-cut) variants. A fenced JSON block that is not ops (a plain object or array of data) is preserved, so legitimate code examples survive.

2. Leaked tool preamble. Before a native tool call, a model may blurt one line of meta-narration meant for itself:

…晨光无声流转。The scene has progressed naturally. Let me mark the anchors that have been satisfied through our gameplay.

Only a trailing, unquoted, predominantly-English segment matching a preamble phrase is removed. A character's English dialogue inside quotes is never touched.

3. Leaked scaffold blocks. Weak models sometimes dump the backend's invisible context straight into the prose:

=== 时间线检索锚点 ===
当前时间:序章 · 站台
待确认跳跃:无

These headers are fixed strings the backend injects; normal narrative never emits them verbatim, so the whole block is removed with zero collateral damage. Extend the header list for your own backend:

scrub(text, extra_headers=("我的自定义脚手架",))

Design: under-strip, never over-strip

Every scrubber is tuned to leave the text alone when in doubt:

  • Fenced/bare JSON is only stripped when it parses and carries state-op markers (op / path / question). Other JSON is preserved.
  • A trailing English segment is only stripped when it is outside quotes and matches a known tool-preamble phrase.
  • A === … === line is only stripped when its inner text matches a known scaffold header — a === 序章 === divider or a markdown setext underline is left intact.
  • If stripping would empty the text (a whole turn of pure leakage), the original is returned rather than blanking the message.

License

MIT — see LICENSE.

Acknowledgements

Extracted from the RPG Roleplay platform (https://github.com/felixchaos/rpg-roleplay-platform).

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

llm_scrub-0.1.0.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

llm_scrub-0.1.0-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file llm_scrub-0.1.0.tar.gz.

File metadata

  • Download URL: llm_scrub-0.1.0.tar.gz
  • Upload date:
  • Size: 16.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for llm_scrub-0.1.0.tar.gz
Algorithm Hash digest
SHA256 628103944b375534a68d8d6d7bb55b49433423b50e2e49f1a10366a70e65efa5
MD5 36fa884dda90d9bb5d832b8e785efdce
BLAKE2b-256 ca04d9aedc02ed157e26140babded8b57b052c4903fd31d0b6d55a91120d4c84

See more details on using hashes here.

Provenance

The following attestation bundles were made for llm_scrub-0.1.0.tar.gz:

Publisher: release.yml on felixchaos/llm-scrub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file llm_scrub-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: llm_scrub-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for llm_scrub-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1675d1c8cbd135574390861ba377a1d9962dd984acec2d43d58d97840c9bc03e
MD5 8d50bfc49c12be5d9a9e404033de81da
BLAKE2b-256 ef51ebc39c23d8c8d462422b3bc7e861df330462f06026119cd9ca784bde0322

See more details on using hashes here.

Provenance

The following attestation bundles were made for llm_scrub-0.1.0-py3-none-any.whl:

Publisher: release.yml on felixchaos/llm-scrub

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page