pub struct DiscourseState { /* private fields */ }Expand description
Tracks discourse state across multiple render calls for natural output.
This is the engine’s internal memory — it knows what entities were recently mentioned, what templates were recently used, what connectives were recently inserted, and what words appeared in recent output.
Implementations§
Source§impl DiscourseState
impl DiscourseState
pub fn new() -> Self
Sourcepub fn set_focus_plural(&mut self, plural: bool)
pub fn set_focus_plural(&mut self, plural: bool)
Mark the current focus as a compound/plural subject so the next pronoun reference uses “they” rather than “it”.
Sourcepub fn focus_is_plural(&self) -> bool
pub fn focus_is_plural(&self) -> bool
Whether the current focus is a plural/compound subject.
Sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Clear ALL discourse state, including the cross-paragraph list-style
cycle counter. Use when starting a fully unrelated narrative — most
callers want Self::reset_for_paragraph instead so consecutive
paragraphs continue to rotate list-style phrasings.
Sourcepub fn reset_for_paragraph(&mut self)
pub fn reset_for_paragraph(&mut self)
Clear discourse state at a paragraph boundary while preserving the
narrative-level stylistic anti-repeat machinery. This is the reset
used by [Session::reset_for_paragraph] so multi-paragraph narratives
don’t restart variant cycles, list-style rotation, word-repetition
penalties, or sentence-rhythm memory on every paragraph break.
Preserved (narrative-level): last_list_style and
recent_list_styles (list-style cycle plus anti-repeat window),
template_history (variant anti-repeat), connective_history
(connective anti-repeat), word_history plus interner (repetition
scoring), sentence_length_history (cadence/rhythm scoring),
render_index (so word-history distances stay correct and
has_prior_render keeps reporting earlier discourse exists).
Cleared (paragraph-local): the entity table, focus entity and its
plurality, last_template_key/last_entity_name (so cross-paragraph
relation/connective inference is suppressed), the Centering Theory
Cb/Cf machinery (cb, previous_focus, current_cf,
previous_cf, last_transition), and per-render diagnostic signals.
The clearance set is the load-bearing invariant: anaphora must not resolve to entities introduced in an earlier paragraph, and rhetorical connectives (“Furthermore,”, “However,”) must not jump paragraph boundaries.
Sourcepub fn reset_list_cycle(&mut self)
pub fn reset_list_cycle(&mut self)
Clear only the list-style cycle counter and its anti-repeat window.
Mirrors [Session::reset_temporal] for callers that want to start a
fresh list-style rotation without otherwise resetting discourse state.
Sourcepub fn begin_render(&mut self)
pub fn begin_render(&mut self)
Advance to the next render. Must be called at the start of each render.
Sourcepub fn mention_entity(&mut self, name: &str, entity_type: &str)
pub fn mention_entity(&mut self, name: &str, entity_type: &str)
Record that an entity was mentioned in the current render at rank 0
(Subject position). Delegates to Self::mention_entity_ranked.
Resets the focus-plural flag — compound subjects must mark
themselves explicitly via Self::set_focus_plural.
Sourcepub fn mention_entity_ranked(&mut self, name: &str, entity_type: &str, rank: u8)
pub fn mention_entity_ranked(&mut self, name: &str, entity_type: &str, rank: u8)
Record that an entity was mentioned in the current render with an explicit grammatical-role rank. Lower rank = more prominent.
Rank convention:
- 0: Subject (most prominent — the Cp candidate)
- 1: Direct Object
- 2: Indirect Object / Location
- 3+: Oblique / other
The entity is inserted into current_cf in rank-ascending order.
If the entity is already in the Cf list, the lower of the two ranks
is kept (a subject mention always beats an object mention).
focus_entity is updated when rank == 0 or when no focus has been
set yet for this render; this keeps the Cp semantics: the Subject is
the preferred center.
Sourcepub fn reference_form_with_density(
&self,
name: &str,
density_low: bool,
density_high: bool,
) -> ReferenceForm
pub fn reference_form_with_density( &self, name: &str, density_low: bool, density_high: bool, ) -> ReferenceForm
Profile-aware variant of Self::reference_form.
PronounDensity::Default is identical to reference_form. Low
demotes any computed Pronoun to ShortName, biasing toward
formal register that keeps full names visible longer. High
promotes a ShortName to Pronoun when the entity is recent
enough (distance ≤ 2) and not in an ambiguity context — biasing
toward conversational register.
Sourcepub fn reference_form(&self, name: &str) -> ReferenceForm
pub fn reference_form(&self, name: &str) -> ReferenceForm
Determine how to refer to an entity given discourse history.
Sourcepub fn record_template_choice(&mut self, key: &str, variant_index: usize)
pub fn record_template_choice(&mut self, key: &str, variant_index: usize)
Record which template variant was selected for anti-repeat.
Sourcepub fn last_template_variant(&self, key: &str) -> Option<usize>
pub fn last_template_variant(&self, key: &str) -> Option<usize>
Get the last variant index used for a key (to avoid repeating it).
Sourcepub fn detect_relation(
&self,
current_key: &str,
current_entity: Option<&str>,
) -> DiscourseRelation
pub fn detect_relation( &self, current_key: &str, current_entity: Option<&str>, ) -> DiscourseRelation
Detect the relationship between the current render and the previous one.
Both entities must be present (and comparable) to assert a “same entity” or “different entity” relationship — otherwise the engine would incorrectly emit e.g. a Similarly, connective for a repeated entity-less template, where no entity comparison is actually meaningful.
Sourcepub fn select_connective(
&mut self,
relation: &DiscourseRelation,
) -> Option<&'static str>
pub fn select_connective( &mut self, relation: &DiscourseRelation, ) -> Option<&'static str>
Select a discourse connective for the given relation, preferring candidates absent from recent history. Three deterministic guardrails layer on top of the LRU pick:
- Connector-family budget. Each pool maps to a lexical family
(continuation, similarity, contrast). When the family already
contributes
pool.len()emissions inside the trailingFAMILY_WINDOW, returnNoneso the next sentence renders plain. This is the lever that breaks theSimilarly,/Likewise,/Similarly,/Likewise,pattern Matt flagged in service-shape prose: the two-element similarity pool is forced to alternate after two emissions, so the third call drops the connective entirely. - Exact-connector cooldown. The immediately preceding connective is excluded from candidacy when the pool offers an alternative — preserves the existing back-to-back anti-repeat.
- A/B alternation penalty. Candidates equal to
connective_history[len-2]take a score deduction so the LRU pick will not extend an A/B pattern into A/B/A when a fresh option exists. For three-element pools this preserves the A,B,C cycle; for two-element pools the family budget kicks in first and the penalty is moot.
Sourcepub fn select_connective_filtered(
&mut self,
relation: &DiscourseRelation,
allowed: Option<&[&str]>,
preferred: Option<&[(&str, f32)]>,
forbidden: Option<&[&str]>,
) -> Option<&'static str>
pub fn select_connective_filtered( &mut self, relation: &DiscourseRelation, allowed: Option<&[&str]>, preferred: Option<&[(&str, f32)]>, forbidden: Option<&[&str]>, ) -> Option<&'static str>
Profile-aware variant of Self::select_connective.
allowed (when Some) restricts the candidate pool to connectives
also present in the slice. If the resulting pool is empty (every
allowed entry was filtered by the existing anti-repeat or family
budget logic, OR no allowed entries match the base pool at all),
the engine falls back to the unfiltered base pool — profile
preferences are biases, never hard constraints.
preferred (when Some) adds a per-connective tie-breaker bonus
to the existing distance/alternation score. Weights are interpreted
in 0.0..=1.0 and scaled by 10 to land in the same rough magnitude
as the existing scoring terms.
forbidden (when Some) is a strict subtractive filter applied
after the allowed/fallback computation — used by the
retrospective refine pass for BlacklistConnective constraints.
Unlike allowed, an empty post-forbidden pool emits None
rather than falling back: that’s the whole point of a blacklist.
Sourcepub fn record_output_words(&mut self, output: &str)
pub fn record_output_words(&mut self, output: &str)
Record the words from a rendered output for repetition scoring.
Sourcepub fn sentence_length_iter(&self) -> impl Iterator<Item = usize> + '_
pub fn sentence_length_iter(&self) -> impl Iterator<Item = usize> + '_
Iterate over the recent sentence-length history (newest last). Each value is the word count of one emitted sentence inside the rhythm-tracking window. Exposed for profile-aware scorers that need to read the cadence buffer without snapshotting the whole session — the buffer is short and read-only from outside.
Sourcepub fn record_sentence_rhythm(&mut self, output: &str)
pub fn record_sentence_rhythm(&mut self, output: &str)
Record word counts for the sentences emitted by the committed render.
Sourcepub fn repetition_score(&self, candidate: &str) -> f64
pub fn repetition_score(&self, candidate: &str) -> f64
Score a candidate output for repetition against recent history. Lower score = less repetition = better.
Sourcepub fn sentence_rhythm_score(&self, candidate: &str) -> f64
pub fn sentence_rhythm_score(&self, candidate: &str) -> f64
Score a candidate output against recent sentence-length cadence. Lower is better: candidates with sentence lengths that were just emitted receive a penalty, while noticeably shorter or longer variants are preferred when repetition scores are otherwise close.
In addition to the per-sentence closeness/mean components, a bounded same-side penalty fires for each consecutive sentence pair (history → candidate, then candidate → candidate) that lands on the same side of the running mean. This nudges the selector toward burst-pivot cadence — alternating short/long around the mean — which is a hallmark of natural prose. The penalty is purely additive and capped per sentence so it cannot zero out repetition penalties or push the score negative.
Sourcepub fn word_frequency(&self, word: &str) -> f64
pub fn word_frequency(&self, word: &str) -> f64
Recency-weighted frequency of a specific word in recent output. Higher numbers mean the word has appeared recently and/or often. Used to pick the least-recently-used synonym from a registered group for elegant variation.
Sourcepub fn next_list_style(&mut self) -> ListStyle
pub fn next_list_style(&mut self) -> ListStyle
Select the next list style. Walks LIST_STYLES deterministically from
last_list_style forward and returns the first style that is not in
the recent-window (recent_list_styles). The walk advances past the
chosen slot so subsequent calls progress through the palette rather
than locking onto the first non-recent slot.
Anti-repeat is fully deterministic — no RNG dependency — and ensures
that an explicit forced style (e.g. {|join:bracketed} recorded via
Self::record_list_style_used) does not collide with the very next
auto-cycle pick. Falls back to the modulo slot if every style somehow
sits in the recent window (unreachable while
LIST_STYLE_RECENT_WINDOW < LIST_STYLES.len(), but kept defensive).
Sourcepub fn next_list_style_with_bias(
&mut self,
bias: Option<ListStyle>,
) -> ListStyle
pub fn next_list_style_with_bias( &mut self, bias: Option<ListStyle>, ) -> ListStyle
Profile-aware variant of Self::next_list_style.
When bias is Some(target) and target is not currently inside
the anti-repeat window, the cycle advances to the slot just past
target and emits it. When bias is None (i.e., the profile’s
ListStyleBias::Auto default), or when the bias target is in the
recent window, the natural cycle picks as in next_list_style.
The bias is a preference, not an override — anti-repeat always wins.
Sourcepub fn record_list_style_used(&mut self, style: ListStyle)
pub fn record_list_style_used(&mut self, style: ListStyle)
Record an explicit list style (e.g. {|join:bracketed}) for
diagnostics AND anti-repeat. Forced styles count toward the recent
window so a subsequent auto-cycle pick won’t immediately repeat the
forced phrasing.
Sourcepub fn last_list_style_used(&self) -> Option<ListStyle>
pub fn last_list_style_used(&self) -> Option<ListStyle>
List style applied by the most recent render’s |join pipe (if any).
Sourcepub fn set_cleanup_stripped_tail(&mut self, stripped: bool)
pub fn set_cleanup_stripped_tail(&mut self, stripped: bool)
Record whether Silent-mode cleanup stripped any trailing orphan words during the most recent render.
Sourcepub fn last_cleanup_stripped_tail(&self) -> bool
pub fn last_cleanup_stripped_tail(&self) -> bool
Whether the most recent render’s cleanup pass removed trailing
orphan words (Silent strictness only). false in other modes.
Sourcepub fn is_first_render(&self) -> bool
pub fn is_first_render(&self) -> bool
Whether this is the first render (no prior discourse context).
Sourcepub fn has_prior_render(&self) -> bool
pub fn has_prior_render(&self) -> bool
Whether a prior render happened in this discourse scope, used by
the {noun|demonstrative} pipe to decide between “this X” and
“the X”. Cleared by reset().
Sourcepub fn advance_cb(&mut self)
pub fn advance_cb(&mut self)
Advance Cb tracking for the next render. Call this after all mutations
from the current render (mention_entity, record_output_words) have
completed and the render has committed. On render failure the
Session snapshot/restore path will roll back cb and previous_focus
along with all other fields via Clone.
Called by Engine::render_tx at the end of each successful render.
Sourcepub fn last_transition(&self) -> Transition
pub fn last_transition(&self) -> Transition
The Centering Theory transition class from the most recent advance_cb call.
Returns Transition::NoCb before any render or after a reset.
Sourcepub fn cf(&self) -> &[Cf]
pub fn cf(&self) -> &[Cf]
The forward-looking centers being built during the current render, ordered by rank ascending (Cp = first element).
Sourcepub fn previous_cf(&self) -> &[Cf]
pub fn previous_cf(&self) -> &[Cf]
The forward-looking centers from the previous render.
Trait Implementations§
Source§impl Clone for DiscourseState
impl Clone for DiscourseState
Source§fn clone(&self) -> DiscourseState
fn clone(&self) -> DiscourseState
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more