ReviewKit is a domain-agnostic Python framework for document review. It is not a legal checker, grammar checker or one-shot document correction tool. Applications provide review profiles, LLM clients and optional context; ReviewKit validates the resulting findings/actions and applies only safe deterministic edits.
It reviews documents in layers (powered by the generic takt cascade engine):
sentence -> paragraph -> section -> document
The control flow (cascaded regulation, homeostats, entropy reduction via splot, vertical waves) is provided by takt. ReviewKit supplies the document plant, LLM detectors, and deterministic effectors.
It can produce three outputs:
reviewed.docxwith every correction, suggestion, question and risk marked for human review.corrected.docxas a clean fully corrected document with text edits applied and no review markers.- a JSON report with findings, actions, statuses, policy reasons and aggregate metrics.
- The LLM is replaceable.
- Profiles are TOML/Markdown folders and can define arbitrary review dimensions.
- Review is hierarchical, not one-shot.
- Findings describe observations; actions describe what might be done about them.
- The library validates and applies actions deterministically.
- Auto-apply is conservative by default and ambiguous edits become conflicts.
- DOCX and JSON reports are first-class outputs.
ReviewKit 0.14+ uses the pinned takt v0.3.1 in-process Mojo binding and re-uses the same Polish cybernetic terminology and structure as Fala and Splot (Marian Mazur, Józef Kossecki).
ReviewKit is the document host:
- builds the document plant and runs LLM detectors
- sends
plant_nodes+layers+raw_signalsover the JSON boundary - maps
actuation/interlock/stableback toReviewActionstatus
The pinned takt dependency is the only cascade engine. TaktClient calls its
cascade_step Python binding in-process and propagates import or execution failures; there
is no subprocess engine or local fallback. Takt compiles its native module on first use and
therefore requires stable Mojo 1.0.0 with the mojo executable on PATH — the same pin
as takt v0.3.1 and Fala (mojo == 1.0.0 on Modular's max channel).
The upstream Takt v0.3.1 manifest supports osx-arm64 only. Install that Mojo build from
Modular's stable Conda channel with Pixi. TAKT_HOME may point to a separate takt
v0.3.1 source checkout, but is normally unnecessary because the package includes the sources.
How ReviewKit maps onto the archetype:
ReviewDocumentPlant+DocNode— host plant over sentence/paragraph/section/document (post-order scan).RawSignal— LLM finding/action candidate → deviation + confidence (+ evidence on the host).LayerSpec— per-scope homeostat thresholds derived from profile action policy.TaktClient.evaluate— fusion + homeostat through the in-processtakt.cascade_stepbinding.- Actuation →
ReviewActionstatusAPPLIED(subject to post-policy). - SafetyInterlock →
NEEDS_HUMAN_DECISION/CONFLICT. - Post-processing (overlap demotion, protected patterns, tracked-revision safety) stays in ReviewKit.
Flow (one document):
ReviewDocumentPlantyields nodes in post-order (deepest first).- Scope-matched
BaseLLMDetectorproducesRawSignals from LLM responses. TaktClientdecides actuation vs interlock vs stable.ReviewEffectorturns the decision + stored LLM response intoReviewActions.- Deterministic post-pass preserves the public output contract.
Public models (ReviewFinding, ReviewAction, ReviewResult), profiles, review_document, and CLI are unchanged.
Requires Python >= 3.13, macOS on Apple silicon, and Mojo 1.0.0. uv sync
installs the Python packages only; the first review compiles and caches Takt's native module.
References (same as Fala / Splot):
Marian Mazur, Cybernetyczna teoria układów samodzielnych (1966), Jakościowa teoria informacji (1970). Józef Kossecki on multi-level autonomous systems (wielopoziomowe układy samodzielne). takt README / docs/FALA_INTEGRATION.md, splot docs/CONCEPTUAL_MODEL.md, Fala docs/CYBERNETIC_MAPPING.md.
# Supported platform: macOS on Apple silicon (osx-arm64)
curl -fsSL https://pixi.sh/install.sh | sh # skip if Pixi is already installed
pixi global install -c https://conda.modular.com/max -c conda-forge \
mojo==1.0.0
mojo --version
uv sync
uv run reviewkit input.docx \
--profile examples/profiles/story.teacher \
--out-reviewed reviewed.docx \
--out-corrected corrected.docx \
--out-report review-report.json \
--llm my_package.clients:make_client--out-report PATH writes the JSON report (the third first-class output). Omit it to skip
the report.
--llm module:factory names a zero-argument callable (dotted module:factory path) that
returns an LLMClient; ReviewKit imports it and calls it to build the client. The option is
required because ReviewKit does not assume a provider and will not silently run a mock review.
Production integrations pass their own LLMClient factory this way.
from reviewkit import review_document
from reviewkit.llm import MockLLMClient
result = review_document(
input_path="input.docx",
profile_path="examples/profiles/story.teacher",
llm=MockLLMClient(),
out_reviewed="reviewed.docx",
out_corrected="corrected.docx",
)
result.save_json("review-report.json")
for finding in result.findings:
print(finding.finding_id, finding.dimension, finding.title)
for action in result.actions:
print(action.action_id, action.status, action.policy_reason)from typing import Protocol
from pydantic import BaseModel
class LLMClient(Protocol):
def complete_json(
self,
messages: list[dict[str, str]],
schema: type[BaseModel],
) -> BaseModel:
...Profiles are folders, not Python objects:
profiles/employment-contract.lawyer/
profile.toml
instructions.md
required-clauses.md
risky-clauses.md
profile.toml defines role, language, document type, pipeline, dimensions and action policy.
Markdown files contain reviewer instructions that can be edited by domain experts.
Profiles are intentionally generic:
profile_id = "internal-policy-review"
name = "internal-policy-review"
display_name = "Internal policy review"
language = "en"
document_type = "policy memo"
reviewer_role = "compliance reviewer"
review_dimensions = ["clarity"]
review_instructions = """
Review only against the caller-provided criteria.
"""
[action_policy]
require_llm_apply_hint = true
min_confidence_for_auto_apply = 0.85
max_severity_for_auto_apply = "medium"
[action_policy.apply_policy]
typo = "apply"
[outputs]
reviewed_docx = true
corrected_docx = trueoutputs toggles which DOCX artifacts the pipeline renders. Both default to true; set
either to false to skip that render (for example a review-only profile that emits a
reviewed.docx but never a corrected.docx). The skipped artifact's path is None on the
ReviewResult and absent from its artifacts map; the JSON report (via
result.save_json(...)) is unaffected.
The default action policy does not silently rewrite text. A write action must be allowed by policy, carry enough confidence, have an explicit apply hint, pass protected-pattern checks and avoid sensitive-looking text changes unless the profile opts in. Stale locators and non-unique text matches are reported as conflicts instead of being applied.
ReviewFinding captures what the reviewer observed: dimension, severity, evidence and
rationale. ReviewAction captures a possible response: comment, replacement, insertion,
deletion, flag or domain-specific advisory action. Actions may reference findings with
finding_id, but the two are serialized separately.
ReviewResult.save_json(path) writes a report shaped for downstream systems:
findingsandactionsas separate arrays;actions_by_type,actions_by_status,findings_by_dimensionandfindings_by_severity;applied_actions,skipped_actions,conflictsandneeds_human_decision(the fail-closed escalation queue of actions handed to a human);- generated artifact paths and warnings.
reviewed.docx starts from the source DOCX and patches reviewed paragraphs in place:
- body, table, header and footer paragraphs keep the original document structure;
- text edits are written as native
w:ins/w:deltracked changes around the changed text; - review notes are anchored as Word comments on the reviewed fragment when possible.
corrected.docxapplies deterministic text-edit actions into a clean document.- Conflicts and hard safety-guard violations are not applied to
corrected.docx.
Either DOCX render can be turned off per profile via the outputs block (see
Review Profiles); a disabled artifact is skipped and its ReviewResult
path is None.
load_docx() exposes the document in its effective, post-change view: text inside
w:ins is present and text inside w:del is omitted from paragraph text. The original
revision evidence remains available through the typed ReviewDocument.revision_ledger:
from reviewkit import RevisionCoverageState, load_docx
document = load_docx("input.docx")
assert document.revision_ledger.coverage is RevisionCoverageState.COMPLETE
for entry in document.revision_ledger.entries:
print(entry.kind, entry.text, entry.locator, entry.revision_id, entry.author, entry.date)SourceRevision entries are domain-neutral and retain inserted/deleted text, stable
paragraph locators, offsets, and available Word revision metadata. Existing Word comments
remain in document.comments as DocxComment values, linked to the same locators; an
unresolved anchor is represented explicitly with locator=None. When the source package
has incomplete revision or comment coverage, both DOCX renderers raise
RevisionCoverageError before creating an output file. This prevents a partial review
artifact from being mistaken for a complete projection.
Revision coverage is measured from the text and control characters directly owned by each
supported revision identity (kind, id, author, and date). Empty property/paragraph
wrappers, empty formatting-only pPrChange / rPrChange snapshots, and nested or split
wrappers therefore do not create false incomplete coverage. A property change that owns
text, nested ins / del, or unexpected children stays fail-closed, as does every other
unsupported revision grammar.
Rendering a reviewed document preserves source revision wrappers, comment bodies, and thread sidecars, then adds new review markup with the reviewer identity and collision-free revision IDs. The source and generated records can therefore be inspected separately.
Besides in-place rendering, ReviewKit ships a standalone insertion engine
(reviewkit.insertions, reviewkit.anchors) for pipelines that add whole paragraphs —
fix clauses or [SUGGESTION: ...] markers — into an existing DOCX at body:p:<n> /
body:p:last anchors:
from reviewkit import ParagraphInserter, InsertionAction, InsertionValidator
inserter = ParagraphInserter(document, signature_keywords=("signed", "signature*"))
report = inserter.apply_actions([
InsertionAction(action_id="a1", anchor="body:p:4", text="New clause."),
InsertionAction(action_id="a2", anchor="body:p:last", text="Wording.",
kind="suggest", reason="Missing clause"),
])Placement is deterministic: all anchors resolve against the pristine document before any
mutation, actions sharing an anchor chain in batch order, a paragraph directly followed by
a table keeps its table (insertions land after it), and end-of-document insertions stay
above a trailing signature block. Signature detection and contextual body:p:last
re-anchoring are injectable (signature_keywords, resolve_last_anchor) — the engine has
no built-in language or domain assumptions. InsertionValidator re-checks a saved document:
each action's text is where its applied_anchor claims, nothing landed below the signature
block, and the body structure is intact.
ReviewKit keeps domain logic outside the core. Legal, editorial, educational or internal policy behavior belongs in profiles, context providers or adapters, not in the framework. Existing extension points include:
- per-document-type
action_policy/action_policiesin profile TOML, - policy reasons, source-system tags, evidence refs and references on
ReviewAction, - protected-pattern guards for corrected output safety,
ReviewContextProviderfor grounding, classifier results or external evidence,- an injectable
ActionPolicy(peer ofReviewContextProvider) passed toreview_document(..., action_policy=...), carrying programmaticPolicyGuardcallables —(action, node_text) -> reason | None— for fail-closed rules that regex config cannot express.
- ReviewKit does not decide whether an edit is legally, medically or contractually correct.
- ReviewKit does not require or bundle a single LLM provider.
- Corrected output is deterministic text editing, not a whole-document rewrite.
- Ambiguous edits, stale locators and sensitive-looking replacements are blocked for human review unless a profile explicitly relaxes the policy.
ReviewKit is released under the MIT License. See LICENSE.