Skip to content

mostlyright.weather.earnings.stt

faster-whisper STT transcriber + alias-aware mention counter (Phase 27, 27-03).

Two surfaces (D-27.5):

SttTranscriber : Wraps faster-whisper’s WhisperModel (CTranslate2). large-v3 is the default (the hosted / our-infra source-of-truth model); small is the on-device earnings.live floor (perfect jargon counts on clean prepared remarks at ~13% WER, no torch dep — RESEARCH-MARKETS §3.1). transcribe threads a per-call initial_prompt through to the model (WhisperModel.transcribe(..., initial_prompt=...)) for vocabulary biasing. faster_whisper is LAZY-imported inside transcribe (guarded by the [earnings] extra) so importing this module + the unit tests (which mock WhisperModel) needs no heavy dep.

seed_initial_prompt / count_mentions : The term-seeding + alias/phonetic-aware fuzzy counter. seed_initial_prompt builds the per-call initial_prompt from the live market’s custom_strike target terms (exploding slash-synonyms). count_mentions counts a term’s mentions in a transcript, NEVER by exact string equality (RESEARCH-MARKETS §3.3): it explodes slash-synonyms + English plural/possessive forms (NOT verb-tense — the Kalshi rule, §2.1(3)) and matches the acronym plural/possessive (APIs/API's). Spelled-out separator matching (O-C-I / O.C.I / O C I) was DROPPED as unsafe — it false-positived on short acronyms and longer spelled sequences (A I inside A I R); acronym emission relies on the per-call initial_prompt biasing instead. Known ASR mis-renders (OCI ↔ OCR/OCIP) are OPT-IN via asr_misrenders=True — OFF by default so OCR (a legitimate standalone term) is not a false-positive over-count. It returns (mention_count, [matched_surface_form, ...]) — the integer is the primary tally (boolean is derived as count >= 1); the surface forms are the auditable spoken strings that feed schema.earnings_fact.v1.

This module does NOT settle a fact — role-anchoring + the fail-closed Kalshi filter is the 27-04 role parser’s job.

classify_mentions(transcript, term, *[, …])Classify EACH occurrence of term in transcript by compound type.
count_mentions(transcript, term, *[, …])Count term’s mentions in transcript (alias/phonetic-aware).
seed_initial_prompt(custom_strike_terms)Build a per-call faster-whisper initial_prompt from target terms.
SttTranscriber([model_size, device, …])faster-whisper (CTranslate2) transcriber (D-27.5).
TranscriptResult(text[, segments, language, …])The joined transcript text + the per-segment records + model info.

class mostlyright.weather.earnings.stt.SttTranscriber(model_size=‘large-v3’, , device=‘cpu’, compute_type=‘int8’)

Section titled “class mostlyright.weather.earnings.stt.SttTranscriber(model_size=‘large-v3’, , device=‘cpu’, compute_type=‘int8’)”

Bases: object

faster-whisper (CTranslate2) transcriber (D-27.5).

model_size selects the tier: large-v3 default (hosted / our-infra source-of-truth); small for the on-device earnings.live floor. device / compute_type mirror faster-whisper’s constructor (CPU int8 is the keyless/offline default). The WhisperModel is constructed lazily on first transcribe so importing this class needs no [earnings] extra.

  • Parameters:
    • model_size (str)
    • device (str)
    • compute_type (str)

transcribe(audio_path, , initial_prompt=None, target_terms=())

Section titled “transcribe(audio_path, , initial_prompt=None, target_terms=())”

Transcribe audio_path via faster-whisper.

Threads initial_prompt straight through to WhisperModel.transcribe(..., initial_prompt=...) for per-call vocabulary biasing. If initial_prompt is None but target_terms are given, the prompt is seeded from them via seed_initial_prompt(). Returns a TranscriptResult carrying the joined text (feeds count_mentions()) plus the per-segment records.

class mostlyright.weather.earnings.stt.TranscriptResult(text, segments=, language=None, duration=None)

Section titled “class mostlyright.weather.earnings.stt.TranscriptResult(text, segments=, language=None, duration=None)”

Bases: object

The joined transcript text + the per-segment records + model info.

mostlyright.weather.earnings.stt.classify_mentions(transcript, term, , match_rule=‘plural_possessive_ok_no_tense’, asr_misrenders=False)

Section titled “mostlyright.weather.earnings.stt.classify_mentions(transcript, term, , match_rule=‘plural_possessive_ok_no_tense’, asr_misrenders=False)”

Classify EACH occurrence of term in transcript by compound type.

A SIBLING of count_mentions() (which is unchanged): returns one record per occurrence, each {"surface", "start", "compound_type"} where compound_type is one of standalone / open / hyphenated / closed / affix_derivation (D-30 §2.1(3) cross-venue compound divergence). It reuses the SAME form expansion + apostrophe + plural/ possessive machinery as count_mentions() (_match_forms_for_term + _form_to_pattern) — never bare exact equality — so a possessive (tariff's) is a standalone occurrence, not a miss.

The word-boundary pass finds standalone / open / hyphenated occurrences exactly as count_mentions counts them (post-D-30 the hyphen guard is gone, so pre-tariff is a real occurrence tagged hyphenated). An ADDITIONAL substring pass finds closed candidates — a surface form FUSED inside a longer word component where the term stays a distinct part (wildfire, killjoy, and the wildfire component of wildfire-related — R3-F1: hyphenated tokens are split and each component scanned) — and separates true CLOSED compounds from affix_derivation roots (joyful, incl. inside hyphenated tokens: joyful-sounding) via a curated stdlib suffix heuristic (_closed_or_affix(); no dictionary dep — D-30 decision 4). The heuristic is CONSERVATIVE: an ambiguous case is a closed candidate that surfaces to a human reviewer, NEVER a silent drop.

Overlap handling matches count_mentions: longest-first, and a span is classified once (the word-boundary occurrences win; a closed substring pass skips any span already covered so pre-tariff is not double-counted as both hyphenated and closed).

Raises ValueError for an unrecognized match_rule or a degenerate term — IDENTICALLY to count_mentions() (review F2, shared _validated_forms_for_term() path): silently returning [] would settle “not mentioned” on a config bug.

mostlyright.weather.earnings.stt.count_mentions(transcript, term, , match_rule=‘plural_possessive_ok_no_tense’, asr_misrenders=False)

Section titled “mostlyright.weather.earnings.stt.count_mentions(transcript, term, , match_rule=‘plural_possessive_ok_no_tense’, asr_misrenders=False)”

Count term’s mentions in transcript (alias/phonetic-aware).

Returns (mention_count, matched_surface_forms). The integer is the PRIMARY tally that earnings-mention markets settle on; the boolean (“said

= 1”) is DERIVED by the caller as count >= 1. matched_surface_forms are the actually-spoken strings (verbatim, in order) so each occurrence is auditable against each venue’s stricter/looser rule (feeds schema.earnings_fact.v1.matched_surface_form).

Matching is NEVER bare exact string equality (RESEARCH-MARKETS §3.3): slash- synonyms, English plural/possessive forms (under plural_possessive_ok_no_tensecompany/companies/company's, data center/data centers), and acronym plural/possessives (APIs/API's) all count. HYPHENATED compounds count for the bare term on BOTH venues (pre-tariff / tariff-based / non-fat / pro-Palestine count for tariff / fat / Palestine — D-30; verbatim Kalshi + Polymarket PDFs cited in _form_to_pattern()). Verb-tense inflections (tariffed) do NOT. A CLOSED (unhyphenated) compound (wildfire for fire) does NOT count via count_mentions — the closed-compound cross-venue divergence is tagged per-occurrence by classify_mentions(). Spelled-out separator forms (O-C-I / O.C.I / O C I) are NOT matched — they were dropped as unsafe (false-positives on short acronyms / longer spelled sequences); acronym emission relies on initial_prompt biasing.

asr_misrenders (default OFF) opts in to the known STT mis-render aliases (OCR/OCIP for OCI). Leave it OFF unless the caller KNOWS the engine mis-rendered the target acronym — OCR is a legitimate standalone term, so aliasing it unconditionally over-counts.

Raises ValueError for an unrecognized match_rule (only plural_possessive_ok_no_tense and exact are accepted), an empty term, a term that expands to NO surface forms (a bare synonym separator " / "), or a term whose every surface form is word-character- free (a lone punctuation char "/" / "." / "-" / "&", which survives as its own form but can never match a word-boundary-anchored pattern). A typo’d rule must fail loud, NOT silently fall through to exact semantics and quietly change the settlement tally; a term that counts 0 for EVERY transcript (a market would settle “not mentioned” on a config bug) must fail loud too.

KNOWN LIMITATION (inherent surface ambiguity, not a bug): a noun whose regular plural is spelled identically to its present-tense verb (cost -> costs, guide -> guides, result -> results) will count a VERB-only usage ("it costs a lot") as a plural-noun mention. The no_tense rule excludes +ed / +ing verb inflections, but a plural-noun / present-tense homograph is indistinguishable without part-of-speech tagging (out of scope); the plural surface is accepted because the NOUN plural is the far more common, settlement-relevant usage in prepared remarks.

mostlyright.weather.earnings.stt.seed_initial_prompt(custom_strike_terms)

Section titled “mostlyright.weather.earnings.stt.seed_initial_prompt(custom_strike_terms)”

Build a per-call faster-whisper initial_prompt from target terms.

Vocabulary biasing (RESEARCH-MARKETS §2.2): seeding the live market’s custom_strike words into the prompt makes the model far likelier to emit the exact jargon token (mitigates the single-utterance rare-acronym miss class). Slash-synonyms are exploded so each alternative surface form is a distinct seeded token. Returns "" for an empty term list.

  • Return type: str
  • Parameters: custom_strike_terms (list [str ])