From ad7094e8a5ba51d604291cd909d264cc66296767 Mon Sep 17 00:00:00 2001 From: PeninsulaInd Date: Tue, 17 Mar 2026 10:51:10 -0500 Subject: [PATCH] Separate PR Topic (angle) from Keyword (anchor text) in press release pipeline PR Topic now only drives headline angle and awareness/news framing. Anchor text is derived from Company Name + Keyword (ClickUp Keyword field). Co-Authored-By: Claude Opus 4.6 (1M context) --- cheddahbot/tools/press_release.py | 37 +++++++++++++------------------ config.yaml | 1 + tests/test_press_advantage.py | 8 +++---- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/cheddahbot/tools/press_release.py b/cheddahbot/tools/press_release.py index 8c4f4f9..76f214a 100644 --- a/cheddahbot/tools/press_release.py +++ b/cheddahbot/tools/press_release.py @@ -364,16 +364,14 @@ def _build_judge_prompt(headlines: str, headlines_ref: str, topic: str = "") -> return prompt -def _derive_anchor_phrase(company_name: str, topic: str) -> str: - """Derive a 'brand + keyword' anchor phrase from company name and topic. +def _derive_anchor_phrase(company_name: str, keyword: str) -> str: + """Derive a 'brand + keyword' anchor phrase from company name and keyword. Examples: ("Advanced Industrial", "PEEK machining") -> "Advanced Industrial PEEK machining" ("Metal Craft", "custom metal fabrication") -> "Metal Craft custom metal fabrication" """ - # Clean up topic: strip leading articles, lowercase - keyword = topic.strip() - return f"{company_name} {keyword}" + return f"{company_name} {keyword.strip()}" def _find_anchor_in_text(text: str, anchor: str) -> bool: @@ -546,6 +544,7 @@ def write_press_releases( topic: str, company_name: str, url: str = "", + keyword: str = "", lsi_terms: str = "", required_phrase: str = "", ctx: dict | None = None, @@ -668,7 +667,7 @@ def write_press_releases( # ── Step 3: Write 2 press releases (execution brain x 2) ───────────── log.info("[PR Pipeline] Step 3/4: Writing 2 press releases...") - anchor_phrase = _derive_anchor_phrase(company_name, topic) + anchor_phrase = _derive_anchor_phrase(company_name, keyword) if keyword else "" pr_texts: list[str] = [] pr_files: list[str] = [] docx_files: list[str] = [] @@ -708,11 +707,11 @@ def write_press_releases( if wc < 575 or wc > 800: log.warning("PR %d word count %d outside 575-800 range", i + 1, wc) - # Validate anchor phrase - if _find_anchor_in_text(clean_result, anchor_phrase): + # Validate anchor phrase (only when keyword provided) + if anchor_phrase and _find_anchor_in_text(clean_result, anchor_phrase): log.info("PR %d contains anchor phrase '%s'", i + 1, anchor_phrase) - else: - fuzzy = _fuzzy_find_anchor(clean_result, company_name, topic) + elif anchor_phrase: + fuzzy = _fuzzy_find_anchor(clean_result, company_name, keyword) if fuzzy: log.info("PR %d: exact anchor not found, fuzzy match: '%s'", i + 1, fuzzy) anchor_warnings.append( @@ -1078,7 +1077,7 @@ def _resolve_branded_url(branded_url: str, company_data: dict | None) -> str: def _build_links( pr_text: str, company_name: str, - topic: str, + keyword: str, target_url: str, branded_url_resolved: str, ) -> tuple[list[dict], list[str]]: @@ -1091,13 +1090,13 @@ def _build_links( warnings: list[str] = [] # Link 1: brand+keyword → target_url - if target_url: - anchor_phrase = _derive_anchor_phrase(company_name, topic) + if target_url and keyword: + anchor_phrase = _derive_anchor_phrase(company_name, keyword) if _find_anchor_in_text(pr_text, anchor_phrase): links.append({"url": target_url, "anchor": anchor_phrase}) else: # Try fuzzy match - fuzzy = _fuzzy_find_anchor(pr_text, company_name, topic) + fuzzy = _fuzzy_find_anchor(pr_text, company_name, keyword) if fuzzy: links.append({"url": target_url, "anchor": fuzzy}) warnings.append( @@ -1141,6 +1140,7 @@ def submit_press_release( company_name: str, target_url: str = "", branded_url: str = "", + keyword: str = "", topic: str = "", pr_text: str = "", file_path: str = "", @@ -1178,13 +1178,6 @@ def submit_press_release( f"Press Advantage requires at least 550 words. Please expand the content." ) - # --- Derive topic from headline if not provided --- - if not topic: - topic = headline - for part in [company_name, "Inc.", "LLC", "Corp.", "Ltd.", "Limited", "Inc"]: - topic = topic.replace(part, "").strip() - topic = re.sub(r"\s+", " ", topic).strip(" -\u2013\u2014,") - # --- Load company data --- companies_text = _load_file_if_exists(_COMPANIES_FILE) company_all = _parse_company_data(companies_text) @@ -1227,7 +1220,7 @@ def submit_press_release( link_list, link_warnings = _build_links( pr_text, company_name, - topic, + keyword, target_url, branded_url_resolved, ) diff --git a/config.yaml b/config.yaml index b9f2f64..e401e5f 100644 --- a/config.yaml +++ b/config.yaml @@ -58,6 +58,7 @@ clickup: required_fields: [topic, company_name, target_url] field_mapping: topic: "PR Topic" + keyword: "Keyword" company_name: "Client" target_url: "IMSURL" branded_url: "SocialURL" diff --git a/tests/test_press_advantage.py b/tests/test_press_advantage.py index e9701cf..56605f1 100644 --- a/tests/test_press_advantage.py +++ b/tests/test_press_advantage.py @@ -552,7 +552,7 @@ class TestSubmitPressRelease: headline="Advanced Industrial Expands PEEK Machining", company_name="Advanced Industrial", pr_text=REALISTIC_PR_TEXT, - topic="PEEK machining", + keyword="PEEK machining", target_url="https://advancedindustrial.com/peek", ctx=submit_ctx, ) @@ -575,7 +575,7 @@ class TestSubmitPressRelease: headline="Advanced Industrial Expands PEEK Machining", company_name="Advanced Industrial", pr_text=REALISTIC_PR_TEXT, - topic="PEEK machining", + keyword="PEEK machining", branded_url="https://linkedin.com/company/advanced-industrial", ctx=submit_ctx, ) @@ -598,7 +598,7 @@ class TestSubmitPressRelease: headline="Advanced Industrial Expands PEEK Machining", company_name="Advanced Industrial", pr_text=REALISTIC_PR_TEXT, - topic="PEEK machining", + keyword="PEEK machining", branded_url="GBP", ctx=submit_ctx, ) @@ -694,7 +694,7 @@ class TestSubmitPressRelease: headline="Advanced Industrial Expands PEEK Machining", company_name="Advanced Industrial", pr_text=LONG_PR_TEXT, - topic="PEEK machining", + keyword="PEEK machining", target_url="https://example.com/peek", ctx=submit_ctx, )