Skip to content

mostlyright.finance.transcripts.role_parser

Transcript-anchored role-attribution parser.

Role attribution is settlement-sensitive: a turn mis-attributed from analyst to executive flips an earnings-mention settlement. Acoustic diarization is not used for the analyst/executive split; roles come from transcript text.

The transcript hands us roles for free:

  • the operator announces every analyst by name and firm (“Our first question comes from John DiFucci from Guggenheim”) — role_source="transcript_structural";
  • analysts self-identify on taking the mic — role_source="transcript_self_id";
  • executives are the named answerers from the prepared-remarks roster — role_source="transcript_structural" / transcript_self_id;
  • a mangled surname (DeFucci for DiFucci, Elnick for Zelnick) is resolved by fuzzy-matching against the published participant roster, gated on the cleanly-transcribed firm token, since firm names survive STT exactly — role_source="roster_match".

A turn that cannot be anchored to any of the three transcript-anchored sources gets role_source="diarization_advisory" (or "unknown"), never a Kalshi-countable source. That drives the fail-closed exclusion downstream in mostlyright.core.schemas.earnings_fact.validate_kalshi_counted_occurrence().

Fuzzy matching uses the stdlib difflib.get_close_matches() rather than a third-party fuzzy library: the surname-repair job is small and difflib’s ratio is sufficient, so no new dependency is needed. The match is gated on the firm token so a close surname at the wrong firm cannot be adopted.

FunctionDescription
fuzzy_match_surname(spoken, roster, …[, …])Repair a mangled analyst surname against the published roster.
parse_operator_announcements(text)Extract operator analyst hand-off announcements from text.
parse_self_identifications(text)Extract analyst self-identifications (“This is from ”).
ClassDescription
RoleParser([roster])Attribute transcript turns to speaker roles from text cues only.
RosterEntry(name, firm[, role])A published-participant roster row: canonical name, firm, and role.
Turn(speaker_name, speaker_role, role_source)A single attributed transcript turn.

class mostlyright.finance.transcripts.role_parser.RoleParser(roster=None)

Section titled “class mostlyright.finance.transcripts.role_parser.RoleParser(roster=None)”

Bases: object

Attribute transcript turns to speaker roles from text cues only.

Construct with the published participant roster ((name, firm) pairs or RosterEntry rows). attribute_turns walks a segmented transcript and assigns each turn a speaker_role + role_source, resolving mangled analyst surnames via fuzzy_match_surname() gated on the firm token. A turn with no structural cue and no roster hit is left role_source="diarization_advisory", never a Kalshi-countable source.

Attribute every turn in transcript to a role from text cues.

transcript is either the raw transcript text (parsed for operator announcements, self-identifications, and label lines) or a list of pre-segmented turn dicts ({"speaker_name": ..., "label": ..., "text": ...}). roster overrides the instance roster for this call.

Each returned Turn carries speaker_role + role_source. Un-anchorable turns get role_source="diarization_advisory".

class mostlyright.finance.transcripts.role_parser.RosterEntry(name, firm, role=‘unknown’)

Section titled “class mostlyright.finance.transcripts.role_parser.RosterEntry(name, firm, role=‘unknown’)”

Bases: object

A published-participant roster row: canonical name, firm, and role.

role is one of the SPEAKER_ROLE_VALUES (for example sell_side_analyst for an analyst, company_executive for a named executive). The firm token gates the fuzzy match: a surname is only repaired against entries sharing the cleanly-transcribed firm.

  • Parameters:

class mostlyright.finance.transcripts.role_parser.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)

Section titled “class mostlyright.finance.transcripts.role_parser.Turn(speaker_name, speaker_role, role_source, firm=None, text=”, confidence=1.0)”

Bases: object

A single attributed transcript turn.

speaker_role is a SPEAKER_ROLE_VALUES member; role_source is a ROLE_SOURCE_VALUES member. An un-anchorable turn carries role_source="diarization_advisory" (or "unknown"), never a Kalshi-countable source, so the downstream fail-closed filter excludes it from the Kalshi count.

  • Parameters:
    • speaker_name (str | None)
    • speaker_role (str)
    • role_source (str)
    • firm (str | None)
    • text (str)
    • confidence (float)

mostlyright.finance.transcripts.role_parser.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)

Section titled “mostlyright.finance.transcripts.role_parser.fuzzy_match_surname(spoken, roster, firm_token, , cutoff=0.72)”

Repair a mangled analyst surname against the published roster.

spoken is the (possibly mis-transcribed) surname as heard — "DeFucci" for "DiFucci", "Elnick" for "Zelnick". roster is the published participant list as (name, firm) pairs (or RosterEntry rows). firm_token is the cleanly-transcribed firm and gates the fuzzy match: only roster entries whose firm matches firm_token (exact, normalized) are candidates, so a close surname at the wrong firm can never be adopted and cause a cross-firm mis-attribution.

Returns the roster’s canonical surname on a confident match, else None. Uses the stdlib difflib.get_close_matches(): the surname-repair job is small, and difflib’s SequenceMatcher ratio comfortably resolves the one-char and phonetic drifts observed (DeFucci→DiFucci ratio ~0.86, Elnick→Zelnick ~0.77), so no third-party fuzzy dependency is needed.

mostlyright.finance.transcripts.role_parser.parse_operator_announcements(text)

Section titled “mostlyright.finance.transcripts.role_parser.parse_operator_announcements(text)”

Extract operator analyst hand-off announcements from text.

Returns one dict per announcement:

{"name": "John DiFucci", "firm": "Guggenheim",
"role_source": "transcript_structural"}

The operator names every analyst by name and firm; this is the most reliable transcript-anchored role cue. The extracted analyst is a sell_side_analyst, and the caller assigns the role. Firm is captured verbatim so it can gate a downstream roster fuzzy-match on a mangled surname.

mostlyright.finance.transcripts.role_parser.parse_self_identifications(text)

Section titled “mostlyright.finance.transcripts.role_parser.parse_self_identifications(text)”

Extract analyst self-identifications (“This is from ”).