fabricatio_diff.capabilities.hashline_edit

Capability for line-anchored, hashline-driven text editing.

Combines a set of programmatic wrappers over the Rust hashline primitives with an LLM-driven self-correcting loop that iteratively applies edits until a natural-language requirement is satisfied.

Exceptions

HashlineEditExhaustedError

Raised when hashline_diff cannot reach a satisfied state in time.

Classes

HashlineOp

A single hashline edit op parsed from an LLM response.

HashlineDiffResult

Outcome of a hashline_diff loop run.

HashlineEdit

Line-anchored editing via hashlines.

Module Contents

class fabricatio_diff.capabilities.hashline_edit.HashlineOp

A single hashline edit op parsed from an LLM response.

Mirrors the rho-hashline HashlineEdit shape 1:1. The four kinds map directly to the four Rust primitives exposed via fabricatio_diff.rust.

kind: Literal['set_line', 'replace_lines', 'insert_after', 'replace']

Op kind; selects which field is meaningful.

anchor: str | None = None

HASH` for set_line / insert_after.

Type:

`LINE

start_anchor: str | None = None

HASH` for the range start (replace_lines).

Type:

`LINE

end_anchor: str | None = None

HASH` for the range end (replace_lines).

Type:

`LINE

new_text: str | None = None

Replacement content for set_line / replace_lines / replace.

text: str | None = None

Inserted content for insert_after.

old_text: str | None = None

Match content for replace.

all: bool = False

If True, replace all occurrences (replace).

class fabricatio_diff.capabilities.hashline_edit.HashlineDiffResult

Outcome of a hashline_diff loop run.

content: str

Final source after all applied edits.

applied_edits: list[HashlineOp] = []

All ops applied across iterations, in order.

iterations: int = 0

Number of LLM calls actually made (parse-only iterations are not counted).

satisfied: bool = False

Whether the final judge verdict was YES.

history: list[str] = []

Per-iteration debug breadcrumbs (parse failures, judge verdicts, etc.).

exception fabricatio_diff.capabilities.hashline_edit.HashlineEditExhaustedError(message: str, *, iterations: int, last_source: str, last_error: str | None)

Bases: RuntimeError

Raised when hashline_diff cannot reach a satisfied state in time.

iterations

Number of LLM calls made before giving up.

last_source

Content after the last successful apply.

last_error

Error from the last failed apply, if any.

iterations
last_source
last_error
class fabricatio_diff.capabilities.hashline_edit.HashlineEdit(/, **data: Any)

Bases: fabricatio_core.capabilities.usages.UseLLM, abc.ABC

Line-anchored editing via hashlines.

Provides thin wrappers over the Rust hashline primitives for programmatic use, and a self-correcting LLM loop (hashline_diff) that iteratively applies edits until a natural-language requirement is satisfied.

async compute_line_hash(line: str) str

Compute the 2-char hex hash of a single line.

Whitespace is stripped before hashing, so “ foo “ and “foo” hash equal.

async format_hashes(content: str, start_line: int = 1) str

Format content as LINE:HASH|content per line, n-joined.

The HASH is the 2-char hex hash of the line (whitespace-stripped).

async parse_anchor(anchor: str) tuple[int, str]

Parse a LINE:HASH anchor into (line_number, hash).

Accepts display suffixes (5:a3|some content) and whitespace around the colon.

async set_line(content: str, anchor: str, new_text: str) str

Replace the line at anchor with new_text.

anchor is a LINE:HASH string. new_text may span multiple lines.

Raises:

RuntimeError – On HashlineError (InvalidAnchor, Mismatch, etc.).

async insert_after(content: str, anchor: str, text: str) str

Insert text after the line at anchor.

text must be non-empty (validated before the Rust call to surface the LLM’s likely mistake as a clearer error).

Raises:
async replace_lines(content: str, start_anchor: str, end_anchor: str, new_text: str) str

Replace the inclusive line range [start_anchor, end_anchor] with new_text.

Collapses to a single-line replace when start_anchor and end_anchor resolve to the same line.

Raises:

RuntimeError – On any HashlineError.

async replace(content: str, old_text: str, new_text: str, all: bool = False) str

Fuzzy text substitution (whitespace-insensitive).

If all is False (default), old_text must match exactly once or the call raises MultipleMatches. If all is True, all occurrences are replaced.

Raises:

RuntimeError – On TextNotFound or MultipleMatches.

async hashline_diff(source: str, requirement: str, *, max_iterations: int | None = None) HashlineDiffResult

Iteratively apply hashline edits until requirement is satisfied.

Each iteration:
  1. Render hashline_diff_template with {source, requirement, last_error}.

  2. Parse the LLM response into a list of `HashlineOp`s.

  3. Apply via Rust. On HashlineError (Mismatch / LineOutOfBounds / InvalidAnchor), capture the error message and re-prompt.

  4. Call ajudge with hashline_judge_template. If YES, return. If NO, loop.

Parameters:
  • source – The current text to edit.

  • requirement – Natural-language description of the target state.

  • max_iterations – Override for diff_config.hashline_diff_max_iterations.

Returns:

A HashlineDiffResult describing the final state.

Raises:

HashlineEditExhaustedError – When the loop cannot satisfy requirement within max_iterations iterations.