The "browse" label's native for=input trigger AND the upload-zone click
handler both called uploadInput.click(), so the picker opened twice when
the user clicked the browse text. Stop propagation on the label and the
input itself, plus tighten the zone handler to recognise any label-for
descendant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restores the dbn-legal-agent-v3 fine-tune on ocelot (was silently aliased
to plain qwen2.5:14b in LiteLLM since the viper retirement) and ships a
new tool that uses it via a two-pass flow:
Pass 1 (Azure 4o-mini) → extract up to 5 distinct legal issues
Pass 2 (ocelot v3 only) → answer each issue, ≤350 tokens, with corpus
Pass 3 (Azure 4o-mini) → synthesise overall assessment + next steps
The 12GB-VRAM constraint motivates the split: dbn-legal-agent-v3 stays
hot in VRAM through the 5 sequential per-issue calls because issue
extraction and synthesis run on Azure, not on ocelot.
New surface:
- includes/LegalAnalysisAgent.php
- api/legal-analysis.php (NDJSON streaming endpoint)
- legal-analysis.php (dedicated tool page)
- assets/js/legal-analysis.js (streamed UI with per-issue cards)
- Save-result + case-result.php rendering for legal-analysis output
- Nav registration in all four UI languages
Add-on integration: a "⚖️🇳🇴 Run deep legal analysis on this text"
button now appears on Summarize, Ask, and Redact result pages and
streams the same pipeline inline below the existing result.
Existing tools relabelled: the misleading "🇳🇴 Norwegian specialist v3 ⭐"
option on advocate/deep-research/discrepancy/barnevernet is now honestly
"DBN Legal Agent" — now that the real fine-tune is actually deployed,
the label finally matches reality. The advocate.php v2 option was
removed since the v2 GGUF is retired.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>