Skip to main content

zh-narrative-guard

Zero-dependency, deterministic consistency checkers for Chinese narrative text — the kind of self-consistency bugs an LLM produces while writing roleplay or fiction. Pure functions in, findings out. No LLM, no I/O, no state machine.

  • No dependencies. Pure standard library (re, dataclasses).
  • Deterministic. Same text always yields the same findings — safe to run in a hot path.
  • Advisory, not destructive. Every checker reports; it never rewrites your text.
  • Dormant when irrelevant. The weekday checker stays silent unless the text actually pins a concrete weekday, so calendar-free settings pay nothing.

The design philosophy: prefer a miss to a misfire (宁漏勿误)

Chinese single characters are hopelessly polysemous as signals:

Character Time meaning But also…
周 week 四周 = all around
后 after / later 后先用真气 = then first uses qi
次 time(s) 第二次 = the second attempt
天 day 天空 = the sky

Treating any single character as a time signal produces false positives, and these detectors were forged by fixing three successive production bug families that were all exactly that. So the rule throughout is: a missed issue is recoverable downstream, a false alarm directly harasses the writer. Every checker here errs toward silence.

Concretely, a time value is only accepted when it is a time-shaped token — a quantity + unit + after/before (三天后), an ordinal chapter/day (第3章 / 第二天), a year (公元1024年), a clock time (八点半), or a named time-of-day / season (清晨 / 傍晚 / 入冬). A lone polysemous character is never enough. Likewise a recall/flashback/fantasy framing (回想 / 梦见 / 想象) suppresses time-jump detection entirely — reminiscing is not jumping.

Install

pip install zh-narrative-guard

Requires Python 3.10+.

Quickstart

from zh_narrative_guard import (
    detect_time_directives,
    is_recall_framing,
    detect_weekday_violations,
    detect_cliche_violations,
)

# 1. Explicit "jump the timeline to <T>" directives.
d = detect_time_directives("时间跳到三天后")
assert d[0].target == "三天后"

# ...but action narration and flashbacks are NOT jumps:
assert detect_time_directives("进入后先用真气感知四周环境") == []
assert is_recall_framing("我继续回想:在进入主神空间前的日子")

# 2. Weekday arithmetic the model got wrong.
v = detect_weekday_violations("今天周日,明天周六")
assert v[0]["expected_name"] == "周一" and v[0]["claimed_name"] == "周六"
# Dormant with no concrete "today = 周X" anchor (fantasy / xianxia):
assert detect_weekday_violations("三天后我们在城门口集合") == []

# 3. Clichéd simile phrasing (surface it, do not strip).
assert detect_cliche_violations("他像投石入水般沉了下去")
# Literal usage and function words are never flagged:
assert detect_cliche_violations("他用投石机攻城") == []
assert detect_cliche_violations("如果投石入水就糟了") == []

API

timeline

Function Signature Description
detect_time_directives (text: str) -> list[TimeDirective] Explicit "jump to <T>" requests. Suppressed entirely if the text reads as a flashback. Each TimeDirective has .target and .raw.
is_recall_framing (text: str) -> bool Is the writer reminiscing / dreaming / imagining (a flashback), rather than advancing the clock?
looks_like_time_value (value: str) -> bool Does value name a time (a time-shaped token, no personal pronoun), rather than an action clause or a place?
clean_time_value (value: str) -> str Trim punctuation, strip leading prepositions and trailing action verbs from a captured value.
is_time_key (key: str) -> bool Does a state-key name denote a timeline-anchor field?

weekday

Function Signature Description
parse_today_weekday (text: str) -> int | None The concrete "今天 = 周X" anchor (0=Mon … 6=Sun), or None. Only 今天/今日/当天/本日 count; a relative day is never a base.
detect_weekday_violations (text: str, base_weekday: int | None = None) -> list[dict] Relative-day mentions (明天/后天/…) paired with an arithmetically wrong weekday. Pass base_weekday to supply an external "today"; otherwise the text's own "今天 = 周X" is the base. No anchor → [] (dormant).

Each weekday violation is {"rel", "match", "claimed", "expected", "claimed_name", "expected_name", "base_name"}.

cliche

Function Signature Description
detect_cliche_violations (text: str) -> list[dict] Clichéd "throwing a stone" (投石) simile phrasing. Matches the simile construction only — literal words (投石机 / 投石车 / 用投石砸) and function words (如果 / 例如) are never flagged.

Each cliché violation is {"pattern_label", "match", "position"}.

Behavior differences from the source platform

This library is extracted from a Chinese roleplay platform. Two deliberate changes were made for the general case:

  1. Dropped book-specific timeline tokens. The source _TIME_TOKEN also matched a novel's place-name timeline labels (柏林 / 图卢兹 / 基地). In ordinary prose those are pure false positives (柏林墙倒塌那年, 回到基地), so they are removed here.
  2. Fixed a simile false positive. The source simile marker 如同? also matched the lone 如 inside the common function words 如果 (if), 例如 (for example), 假如 (if), 如今 (nowadays), flagging non-similes such as "如果投石入水就糟了". The marker is now guarded (如同 plus a bare 如 with look-behind/look-ahead against those function words), while real similes — 像投石, 如同投石, literary bare 如投石问路 — still match.

Known limitations

Consistent with prefer a miss to a misfire: the weekday token accepts only ASCII 1-7, so full-width (周6) or Arabic-Indic (周٦) digits are silently missed rather than mis-checked (a miss is the safe direction). Chinese numerals (周三) and ASCII (周3) are fully supported.

License

MIT — see LICENSE.

Acknowledgements

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

Metadata

Release files for zh-narrative-guard 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zh-narrative-guard 0.1.0
File Size Uploaded Provenance Actions
zh_narrative_guard-0.1.0.tar.gz 18.1 kB With provenance Details for zh_narrative_guard-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for zh-narrative-guard 0.1.0
File Interpreter ABI Platform Actions
zh_narrative_guard-0.1.0-py3-none-any.whl Python 3 none any Details for zh_narrative_guard-0.1.0-py3-none-any.whl

Total release size: 32.2 kB

Release files / zh_narrative_guard-0.1.0.tar.gz

Download URL zh_narrative_guard-0.1.0.tar.gz
Size 18.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9d54dbdccad50d012f3c42895a48ede8d2bc39f12d11b338a804cf997e20c0af
BLAKE2b-256 checksum
How to use checksums
701fa7dcaacfd61ed2e1b5df5cdd7470ee9aa4a31d7cacaec81785f08dd50e4b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.

Transparency log

Release files / zh_narrative_guard-0.1.0-py3-none-any.whl

Download URL zh_narrative_guard-0.1.0-py3-none-any.whl
Size 14.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fa715af6348670214f73a762f34fffcd02264b31d02af0a3a1f0b3eb31bd75ad
BLAKE2b-256 checksum
How to use checksums
c4fa045e01765e7ec729e46f16ecedd777389c745f36e42a6096461171439ad8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.

Transparency log

Security for zh-narrative-guard 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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for zh_narrative_guard-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.

Transparency log

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.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page