From 40f9dc48774f9e8377fcbe1ccf7fa1d17c57d0ea Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Fri, 23 Jan 2026 15:26:33 -0800 Subject: [PATCH] Fix output order and add Reddit error handling - SKILL.md: Move "What I learned" BEFORE "Research Complete" stats - Add error tracking to Report schema (reddit_error, x_error fields) - Wrap OpenAI API calls in try/catch with clear error messages - Show explicit error or "no results" messages in compact output - Fix false positive error detection for null error fields Co-Authored-By: Claude Opus 4.5 --- SKILL.md | 14 +++++--------- scripts/last30days.py | 34 +++++++++++++++++++++++++--------- scripts/lib/openai_reddit.py | 8 ++++++++ scripts/lib/render.py | 24 ++++++++++++++++++++++-- scripts/lib/schema.py | 10 +++++++++- 5 files changed, 69 insertions(+), 21 deletions(-) diff --git a/SKILL.md b/SKILL.md index 4dc855e..82d6618 100644 --- a/SKILL.md +++ b/SKILL.md @@ -108,20 +108,15 @@ Read the research output and become an **expert**. Identify: ## THEN: Show Summary + Invite Vision -Display in this EXACT order (so stats are visible at bottom of terminal): +**CRITICAL ORDER**: Display sections in this EXACT sequence (insights FIRST, stats LAST): ``` -**Key patterns discovered:** -1. [Pattern 1] - [one-line insight] -2. [Pattern 2] - [one-line insight] -3. [Pattern 3] - [one-line insight] -4. [Pattern 4] - [one-line insight] -5. [Pattern 5] - [one-line insight] +--- +What I learned: -I'm now an expert in {TOPIC}. +[2-4 sentences synthesizing the key insight from your research. What's the secret? What pattern emerged? What do experts do differently? Write this as a mini-expert briefing, not a list.] --- - 📊 Research Complete Analyzed {total_sources} sources from the last 30 days @@ -129,6 +124,7 @@ Analyzed {total_sources} sources from the last 30 days ├─ X: {n} posts │ {sum} likes │ {sum} reposts └─ Top voices: r/{sub1}, r/{sub2}, @{handle1}, @{handle2} +--- Share your vision for what you want to create and I'll write a thoughtful prompt you can copy-paste directly into {TARGET_TOOL}. ``` diff --git a/scripts/last30days.py b/scripts/last30days.py index bcd482d..59f2f96 100644 --- a/scripts/last30days.py +++ b/scripts/last30days.py @@ -29,6 +29,7 @@ from lib import ( dates, dedupe, env, + http, models, normalize, openai_reddit, @@ -62,25 +63,38 @@ def run_research( """Run the research pipeline. Returns: - Tuple of (reddit_items, x_items, raw_openai, raw_xai, raw_reddit_enriched) + Tuple of (reddit_items, x_items, raw_openai, raw_xai, raw_reddit_enriched, reddit_error, x_error) """ reddit_items = [] x_items = [] raw_openai = None raw_xai = None raw_reddit_enriched = [] + reddit_error = None + x_error = None # Reddit search via OpenAI if sources in ("both", "reddit"): if mock: raw_openai = load_fixture("openai_sample.json") else: - raw_openai = openai_reddit.search_reddit( - config["OPENAI_API_KEY"], - selected_models["openai"], - topic, - depth=depth, - ) + try: + raw_openai = openai_reddit.search_reddit( + config["OPENAI_API_KEY"], + selected_models["openai"], + topic, + depth=depth, + ) + except http.HTTPError as e: + print(f"[REDDIT ERROR] OpenAI API request failed: {e}", flush=True) + if e.body: + print(f"[REDDIT ERROR] Response body: {e.body[:500]}", flush=True) + raw_openai = {"error": str(e)} + reddit_error = f"API error: {e}" + except Exception as e: + print(f"[REDDIT ERROR] Unexpected error: {type(e).__name__}: {e}", flush=True) + raw_openai = {"error": str(e)} + reddit_error = f"{type(e).__name__}: {e}" # Parse response reddit_items = openai_reddit.parse_reddit_response(raw_openai) @@ -112,7 +126,7 @@ def run_research( # Parse response x_items = xai_x.parse_x_response(raw_xai) - return reddit_items, x_items, raw_openai, raw_xai, raw_reddit_enriched + return reddit_items, x_items, raw_openai, raw_xai, raw_reddit_enriched, reddit_error, x_error def main(): @@ -227,7 +241,7 @@ def main(): mode = "x-only" # Run research - reddit_items, x_items, raw_openai, raw_xai, raw_reddit_enriched = run_research( + reddit_items, x_items, raw_openai, raw_xai, raw_reddit_enriched, reddit_error, x_error = run_research( args.topic, sources, config, @@ -265,6 +279,8 @@ def main(): ) report.reddit = deduped_reddit report.x = deduped_x + report.reddit_error = reddit_error + report.x_error = x_error # Generate context snippet report.context_snippet_md = render.render_context_snippet(report) diff --git a/scripts/lib/openai_reddit.py b/scripts/lib/openai_reddit.py index a19db89..f07b027 100644 --- a/scripts/lib/openai_reddit.py +++ b/scripts/lib/openai_reddit.py @@ -101,6 +101,13 @@ def parse_reddit_response(response: Dict[str, Any]) -> List[Dict[str, Any]]: """ items = [] + # Check for API errors first + if "error" in response and response["error"]: + error = response["error"] + err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error) + print(f"[REDDIT ERROR] OpenAI API error: {err_msg}", flush=True) + return items + # Try to find the output text output_text = "" if "output" in response: @@ -131,6 +138,7 @@ def parse_reddit_response(response: Dict[str, Any]) -> List[Dict[str, Any]]: break if not output_text: + print(f"[REDDIT WARNING] No output text found in OpenAI response. Keys present: {list(response.keys())}", flush=True) return items # Extract JSON from the response diff --git a/scripts/lib/render.py b/scripts/lib/render.py index af59ccf..14c8149 100644 --- a/scripts/lib/render.py +++ b/scripts/lib/render.py @@ -46,7 +46,17 @@ def render_compact(report: schema.Report, limit: int = 15) -> str: lines.append("") # Reddit items - if report.reddit: + if report.reddit_error: + lines.append("### Reddit Threads") + lines.append("") + lines.append(f"**ERROR:** {report.reddit_error}") + lines.append("") + elif report.mode in ("both", "reddit-only") and not report.reddit: + lines.append("### Reddit Threads") + lines.append("") + lines.append("*No relevant Reddit threads found for this topic.*") + lines.append("") + elif report.reddit: lines.append("### Reddit Threads") lines.append("") for item in report.reddit[:limit]: @@ -78,7 +88,17 @@ def render_compact(report: schema.Report, limit: int = 15) -> str: lines.append("") # X items - if report.x: + if report.x_error: + lines.append("### X Posts") + lines.append("") + lines.append(f"**ERROR:** {report.x_error}") + lines.append("") + elif report.mode in ("both", "x-only") and not report.x: + lines.append("### X Posts") + lines.append("") + lines.append("*No relevant X posts found for this topic.*") + lines.append("") + elif report.x: lines.append("### X Posts") lines.append("") for item in report.x[:limit]: diff --git a/scripts/lib/schema.py b/scripts/lib/schema.py index 0069603..a09f2a5 100644 --- a/scripts/lib/schema.py +++ b/scripts/lib/schema.py @@ -153,9 +153,12 @@ class Report: best_practices: List[str] = field(default_factory=list) prompt_pack: List[str] = field(default_factory=list) context_snippet_md: str = "" + # Status tracking + reddit_error: Optional[str] = None + x_error: Optional[str] = None def to_dict(self) -> Dict[str, Any]: - return { + d = { 'topic': self.topic, 'range': { 'from': self.range_from, @@ -171,6 +174,11 @@ class Report: 'prompt_pack': self.prompt_pack, 'context_snippet_md': self.context_snippet_md, } + if self.reddit_error: + d['reddit_error'] = self.reddit_error + if self.x_error: + d['x_error'] = self.x_error + return d def create_report(