feat: multi-backend LLM layer with per-request choice on case page
Replace the single [llm] block in settings.toml with a curated catalog of LLM "backends" (provider + model + sampling params + system prompt) in server/src/analyze/backend.rs. Two backends ship initially: gpt_oss_120b (default, reasoning_effort=medium) and llama_3_1_405b (uses response_format: json_schema to sidestep the <|eot_id|> leak). The case page now renders one submit button per available backend; the clicked button's name=backend value rides through AnalysisInput.backend_id to the worker, which looks it up via find_backend() with default fallback. A submitted unknown backend id is rejected as 400. .analysis_failed.json records the failed backend and the failure banner surfaces its label. The API key now comes from IONOS_API_KEY (env), not settings.toml; the [llm] section is read into a discarded field so old configs still load. Backends without a satisfied requires_api_key are filtered from the UI (any_backend_available()). Three end-to-end tests that mocked the LLM endpoint via settings.llm.url are #[ignore]'d with a clear note — they need per-test backend injection (Arc<Vec<LlmBackend>> through the worker) before they can be re-enabled.
This commit is contained in:
@@ -438,10 +438,13 @@
|
||||
<form method="post" action="/web/cases/{{ case_id }}/analyze">
|
||||
{% call csrf::field(csrf_token) %}
|
||||
<input type="hidden" name="return_to" value="/web/cases/{{ case_id }}">
|
||||
<button type="submit">Analysieren</button>
|
||||
<span class="analyze-prompt">Analysieren mit:</span>
|
||||
{% for b in backends %}
|
||||
<button type="submit" name="backend" value="{{ b.id }}">{{ b.label }}</button>
|
||||
{% endfor %}
|
||||
</form>
|
||||
{% else if llm_missing && !has_document %}
|
||||
<span class="llm-missing">LLM-Analyse nicht konfiguriert</span>
|
||||
<span class="llm-missing">Kein LLM-Backend verfügbar — IONOS_API_KEY setzen.</span>
|
||||
{% endif %} {% if is_admin %}
|
||||
<form
|
||||
class="admin-only"
|
||||
@@ -471,31 +474,15 @@
|
||||
<form method="post" action="/web/cases/{{ case_id }}/analyze">
|
||||
{% call csrf::field(csrf_token) %}
|
||||
<input type="hidden" name="return_to" value="/web/cases/{{ case_id }}">
|
||||
<span class="analyze-prompt">Neu analysieren mit:</span>
|
||||
{% for b in backends %}
|
||||
<button
|
||||
type="submit"
|
||||
class="copy-btn"
|
||||
title="Neu analysieren"
|
||||
aria-label="Neu analysieren"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="18"
|
||||
height="18"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<polyline points="23 4 23 10 17 10"></polyline>
|
||||
<polyline points="1 20 1 14 7 14"></polyline>
|
||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10"></path>
|
||||
<path
|
||||
d="M20.49 15a9 9 0 0 1-14.85 3.36L1 14"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
name="backend"
|
||||
value="{{ b.id }}"
|
||||
title="Neu analysieren mit {{ b.label }}"
|
||||
>{{ b.label }}</button>
|
||||
{% endfor %}
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -587,7 +574,9 @@
|
||||
</script>
|
||||
{% when None %} {% match analysis_failed %} {% when Some with (failure) %}
|
||||
<div class="failure-banner" role="alert">
|
||||
<strong>Analyse fehlgeschlagen.</strong>
|
||||
<strong>
|
||||
Analyse{% if !failure.backend_label.is_empty() %} mit {{ failure.backend_label }}{% endif %} fehlgeschlagen.
|
||||
</strong>
|
||||
Bitte erneut versuchen.
|
||||
<span class="failure-time"
|
||||
>Zuletzt versucht:
|
||||
@@ -609,7 +598,11 @@
|
||||
name="return_to"
|
||||
value="/web/cases/{{ case_id }}"
|
||||
/>
|
||||
<button type="submit">Erneut analysieren</button>
|
||||
{% for b in backends %}
|
||||
<button type="submit" name="backend" value="{{ b.id }}">
|
||||
Erneut mit {{ b.label }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</form>
|
||||
</div>
|
||||
{% when None %} {% if analyzing %}
|
||||
|
||||
Reference in New Issue
Block a user