Make /ask free by default, Claude synthesis is opt-in

Search results (FTS5 + Ollama embeddings) are shown immediately at no
cost. AI synthesis via Claude is behind a "Synthesize" button that the
user must explicitly click. Results are cached permanently so repeat
visitors never trigger API calls.

- Split ask into search_only() (free) and ask() (paid, cached)
- GET /ask now uses search_only — no Claude tokens spent
- POST /api/ask/synthesize triggers Claude (Haiku, ~$0.001)
- Cached answers shown with "cached" badge, no re-generation
- Template shows sources immediately + optional synthesize button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 21:15:07 +01:00
parent e65d1cfacd
commit 34c36f81f1
4 changed files with 161 additions and 64 deletions

View File

@@ -1204,11 +1204,18 @@ def get_comparison_data(db: Database, names: list[str]) -> dict | None:
}
def get_ask_data(db: Database, question: str, top_k: int = 5, cheap: bool = True) -> dict:
"""Run hybrid search + Claude synthesis for a question.
def get_ask_search(db: Database, question: str, top_k: int = 5) -> dict:
"""Search-only (free) — returns sources + cached answer if available."""
from ietf_analyzer.config import Config
from ietf_analyzer.search import HybridSearch
Returns {answer: str, sources: [{name, title, similarity, excerpt}]}.
"""
config = Config.load()
searcher = HybridSearch(config, db)
return searcher.search_only(question, top_k=top_k)
def get_ask_synthesize(db: Database, question: str, top_k: int = 5, cheap: bool = True) -> dict:
"""Run Claude synthesis (costs tokens, result is cached permanently)."""
from ietf_analyzer.config import Config
from ietf_analyzer.search import HybridSearch