Claude Claude Opus 4.8 commited on
Commit
ca80acf
·
unverified ·
1 Parent(s): 282bc7c

Surface LLM provider/model/last-error on /health for diagnostics

Browse files

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN4QRr6dTE2W7hQ2SDnLmY

Files changed (2) hide show
  1. backend/app/generate.py +17 -0
  2. backend/app/main.py +2 -2
backend/app/generate.py CHANGED
@@ -53,11 +53,25 @@ _SYSTEM = (
53
  )
54
 
55
 
 
 
 
 
56
  def llm_available() -> bool:
57
  """True when an LLM API key is configured."""
58
  return bool(os.getenv("LLM_API_KEY"))
59
 
60
 
 
 
 
 
 
 
 
 
 
 
61
  def generate_answer(question: str, passages: list[str]) -> str | None:
62
  """Return a grounded answer from the passages, or None to fall back.
63
 
@@ -87,8 +101,11 @@ def generate_answer(question: str, passages: list[str]) -> str | None:
87
  ],
88
  )
89
  except Exception as exc: # never let generation break /ask
 
 
90
  logger.warning("LLM generation failed, using extractive answer: %s", exc)
91
  return None
92
 
 
93
  text = (response.choices[0].message.content or "").strip()
94
  return text or None
 
53
  )
54
 
55
 
56
+ # Last generation error (for diagnostics via /health). Trimmed, no secrets.
57
+ last_error: str | None = None
58
+
59
+
60
  def llm_available() -> bool:
61
  """True when an LLM API key is configured."""
62
  return bool(os.getenv("LLM_API_KEY"))
63
 
64
 
65
+ def status() -> dict[str, object]:
66
+ """Diagnostic snapshot surfaced on /health."""
67
+ return {
68
+ "llm_enabled": llm_available(),
69
+ "llm_provider": _provider,
70
+ "llm_model": _MODEL,
71
+ "llm_error": last_error,
72
+ }
73
+
74
+
75
  def generate_answer(question: str, passages: list[str]) -> str | None:
76
  """Return a grounded answer from the passages, or None to fall back.
77
 
 
101
  ],
102
  )
103
  except Exception as exc: # never let generation break /ask
104
+ global last_error
105
+ last_error = f"{type(exc).__name__}: {exc}"[:400]
106
  logger.warning("LLM generation failed, using extractive answer: %s", exc)
107
  return None
108
 
109
+ last_error = None
110
  text = (response.choices[0].message.content or "").strip()
111
  return text or None
backend/app/main.py CHANGED
@@ -15,7 +15,7 @@ from fastapi import FastAPI, File, Form, HTTPException, UploadFile
15
  from fastapi.middleware.cors import CORSMiddleware
16
  from pydantic import BaseModel
17
 
18
- from . import __version__, db
19
  from .generate import generate_answer, llm_available
20
  from .parsing import DocumentError, chunk_text, extract_pdf_text
21
  from .retrieval import DocumentIndex, best_sentence
@@ -59,7 +59,7 @@ app.add_middleware(
59
  def health() -> dict[str, object]:
60
  """Liveness probe. Also reports the running version and whether LLM answer
61
  generation is configured, which makes deploys easy to verify."""
62
- return {"status": "ok", "version": __version__, "llm_enabled": llm_available()}
63
 
64
 
65
  def _looks_like_pdf(file: UploadFile) -> bool:
 
15
  from fastapi.middleware.cors import CORSMiddleware
16
  from pydantic import BaseModel
17
 
18
+ from . import __version__, db, generate
19
  from .generate import generate_answer, llm_available
20
  from .parsing import DocumentError, chunk_text, extract_pdf_text
21
  from .retrieval import DocumentIndex, best_sentence
 
59
  def health() -> dict[str, object]:
60
  """Liveness probe. Also reports the running version and whether LLM answer
61
  generation is configured, which makes deploys easy to verify."""
62
+ return {"status": "ok", "version": __version__, **generate.status()}
63
 
64
 
65
  def _looks_like_pdf(file: UploadFile) -> bool: