Two data-quality fixes surfaced by inspecting the first 94 claims:
- _clean_ticker: coerce the literal 'NULL'/'NONE'/'$AAPL' strings the LLM sometimes returns to
a real None, and collapse ASR space-mangling ('B VC' -> 'BVC').
- require_asset filter: drop claims with neither a ticker nor an asset_name -- generic trading
commentary ('buy the dip') from tutorial content is not an asset claim. On the backlog this
cut 94 -> 58 claims (36 generic), leaving clean per-asset signal.
Added the $2,000 Small Account Challenge recap playlist (the daily per-asset trade breakdowns) --
far higher claim density than the evergreen 'Anatomy of a Trade' tutorials. 7 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
2.3 KiB
Python
53 lines
2.3 KiB
Python
"""Pure-helper tests for the claim extractor — the anti-hallucination quote match + chunking.
|
|
The Ollama call itself is integration-tested live (not mocked here)."""
|
|
|
|
from youtube_claims.extraction import (
|
|
_chunks, _clean_ticker, _norm, _normalize, _quote_supported)
|
|
|
|
|
|
def test_norm_collapses_whitespace():
|
|
assert _norm(" A\nB c ") == "a b c"
|
|
|
|
|
|
def test_quote_supported_matches_across_segment_joins():
|
|
# the caption text is fragmented (joined with spaces); a quote spanning the join still matches
|
|
ntext = _norm("as it broke through 12\ni took 3000 off the table at 12 and 13 cents")
|
|
assert _quote_supported("as it broke through 12 I took 3000 off the table", ntext)
|
|
|
|
|
|
def test_quote_supported_rejects_hallucination():
|
|
ntext = _norm("the trader discussed a reverse split squeeze")
|
|
assert not _quote_supported("AAPL will hit 300 next week", ntext)
|
|
assert not _quote_supported("", ntext)
|
|
|
|
|
|
def test_normalize_drops_claim_without_verbatim_quote():
|
|
assert _normalize({"asset_ticker": "aapl", "verbatim_quote": ""}, 0, 6) is None
|
|
ok = _normalize({"asset_ticker": "aapl", "verbatim_quote": "aapl broke out"}, 0, 6)
|
|
assert ok and ok["asset_ticker"] == "AAPL" and ok["timestamp_start"] == 0
|
|
|
|
|
|
def test_clean_ticker_coerces_nullish_and_spacing():
|
|
assert _clean_ticker("NULL") is None and _clean_ticker("none") is None
|
|
assert _clean_ticker("$AAPL") == "AAPL"
|
|
assert _clean_ticker("B VC") == "BVC" # ASR space-mangling
|
|
assert _clean_ticker("") is None
|
|
|
|
|
|
def test_asset_specific_filter_drops_generic_commentary():
|
|
# a "NULL"-ticker generic tutorial line with no asset is dropped
|
|
generic = {"asset_ticker": "NULL", "asset_name": None, "verbatim_quote": "buy the dip"}
|
|
assert _normalize(generic, 0, 6) is None
|
|
# a real asset claim survives, ticker cleaned
|
|
real = {"asset_ticker": "null", "asset_name": "Apple", "verbatim_quote": "apple broke out"}
|
|
keep = _normalize(real, 0, 6)
|
|
assert keep and keep["asset_ticker"] is None and keep["asset_name"] == "Apple"
|
|
|
|
|
|
def test_chunks_group_segments_with_time_span():
|
|
segs = [{"start": float(i), "end": float(i + 1), "text": "x" * 100} for i in range(200)]
|
|
wins = list(_chunks(segs, max_chars=1000))
|
|
assert len(wins) > 1
|
|
buf, start, end = wins[0]
|
|
assert start == 0.0 and end is not None and len(buf) >= 1
|