Summarize: LAN primary with local-Ollama rescue + in-app failure guidance

- Engine: when the primary summarizer is out of retries (or misconfigured),
  run_summarize now finishes the job on the configured rescue provider
  (SHONAR_LLM_FALLBACK_*), tags the summary with the provider that wrote it,
  and stores a human note in job.error; success clears stale notes.
- App (auto/lan): passes Ollama as the rescue provider when it is up.
- Detail screen: shows the rescue-swap note in plain words, a red
  'Summary failed' line with Settings -> Summarizer fix instructions and a
  Retry summary button on hard failure.
- Settings copy explains the fallback. 3 new pytest cases (14/14 pass);
  live E2E on 2026-09-18: sarcastic summary v6 via LAN on attempt 2.
This commit is contained in:
avi 2026-09-17 21:44:02 -05:00
commit 3061629dc5
6 changed files with 172 additions and 8 deletions

View file

@ -160,3 +160,32 @@ def get_llm_provider(settings: Settings) -> LlmProvider | None:
model=settings.llm_model,
)
raise ProviderConfigError(f"Unknown LLM provider: {settings.llm_provider!r}")
def get_llm_fallback_provider(settings: Settings) -> LlmProvider | None:
"""Rescue provider tried when the primary summarize fails mid-job.
None means "no fallback configured" — the primary's error stands.
Same kinds as get_llm_provider, built from the llm_fallback_* settings.
"""
kind = settings.llm_fallback_provider.strip().lower()
if kind in ("", "none"):
return None
if kind == "openai_compat":
from shonar.services.ai.openai_compat import OpenAICompatProvider
return OpenAICompatProvider(
base_url=settings.llm_fallback_base_url,
model=settings.llm_fallback_model,
api_key=settings.llm_fallback_api_key,
)
if kind == "ollama":
from shonar.services.ai.ollama import OllamaProvider
return OllamaProvider(
base_url=settings.llm_fallback_base_url,
model=settings.llm_fallback_model,
)
raise ProviderConfigError(
f"Unknown LLM fallback provider: {settings.llm_fallback_provider!r}"
)