Port the dobetterlegal-tools two-tier quality stack to dobetternorge.no:
QUALITY_TIERS registry + resolveTier (ToolModels), dbnToolsResolveToolRun
(bootstrap), tier read+charge in the 6 analytical endpoints, Quick/Pro
UI + payload.tier on the 6 tool pages/JS, and the bounded
corpusContextForSummarize RAG fix (per-passage trim + total budget +
reranker_enabled). Back-compat: requests without `tier` keep legacy
engine behavior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wire the legal-domain persona picker into corpus, deep-research, korrespond and
the dashboard chat. Each endpoint reads the chosen profile, resolves its packages
against client 57, and scopes retrieval via package_ids (falling back to family
when omitted). New dashboard tenants now subscribe to all DBN domain packages so
persona switching survives the subscription intersection.
Co-Authored-By: Claude Opus 4.7 <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>
- Public landing page at / for unauthenticated users (EN/NO/UK/PL)
- Authenticated / shows Case Workbench dashboard with manifesto strip,
stats, and launched-tool grid (Transcribe, Timeline, BVJ, Advocate,
Deep Research, Corpus)
- Added includes/i18n.php with full 4-language translation layer
- Extended layout.php to Case Workbench shell with tool rail, lang switcher
- AI output language normalization extended to en/no/uk/pl in PHP agents
- SSO token validation in bootstrap.php / index.php (dobetternorge.no bridge)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Source modal now shows LLM-generated document summary (lazy-gen + cached
in documents.summary) instead of raw chunk text; toggle reveals matched
chunk; "View all chunks" button fetches every chunk of the document via
new api/document-chunks.php endpoint
- Each sub-question card gets a "Branch ↓" button that pre-fills the query
with that sub-question and shows a context panel with the prior brief
summary; prior_context + branch_notes are injected into interpretSeed()
and synthesise() so the LLM knows where the research is coming from
- Upload document summaries generated at synthesis time and attached to
upload sources alongside corpus summaries
- DB: documents.summary TEXT column added to bnl_corpus on chloe
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace combined echr_hague slice with echr (Art.8+9, HUDOC, NIM) and hague (INCADAT,
cross-border abduction) as separate toggles; echr defaults ON, hague defaults OFF
- Add norwegian_courts slice: Domstol (src 5,26) + Rettspraksis.no (src 33, 482 docs)
- Add bufdir_guidance slice: Barneombudet (19), Bufdir (20), Statsforvalteren (31)
- Add dbn_resources slice: DBN website pages (flashcards, resource directory), defaults OFF
- Replace isWebsiteChunk() with slice-aware shouldExcludeChunk(): always strips EU AI Act
chunks (EUR-Lex source 7 leaks through when Qdrant runs unconstrained) and DBN website
pages unless dbn_resources slice is explicitly ON
- Update SLICE_DEFS in advocate.js and deep-research.js to match all 8 slices
- Backward compat: echr_hague key in incoming requests fans out to echr+hague
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the endpoint returned a single JSON object at the end. Apache+
PHP-FPM buffers the entire body until PHP exits, so a 160s azure_full run
caused the browser to drop the fetch as "Failed to fetch" while the server
was still synthesising — the response then arrived to a dead socket.
Switch to application/x-ndjson with one event per line. The endpoint emits
'progress', 'start', 'step' (running/complete/warning/error), 'subq', and a
final 'final' event carrying the full result payload. Output buffering is
explicitly disabled so each line flushes through Apache as soon as the
agent emits it.
DbnDeepResearchAgent::run() now accepts an optional ?callable $emit and
fires step:running before each step + step:complete after, plus a subq
event per sub-question retrieval round.
JS reads response.body as a stream, splits on newlines, updates the
trace panel live, and renders the final result when the final event
arrives. Status pill shows live progress detail (e.g. "Synthesising with
Azure gpt-4o — this is the slowest step…").
Engine row in the form now shows expected duration per engine
(~15-45s mini, ~60-180s full, ~30-90s GPU) so users know what they're in
for before clicking Run.
New surface at /deep-research.php where the user pastes a question or
uploads PDF/DOCX/TXT case files and a LLM-orchestrated agent researches
the Do Better Norge legal corpus from 3-5 angles, with hybrid retrieval,
cross-encoder rerank, and synthesis that emits an inline-[n]-cited
markdown brief plus a numbered sources panel.
Uploaded documents are chunked + embedded in memory only (nomic-embed-text
via LiteLLM) and searched alongside the shared corpus during the same
request — never persisted to disk, DB, or Qdrant.
Reuses ClientRagPipeline::searchAll (hybrid + rerank), dbnV6 slice
helpers, and the existing extract.php text-extraction logic via a new
dbnToolsExtractUploadedFile() helper. Also adds dbnToolsCallGpuLlm()
helper in bootstrap.php — fixes a latent bug where LegalTools.php
was already calling that name with no definition.
Search.php is unchanged.