f9a3c919bc
* feat(discover): add topic_shape name distiller and junk-shape classifier * feat(discover): stage-1 judge pass - short names, junk flags, worthiness-blended ranking * feat(discover): add angle and topic-queue fields to DiscoveryTopic, bump export schema to 1.1 * feat(discover): junk-shape floor gate counts seed sources; weak_signal prefers non-junk failures * feat(discover): stage-2 angle pass renders podcast and X-article hooks on trend cards * feat(discover): persistent topic queue - surfacing memory, covered tracking, queue CLI * feat(discover): SKILL.md trending trigger, relay contract for angle/pipeline lines, queue interactions * chore(discover): drop unused query import in rerank * refactor(discover): dedupe judge/angle plumbing, reuse shared normalizers, fix Pipeline line wording * fix(review): guard queue hook (P0), two-phase annotation, domain-preserving upsert, queue-list message, ordinal tests, SKILL.md queue fast path (#1 #4 #6 #8 #9) * fix(review): dict-payload guard, extract discovery_judge module, multi-token collision disambiguation (#2 #3 #5) * fix(review): covered status survives judge naming drift - fuzzy-matched covered priors born covered (#7) * docs(changelog): fill PR link for discovery content pipeline entries --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
198 lines
6.8 KiB
Python
198 lines
6.8 KiB
Python
"""U1 - deterministic name distiller and junk-shape classifier.
|
|
|
|
topic_shape is the stdlib-only, pure-function foundation for discovery's
|
|
content-worthy topic naming: distill_topic_name turns a listing title (+
|
|
optional snippet) into a short, ordered, searchable phrase, and is_junk_shape
|
|
flags help-me questions, beginner asks, and personal musings that should never
|
|
become topics. Titles below marked "real run" are verbatim from the motivating
|
|
2026-07 discovery run.
|
|
"""
|
|
|
|
from lib import topic_shape
|
|
|
|
# Real run: long news headline whose subject entity ("Gemma 4") must survive.
|
|
GEMMA_TITLE = (
|
|
"Google is updating Gemma 4's chat templates, bringing major fixes to "
|
|
'tool calling and reducing "laziness", and enabling Flash Attention 4 '
|
|
"on Hopper GPUs"
|
|
)
|
|
|
|
# Real run: anecdote framing that must not leak into the name.
|
|
COWORKER_TITLE = (
|
|
"My coworker let an AI agent handle Slack replies while he was "
|
|
'"unavailable." It did not go well.'
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------- naming ----
|
|
|
|
|
|
def test_distill_prefers_digit_bearing_entity_phrase():
|
|
name = topic_shape.distill_topic_name(GEMMA_TITLE)
|
|
assert "gemma 4" in name.lower()
|
|
assert len(name.split()) <= 6
|
|
# No sentence scaffolding: the verb-phrase framing must be gone.
|
|
assert "is updating" not in name.lower()
|
|
assert '"' not in name
|
|
|
|
|
|
def test_distill_drops_anecdote_framing():
|
|
name = topic_shape.distill_topic_name(COWORKER_TITLE)
|
|
words = name.lower().split()
|
|
assert not name.lower().startswith("my coworker")
|
|
assert "slack" in words
|
|
assert "ai" in words or "agent" in words
|
|
assert len(words) <= 6
|
|
|
|
|
|
def test_distill_short_title_passes_through():
|
|
assert topic_shape.distill_topic_name("Agent swarms economics") == "Agent swarms economics"
|
|
|
|
|
|
def test_distill_show_hn_launch_title():
|
|
name = topic_shape.distill_topic_name("Show HN: I built an open-source agent memory layer")
|
|
assert name == "open-source agent memory layer"
|
|
|
|
|
|
def test_distill_strips_interrogative_scaffolding():
|
|
name = topic_shape.distill_topic_name("How do I get started with LangGraph?")
|
|
assert "how do i" not in name.lower()
|
|
assert "langgraph" in name.lower()
|
|
|
|
|
|
# ------------------------------------------------------------ junk: true ----
|
|
|
|
|
|
def test_junk_beginner_help_ask():
|
|
# Real run.
|
|
assert topic_shape.is_junk_shape("I need help starting to learn about AI AGENTS")
|
|
|
|
|
|
def test_junk_is_anyone_question():
|
|
# Real run.
|
|
assert topic_shape.is_junk_shape(
|
|
"Is anyone actually orchestrating multi-agent workflows well, or are we all duct-taping?"
|
|
)
|
|
|
|
|
|
def test_junk_everyone_musing():
|
|
# Real run: musing shape despite capitalized entities.
|
|
assert topic_shape.is_junk_shape(
|
|
'Everyone Is "Building AI Agents" - But Do We Mean the Same Thing?'
|
|
)
|
|
|
|
|
|
def test_junk_leading_interrogative_asks():
|
|
assert topic_shape.is_junk_shape("How do I get started with agent frameworks")
|
|
assert topic_shape.is_junk_shape("Does anyone use LangGraph in production?")
|
|
assert topic_shape.is_junk_shape("Can someone explain MCP servers to me")
|
|
assert topic_shape.is_junk_shape("where do I start with local models")
|
|
|
|
|
|
def test_junk_trailing_question_without_entity():
|
|
assert topic_shape.is_junk_shape("so are we just letting agents run wild now?")
|
|
|
|
|
|
def test_junk_uses_snippet_only_when_title_has_no_entities():
|
|
assert topic_shape.is_junk_shape(
|
|
"getting into local models",
|
|
"I'm a total beginner, where do I start?",
|
|
)
|
|
# Entity-bearing title wins over a chatty snippet.
|
|
assert not topic_shape.is_junk_shape(
|
|
"Gemma 4 tool calling fixes",
|
|
"need help understanding the changelog",
|
|
)
|
|
|
|
|
|
# ----------------------------------------------------------- junk: false ----
|
|
|
|
|
|
def test_not_junk_news_shaped_statement():
|
|
assert not topic_shape.is_junk_shape("Agent swarms and the new model economics")
|
|
assert not topic_shape.is_junk_shape(GEMMA_TITLE)
|
|
|
|
|
|
def test_not_junk_show_hn_launch():
|
|
assert not topic_shape.is_junk_shape("Show HN: I built an open-source agent memory layer")
|
|
|
|
|
|
def test_not_junk_question_about_named_entity():
|
|
assert not topic_shape.is_junk_shape("Is Gemma 4 actually good?")
|
|
|
|
|
|
def test_not_junk_explainer_headline_with_entity():
|
|
assert not topic_shape.is_junk_shape("What Gemma 4 means for local inference")
|
|
assert not topic_shape.is_junk_shape("Why Gemma 4 flopped on Hopper GPUs")
|
|
|
|
|
|
# ------------------------------------------------------------ edge cases ----
|
|
|
|
|
|
def test_both_functions_work_with_title_alone():
|
|
assert topic_shape.distill_topic_name("Gemma 4 benchmarks") == "Gemma 4 benchmarks"
|
|
assert topic_shape.is_junk_shape("need help with my agent setup") is True
|
|
assert topic_shape.is_junk_shape("Gemma 4 benchmarks") is False
|
|
|
|
|
|
def test_distill_lowercase_no_entity_falls_back_to_truncated_title():
|
|
title = "thoughts on where this is all going"
|
|
name = topic_shape.distill_topic_name(title)
|
|
assert name == "thoughts on where this is all"
|
|
assert topic_shape.is_junk_shape(title) is True
|
|
|
|
|
|
def test_cjk_title_no_crash_and_passes_through():
|
|
title = "大模型智能体的未来发展方向"
|
|
assert topic_shape.distill_topic_name(title) == title
|
|
assert topic_shape.is_junk_shape(title) is False
|
|
|
|
|
|
def test_mixed_cjk_title_extracts_latin_entity():
|
|
name = topic_shape.distill_topic_name("谷歌 更新 Gemma 4 聊天模板")
|
|
assert name == "Gemma 4"
|
|
|
|
|
|
def test_long_unspaced_cjk_title_capped_reasonably():
|
|
title = "大模型" * 60 # single unspaced 180-char run
|
|
name = topic_shape.distill_topic_name(title)
|
|
assert name
|
|
assert len(name) <= 80
|
|
|
|
|
|
def test_name_never_has_trailing_punct_or_quotes():
|
|
cases = [
|
|
'Gemma 4 benchmarks are "insane"...',
|
|
"Agent swarms economics?!",
|
|
"What is the deal with agent frameworks???",
|
|
GEMMA_TITLE,
|
|
COWORKER_TITLE,
|
|
]
|
|
for title in cases:
|
|
name = topic_shape.distill_topic_name(title)
|
|
assert name, title
|
|
assert name[-1] not in ".,;:!?\"'`- ", title
|
|
assert '"' not in name and "“" not in name and "”" not in name, title
|
|
|
|
|
|
def test_very_long_single_token_title_is_capped():
|
|
name = topic_shape.distill_topic_name("a" * 300)
|
|
assert name
|
|
assert len(name) <= 80
|
|
|
|
|
|
def test_url_only_title_is_safe_and_nonempty():
|
|
name = topic_shape.distill_topic_name("https://news.example.com/2026/07/agent-memory-layer/")
|
|
assert name
|
|
assert len(name.split()) == 1
|
|
assert name[-1] not in "/?.,;:!\"'"
|
|
|
|
|
|
def test_blank_inputs():
|
|
# Sole exception to the never-empty contract: no word content anywhere.
|
|
assert topic_shape.distill_topic_name("") == ""
|
|
assert topic_shape.distill_topic_name(" ", " ") == ""
|
|
assert topic_shape.is_junk_shape("") is True
|
|
# A blank title falls back to the snippet.
|
|
assert topic_shape.distill_topic_name("", "Gemma 4 rollout chatter") == "Gemma 4 rollout chatter"
|