fix: Critical bugs - LLM client, server imports, error handling

Team 1: Critical Bug Fix Squad

Fixed:
- #1: LLM Client async methods use undefined self.client
  Changed to self._async_client in utils/llm.py
- #3: Server Upload endpoint missing UploadFile/File imports
  Added to server.py imports
- #17: LangGraph workflow error recovery
  - Replaced fake fallback with proper error raising
  - Enabled MemorySaver checkpointing for state persistence
  - Added traceback printing for debugging
This commit is contained in:
2026-03-13 18:14:11 +00:00
parent b584e42d65
commit 452c3daec1
5 changed files with 390 additions and 11 deletions
+21 -8
View File
@@ -198,7 +198,7 @@ class OpusGraph:
workflow.add_edge("write_chapters", "complete")
workflow.add_edge("complete", END)
checkpointer = None # Disable for simpler debugging
checkpointer = MemorySaver() # Enable for state persistence
return workflow.compile(checkpointer=checkpointer)
# ============== NODES (Return DICT, not mutated state) ==============
@@ -635,19 +635,32 @@ Write ~{plan.word_count_target} words.
print(f"[STREAM] Reconstructed state from dict")
except Exception as e:
print(f"[RUN] Stream error: {e}")
import traceback
traceback.print_exc()
# Don't give up - try to recover partial state
# SAFETY FALLBACK: Pull from checkpoint/snapshot
# Enable checkpointing for recovery
print("[RUN] Checking final state...")
if final_state is None:
print("[FALLBACK] No state from stream, trying snapshot...")
final_state = initial_state
print("[WARNING] No state from stream, attempting recovery...")
# Try to recover from any partial state that was accumulated
# In a full implementation, we'd load from checkpoint here
# For now, raise a clear error instead of silently failing
raise RuntimeError(
f"Workflow failed to complete. "
f"Last known stage: {getattr(final_state, 'stage', 'unknown') if final_state else 'initial'}. "
f"Error: {e}"
)
# Verify we have manuscript
if not final_state.manuscript:
print("[FALLBACK] No manuscript in state!")
# Last resort: return what we have
else:
print(f"[RESULT] SUCCESS! {len(final_state.chapters)} chapters, {final_state.total_word_count} words")
print("[WARNING] No manuscript generated!")
# Return partial state for debugging
if final_state.prewriting.one_sentence:
print(f"[PARTIAL] Generated: {final_state.prewriting.one_sentence[:100]}...")
raise RuntimeError("Workflow completed but no manuscript was generated.")
print(f"[RESULT] SUCCESS! {len(final_state.chapters)} chapters, {final_state.total_word_count} words")
return final_state
+1 -1
View File
@@ -7,7 +7,7 @@ import os
from typing import Any, Optional
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, BackgroundTasks
from fastapi import FastAPI, HTTPException, BackgroundTasks, UploadFile, File
from fastapi.responses import JSONResponse, RedirectResponse
from pydantic import BaseModel, Field
from dotenv import load_dotenv
+2 -2
View File
@@ -113,7 +113,7 @@ class LLMClient:
if max_tokens:
payload["max_tokens"] = max_tokens
response = await self.client.post(
response = await self._async_client.post(
f"{self.base_url}/text/chatcompletion_v2",
headers=headers,
json=payload,
@@ -157,7 +157,7 @@ class LLMClient:
if max_tokens:
payload["max_tokens"] = max_tokens
response = await self.client.post(
response = await self._async_client.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload,