From 7adf1bbcadeb73f8781e0ae1bd7de098bb23f322 Mon Sep 17 00:00:00 2001 From: tcpsyn Date: Fri, 6 Feb 2026 01:56:09 -0700 Subject: [PATCH] Fix LLM model list, Castopod API, and server runner - Remove gpt-4o-realtime (WebSocket-only) from OpenRouter models - Increase OpenRouter timeout to 60s and max_tokens to 150 - Handle empty LLM responses - Fix publish_episode.py for current Castopod API fields - Add port conflict check and graceful shutdown to run.sh Co-Authored-By: Claude Opus 4.6 --- backend/services/llm.py | 14 ++++++++----- publish_episode.py | 26 ++++++++++++++++-------- run.sh | 45 +++++++++++++++++++++++++++++++++-------- 3 files changed, 64 insertions(+), 21 deletions(-) diff --git a/backend/services/llm.py b/backend/services/llm.py index d6c108b..8982ee3 100644 --- a/backend/services/llm.py +++ b/backend/services/llm.py @@ -7,10 +7,10 @@ from ..config import settings # Available OpenRouter models OPENROUTER_MODELS = [ - "anthropic/claude-3-haiku", - "anthropic/claude-3.5-sonnet", "openai/gpt-4o-mini", "openai/gpt-4o", + "anthropic/claude-3-haiku", + "anthropic/claude-3.5-sonnet", "google/gemini-flash-1.5", "google/gemini-pro-1.5", "meta-llama/llama-3.1-8b-instruct", @@ -114,7 +114,7 @@ class LLMService: """Call OpenRouter API with retry""" for attempt in range(2): # Try twice try: - async with httpx.AsyncClient(timeout=30.0) as client: + async with httpx.AsyncClient(timeout=60.0) as client: response = await client.post( "https://openrouter.ai/api/v1/chat/completions", headers={ @@ -124,12 +124,16 @@ class LLMService: json={ "model": self.openrouter_model, "messages": messages, - "max_tokens": 100, + "max_tokens": 150, }, ) response.raise_for_status() data = response.json() - return data["choices"][0]["message"]["content"] + content = data["choices"][0]["message"]["content"] + if not content or not content.strip(): + print(f"OpenRouter returned empty response") + return "" + return content except (httpx.TimeoutException, httpx.ReadTimeout): print(f"OpenRouter timeout (attempt {attempt + 1})") if attempt == 0: diff --git a/publish_episode.py b/publish_episode.py index 4dae7b3..8c8236a 100755 --- a/publish_episode.py +++ b/publish_episode.py @@ -155,11 +155,12 @@ Respond with ONLY valid JSON, no markdown or explanation.""" return metadata -def create_episode(audio_path: str, metadata: dict, duration: int) -> dict: +def create_episode(audio_path: str, metadata: dict, episode_number: int) -> dict: """Create episode on Castopod.""" print("[3/5] Creating episode on Castopod...") headers = get_auth_header() + slug = re.sub(r'[^a-z0-9]+', '-', metadata["title"].lower()).strip('-') # Upload audio and create episode with open(audio_path, "rb") as f: @@ -168,21 +169,25 @@ def create_episode(audio_path: str, metadata: dict, duration: int) -> dict: } data = { "title": metadata["title"], - "description_markdown": metadata["description"], + "slug": slug, + "description": metadata["description"], "parental_advisory": "explicit", "type": "full", - "created_by": "1" + "podcast_id": str(PODCAST_ID), + "created_by": "1", + "updated_by": "1", + "episode_number": str(episode_number), } response = requests.post( - f"{CASTOPOD_URL}/api/rest/v1/podcasts/{PODCAST_ID}/episodes", + f"{CASTOPOD_URL}/api/rest/v1/episodes", headers=headers, files=files, data=data ) if response.status_code not in (200, 201): - print(f"Error creating episode: {response.text}") + print(f"Error creating episode: {response.status_code} {response.text}") sys.exit(1) episode = response.json() @@ -312,7 +317,7 @@ def get_next_episode_number() -> int: headers = get_auth_header() response = requests.get( - f"{CASTOPOD_URL}/api/rest/v1/podcasts/{PODCAST_ID}/episodes", + f"{CASTOPOD_URL}/api/rest/v1/episodes", headers=headers ) @@ -323,7 +328,12 @@ def get_next_episode_number() -> int: if not episodes: return 1 - max_num = max(ep.get("number", 0) for ep in episodes) + # Filter to our podcast + our_episodes = [ep for ep in episodes if ep.get("podcast_id") == PODCAST_ID] + if not our_episodes: + return 1 + + max_num = max(ep.get("number", 0) or 0 for ep in our_episodes) return max_num + 1 @@ -373,7 +383,7 @@ def main(): return # Step 3: Create episode - episode = create_episode(str(audio_path), metadata, transcript["duration"]) + episode = create_episode(str(audio_path), metadata, episode_number) # Step 4: Publish episode = publish_episode(episode["id"]) diff --git a/run.sh b/run.sh index b45ac7f..7f68da0 100755 --- a/run.sh +++ b/run.sh @@ -1,6 +1,7 @@ #!/bin/bash # AI Radio Show - Server Runner with restart support +PORT=8000 LOG_FILE="/tmp/ai-radio-show.log" RESTART_FLAG="/tmp/ai-radio-show.restart" STOP_FLAG="/tmp/ai-radio-show.stop" @@ -13,16 +14,46 @@ source venv/bin/activate # Cleanup old flags rm -f "$RESTART_FLAG" "$STOP_FLAG" +# Check if port is already in use +if lsof -i ":$PORT" -sTCP:LISTEN -t >/dev/null 2>&1; then + EXISTING_PID=$(lsof -i ":$PORT" -sTCP:LISTEN -t 2>/dev/null | head -1) + echo "ERROR: Port $PORT is already in use by PID $EXISTING_PID" + echo "Run: kill $EXISTING_PID" + exit 1 +fi + +kill_server() { + local pid=$1 + if ! kill -0 "$pid" 2>/dev/null; then + return + fi + kill "$pid" 2>/dev/null + # Wait up to 5 seconds for graceful shutdown + for i in $(seq 1 10); do + if ! kill -0 "$pid" 2>/dev/null; then + return + fi + sleep 0.5 + done + # Force kill if still alive + echo "[$(date)] Server didn't stop gracefully, force killing..." | tee -a "$LOG_FILE" + kill -9 "$pid" 2>/dev/null + wait "$pid" 2>/dev/null +} + echo "AI Radio Show Server Runner" echo "Log file: $LOG_FILE" echo "Press Ctrl+C to stop" echo "" -while true; do - echo "[$(date)] Starting server..." | tee -a "$LOG_FILE" +# Handle Ctrl+C +trap 'echo ""; echo "[$(date)] Interrupted" | tee -a "$LOG_FILE"; kill_server $SERVER_PID; exit 0' INT TERM - # Start uvicorn with output to both console and log file - python -m uvicorn backend.main:app --host 0.0.0.0 --port 8000 2>&1 | tee -a "$LOG_FILE" & +while true; do + echo "[$(date)] Starting server on port $PORT..." | tee -a "$LOG_FILE" + + # Start uvicorn directly (not through tee pipe so we get the real PID) + python -m uvicorn backend.main:app --host 0.0.0.0 --port $PORT >> "$LOG_FILE" 2>&1 & SERVER_PID=$! # Wait for server to exit or restart signal @@ -30,8 +61,7 @@ while true; do if [ -f "$RESTART_FLAG" ]; then echo "[$(date)] Restart requested..." | tee -a "$LOG_FILE" rm -f "$RESTART_FLAG" - kill $SERVER_PID 2>/dev/null - wait $SERVER_PID 2>/dev/null + kill_server $SERVER_PID sleep 1 break fi @@ -39,8 +69,7 @@ while true; do if [ -f "$STOP_FLAG" ]; then echo "[$(date)] Stop requested..." | tee -a "$LOG_FILE" rm -f "$STOP_FLAG" - kill $SERVER_PID 2>/dev/null - wait $SERVER_PID 2>/dev/null + kill_server $SERVER_PID echo "[$(date)] Server stopped." | tee -a "$LOG_FILE" exit 0 fi