| 1 | """U1 - deterministic name distiller and junk-shape classifier. |
| 2 | |
| 3 | topic_shape is the stdlib-only, pure-function foundation for discovery's |
| 4 | content-worthy topic naming: distill_topic_name turns a listing title (+ |
| 5 | optional snippet) into a short, ordered, searchable phrase, and is_junk_shape |
| 6 | flags help-me questions, beginner asks, and personal musings that should never |
| 7 | become topics. Titles below marked "real run" are verbatim from the motivating |
| 8 | 2026-07 discovery run. |
| 9 | """ |
| 10 | |
| 11 | from lib import topic_shape |
| 12 | |
| 13 | # Real run: long news headline whose subject entity ("Gemma 4") must survive. |
| 14 | GEMMA_TITLE = ( |
| 15 | "Google is updating Gemma 4's chat templates, bringing major fixes to " |
| 16 | 'tool calling and reducing "laziness", and enabling Flash Attention 4 ' |
| 17 | "on Hopper GPUs" |
| 18 | ) |
| 19 | |
| 20 | # Real run: anecdote framing that must not leak into the name. |
| 21 | COWORKER_TITLE = ( |
| 22 | "My coworker let an AI agent handle Slack replies while he was " |
| 23 | '"unavailable." It did not go well.' |
| 24 | ) |
| 25 | |
| 26 | |
| 27 | # ---------------------------------------------------------------- naming ---- |
| 28 | |
| 29 | |
| 30 | def test_distill_prefers_digit_bearing_entity_phrase(): |
| 31 | name = topic_shape.distill_topic_name(GEMMA_TITLE) |
| 32 | assert "gemma 4" in name.lower() |
| 33 | assert len(name.split()) <= 6 |
| 34 | # No sentence scaffolding: the verb-phrase framing must be gone. |
| 35 | assert "is updating" not in name.lower() |
| 36 | assert '"' not in name |
| 37 | |
| 38 | |
| 39 | def test_distill_drops_anecdote_framing(): |
| 40 | name = topic_shape.distill_topic_name(COWORKER_TITLE) |
| 41 | words = name.lower().split() |
| 42 | assert not name.lower().startswith("my coworker") |
| 43 | assert "slack" in words |
| 44 | assert "ai" in words or "agent" in words |
| 45 | assert len(words) <= 6 |
| 46 | |
| 47 | |
| 48 | def test_distill_short_title_passes_through(): |
| 49 | assert topic_shape.distill_topic_name("Agent swarms economics") == "Agent swarms economics" |
| 50 | |
| 51 | |
| 52 | def test_distill_show_hn_launch_title(): |
| 53 | name = topic_shape.distill_topic_name("Show HN: I built an open-source agent memory layer") |
| 54 | assert name == "open-source agent memory layer" |
| 55 | |
| 56 | |
| 57 | def test_distill_strips_interrogative_scaffolding(): |
| 58 | name = topic_shape.distill_topic_name("How do I get started with LangGraph?") |
| 59 | assert "how do i" not in name.lower() |
| 60 | assert "langgraph" in name.lower() |
| 61 | |
| 62 | |
| 63 | # ------------------------------------------------------------ junk: true ---- |
| 64 | |
| 65 | |
| 66 | def test_junk_beginner_help_ask(): |
| 67 | # Real run. |
| 68 | assert topic_shape.is_junk_shape("I need help starting to learn about AI AGENTS") |
| 69 | |
| 70 | |
| 71 | def test_junk_is_anyone_question(): |
| 72 | # Real run. |
| 73 | assert topic_shape.is_junk_shape( |
| 74 | "Is anyone actually orchestrating multi-agent workflows well, or are we all duct-taping?" |
| 75 | ) |
| 76 | |
| 77 | |
| 78 | def test_junk_everyone_musing(): |
| 79 | # Real run: musing shape despite capitalized entities. |
| 80 | assert topic_shape.is_junk_shape( |
| 81 | 'Everyone Is "Building AI Agents" - But Do We Mean the Same Thing?' |
| 82 | ) |
| 83 | |
| 84 | |
| 85 | def test_junk_leading_interrogative_asks(): |
| 86 | assert topic_shape.is_junk_shape("How do I get started with agent frameworks") |
| 87 | assert topic_shape.is_junk_shape("Does anyone use LangGraph in production?") |
| 88 | assert topic_shape.is_junk_shape("Can someone explain MCP servers to me") |
| 89 | assert topic_shape.is_junk_shape("where do I start with local models") |
| 90 | |
| 91 | |
| 92 | def test_junk_trailing_question_without_entity(): |
| 93 | assert topic_shape.is_junk_shape("so are we just letting agents run wild now?") |
| 94 | |
| 95 | |
| 96 | def test_junk_uses_snippet_only_when_title_has_no_entities(): |
| 97 | assert topic_shape.is_junk_shape( |
| 98 | "getting into local models", |
| 99 | "I'm a total beginner, where do I start?", |
| 100 | ) |
| 101 | # Entity-bearing title wins over a chatty snippet. |
| 102 | assert not topic_shape.is_junk_shape( |
| 103 | "Gemma 4 tool calling fixes", |
| 104 | "need help understanding the changelog", |
| 105 | ) |
| 106 | |
| 107 | |
| 108 | # ----------------------------------------------------------- junk: false ---- |
| 109 | |
| 110 | |
| 111 | def test_not_junk_news_shaped_statement(): |
| 112 | assert not topic_shape.is_junk_shape("Agent swarms and the new model economics") |
| 113 | assert not topic_shape.is_junk_shape(GEMMA_TITLE) |
| 114 | |
| 115 | |
| 116 | def test_not_junk_show_hn_launch(): |
| 117 | assert not topic_shape.is_junk_shape("Show HN: I built an open-source agent memory layer") |
| 118 | |
| 119 | |
| 120 | def test_not_junk_question_about_named_entity(): |
| 121 | assert not topic_shape.is_junk_shape("Is Gemma 4 actually good?") |
| 122 | |
| 123 | |
| 124 | def test_not_junk_explainer_headline_with_entity(): |
| 125 | assert not topic_shape.is_junk_shape("What Gemma 4 means for local inference") |
| 126 | assert not topic_shape.is_junk_shape("Why Gemma 4 flopped on Hopper GPUs") |
| 127 | |
| 128 | |
| 129 | # ------------------------------------------------------------ edge cases ---- |
| 130 | |
| 131 | |
| 132 | def test_both_functions_work_with_title_alone(): |
| 133 | assert topic_shape.distill_topic_name("Gemma 4 benchmarks") == "Gemma 4 benchmarks" |
| 134 | assert topic_shape.is_junk_shape("need help with my agent setup") is True |
| 135 | assert topic_shape.is_junk_shape("Gemma 4 benchmarks") is False |
| 136 | |
| 137 | |
| 138 | def test_distill_lowercase_no_entity_falls_back_to_truncated_title(): |
| 139 | title = "thoughts on where this is all going" |
| 140 | name = topic_shape.distill_topic_name(title) |
| 141 | assert name == "thoughts on where this is all" |
| 142 | assert topic_shape.is_junk_shape(title) is True |
| 143 | |
| 144 | |
| 145 | def test_cjk_title_no_crash_and_passes_through(): |
| 146 | title = "大模型智能体的未来发展方向" |
| 147 | assert topic_shape.distill_topic_name(title) == title |
| 148 | assert topic_shape.is_junk_shape(title) is False |
| 149 | |
| 150 | |
| 151 | def test_mixed_cjk_title_extracts_latin_entity(): |
| 152 | name = topic_shape.distill_topic_name("谷歌 更新 Gemma 4 聊天模板") |
| 153 | assert name == "Gemma 4" |
| 154 | |
| 155 | |
| 156 | def test_long_unspaced_cjk_title_capped_reasonably(): |
| 157 | title = "大模型" * 60 # single unspaced 180-char run |
| 158 | name = topic_shape.distill_topic_name(title) |
| 159 | assert name |
| 160 | assert len(name) <= 80 |
| 161 | |
| 162 | |
| 163 | def test_name_never_has_trailing_punct_or_quotes(): |
| 164 | cases = [ |
| 165 | 'Gemma 4 benchmarks are "insane"...', |
| 166 | "Agent swarms economics?!", |
| 167 | "What is the deal with agent frameworks???", |
| 168 | GEMMA_TITLE, |
| 169 | COWORKER_TITLE, |
| 170 | ] |
| 171 | for title in cases: |
| 172 | name = topic_shape.distill_topic_name(title) |
| 173 | assert name, title |
| 174 | assert name[-1] not in ".,;:!?\"'`- ", title |
| 175 | assert '"' not in name and "“" not in name and "”" not in name, title |
| 176 | |
| 177 | |
| 178 | def test_very_long_single_token_title_is_capped(): |
| 179 | name = topic_shape.distill_topic_name("a" * 300) |
| 180 | assert name |
| 181 | assert len(name) <= 80 |
| 182 | |
| 183 | |
| 184 | def test_url_only_title_is_safe_and_nonempty(): |
| 185 | name = topic_shape.distill_topic_name("https://news.example.com/2026/07/agent-memory-layer/") |
| 186 | assert name |
| 187 | assert len(name.split()) == 1 |
| 188 | assert name[-1] not in "/?.,;:!\"'" |
| 189 | |
| 190 | |
| 191 | def test_blank_inputs(): |
| 192 | # Sole exception to the never-empty contract: no word content anywhere. |
| 193 | assert topic_shape.distill_topic_name("") == "" |
| 194 | assert topic_shape.distill_topic_name(" ", " ") == "" |
| 195 | assert topic_shape.is_junk_shape("") is True |
| 196 | # A blank title falls back to the snippet. |
| 197 | assert topic_shape.distill_topic_name("", "Gemma 4 rollout chatter") == "Gemma 4 rollout chatter" |
| 198 |