Add --debug flag for verbose logging
- Add LAST30DAYS_DEBUG env var / --debug flag - Log HTTP requests, responses, and errors - Show API error details when debug enabled - Helps diagnose API failures Usage: python3 last30days.py "topic" --debug Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,10 +11,12 @@ Options:
|
|||||||
--sources=MODE Source selection: auto|reddit|x|both (default: auto)
|
--sources=MODE Source selection: auto|reddit|x|both (default: auto)
|
||||||
--quick Faster research with fewer sources (8-12 each)
|
--quick Faster research with fewer sources (8-12 each)
|
||||||
--deep Comprehensive research with more sources (50-70 Reddit, 40-60 X)
|
--deep Comprehensive research with more sources (50-70 Reddit, 40-60 X)
|
||||||
|
--debug Enable verbose debug logging
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -190,9 +192,21 @@ def main():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Comprehensive research with more sources (50-70 Reddit, 40-60 X)",
|
help="Comprehensive research with more sources (50-70 Reddit, 40-60 X)",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--debug",
|
||||||
|
action="store_true",
|
||||||
|
help="Enable verbose debug logging",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Enable debug logging if requested
|
||||||
|
if args.debug:
|
||||||
|
os.environ["LAST30DAYS_DEBUG"] = "1"
|
||||||
|
# Re-import http to pick up debug flag
|
||||||
|
from lib import http as http_module
|
||||||
|
http_module.DEBUG = True
|
||||||
|
|
||||||
# Determine depth
|
# Determine depth
|
||||||
if args.quick and args.deep:
|
if args.quick and args.deep:
|
||||||
print("Error: Cannot use both --quick and --deep", file=sys.stderr)
|
print("Error: Cannot use both --quick and --deep", file=sys.stderr)
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
"""HTTP utilities for last30days skill (stdlib only)."""
|
"""HTTP utilities for last30days skill (stdlib only)."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@@ -8,6 +10,14 @@ from typing import Any, Dict, Optional
|
|||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
DEFAULT_TIMEOUT = 30
|
DEFAULT_TIMEOUT = 30
|
||||||
|
DEBUG = os.environ.get("LAST30DAYS_DEBUG", "").lower() in ("1", "true", "yes")
|
||||||
|
|
||||||
|
|
||||||
|
def log(msg: str):
|
||||||
|
"""Log debug message to stderr."""
|
||||||
|
if DEBUG:
|
||||||
|
sys.stderr.write(f"[DEBUG] {msg}\n")
|
||||||
|
sys.stderr.flush()
|
||||||
MAX_RETRIES = 3
|
MAX_RETRIES = 3
|
||||||
RETRY_DELAY = 1.0
|
RETRY_DELAY = 1.0
|
||||||
USER_AGENT = "last30days-skill/1.0 (Claude Code Skill)"
|
USER_AGENT = "last30days-skill/1.0 (Claude Code Skill)"
|
||||||
@@ -55,11 +65,16 @@ def request(
|
|||||||
|
|
||||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||||
|
|
||||||
|
log(f"{method} {url}")
|
||||||
|
if json_data:
|
||||||
|
log(f"Payload keys: {list(json_data.keys())}")
|
||||||
|
|
||||||
last_error = None
|
last_error = None
|
||||||
for attempt in range(retries):
|
for attempt in range(retries):
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(req, timeout=timeout) as response:
|
with urllib.request.urlopen(req, timeout=timeout) as response:
|
||||||
body = response.read().decode('utf-8')
|
body = response.read().decode('utf-8')
|
||||||
|
log(f"Response: {response.status} ({len(body)} bytes)")
|
||||||
return json.loads(body) if body else {}
|
return json.loads(body) if body else {}
|
||||||
except urllib.error.HTTPError as e:
|
except urllib.error.HTTPError as e:
|
||||||
body = None
|
body = None
|
||||||
@@ -67,6 +82,9 @@ def request(
|
|||||||
body = e.read().decode('utf-8')
|
body = e.read().decode('utf-8')
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
log(f"HTTP Error {e.code}: {e.reason}")
|
||||||
|
if body:
|
||||||
|
log(f"Error body: {body[:500]}")
|
||||||
last_error = HTTPError(f"HTTP {e.code}: {e.reason}", e.code, body)
|
last_error = HTTPError(f"HTTP {e.code}: {e.reason}", e.code, body)
|
||||||
|
|
||||||
# Don't retry client errors (4xx) except rate limits
|
# Don't retry client errors (4xx) except rate limits
|
||||||
@@ -76,10 +94,12 @@ def request(
|
|||||||
if attempt < retries - 1:
|
if attempt < retries - 1:
|
||||||
time.sleep(RETRY_DELAY * (attempt + 1))
|
time.sleep(RETRY_DELAY * (attempt + 1))
|
||||||
except urllib.error.URLError as e:
|
except urllib.error.URLError as e:
|
||||||
|
log(f"URL Error: {e.reason}")
|
||||||
last_error = HTTPError(f"URL Error: {e.reason}")
|
last_error = HTTPError(f"URL Error: {e.reason}")
|
||||||
if attempt < retries - 1:
|
if attempt < retries - 1:
|
||||||
time.sleep(RETRY_DELAY * (attempt + 1))
|
time.sleep(RETRY_DELAY * (attempt + 1))
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
|
log(f"JSON decode error: {e}")
|
||||||
last_error = HTTPError(f"Invalid JSON response: {e}")
|
last_error = HTTPError(f"Invalid JSON response: {e}")
|
||||||
raise last_error
|
raise last_error
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,17 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from . import http
|
from . import http
|
||||||
|
|
||||||
|
|
||||||
|
def _log_error(msg: str):
|
||||||
|
"""Log error to stderr."""
|
||||||
|
sys.stderr.write(f"[REDDIT ERROR] {msg}\n")
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses"
|
OPENAI_RESPONSES_URL = "https://api.openai.com/v1/responses"
|
||||||
|
|
||||||
# Depth configurations: (min, max) threads to request
|
# Depth configurations: (min, max) threads to request
|
||||||
@@ -120,7 +127,9 @@ def parse_reddit_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|||||||
if "error" in response and response["error"]:
|
if "error" in response and response["error"]:
|
||||||
error = response["error"]
|
error = response["error"]
|
||||||
err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(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)
|
_log_error(f"OpenAI API error: {err_msg}")
|
||||||
|
if http.DEBUG:
|
||||||
|
_log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
|
||||||
return items
|
return items
|
||||||
|
|
||||||
# Try to find the output text
|
# Try to find the output text
|
||||||
|
|||||||
+10
-1
@@ -2,10 +2,17 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from . import http
|
from . import http
|
||||||
|
|
||||||
|
|
||||||
|
def _log_error(msg: str):
|
||||||
|
"""Log error to stderr."""
|
||||||
|
sys.stderr.write(f"[X ERROR] {msg}\n")
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
# xAI uses chat completions endpoint
|
# xAI uses chat completions endpoint
|
||||||
XAI_CHAT_URL = "https://api.x.ai/v1/chat/completions"
|
XAI_CHAT_URL = "https://api.x.ai/v1/chat/completions"
|
||||||
|
|
||||||
@@ -129,7 +136,9 @@ def parse_x_response(response: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|||||||
if "error" in response and response["error"]:
|
if "error" in response and response["error"]:
|
||||||
error = response["error"]
|
error = response["error"]
|
||||||
err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
|
err_msg = error.get("message", str(error)) if isinstance(error, dict) else str(error)
|
||||||
print(f"[X ERROR] xAI API error: {err_msg}", flush=True)
|
_log_error(f"xAI API error: {err_msg}")
|
||||||
|
if http.DEBUG:
|
||||||
|
_log_error(f"Full error response: {json.dumps(response, indent=2)[:1000]}")
|
||||||
return items
|
return items
|
||||||
|
|
||||||
# Try to find the output text
|
# Try to find the output text
|
||||||
|
|||||||
Reference in New Issue
Block a user