Files
daveadmin a8b1bb87a6 feat(tools): converge two-tier Quick/Pro selector onto .no fork
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>
2026-06-15 12:23:46 +02:00

267 lines
20 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
$toolName = 'advocate';
$toolTitle = 'Case Advocate';
$toolKind = 'Legal advocate';
$toolBadge = 'Partisan brief';
$extraScripts = ['assets/js/advocate.js'];
require_once __DIR__ . '/includes/layout.php';
?>
<form id="advocateForm" class="tool-form deep-research" enctype="multipart/form-data">
<div class="lang-switcher" id="advLangSwitcher" role="group" aria-label="UI language">
<button type="button" class="lang-btn is-active" data-lang="en">&#127468;&#127463; EN</button>
<button type="button" class="lang-btn" data-lang="no">&#127475;&#127476; NO</button>
<button type="button" class="lang-btn" data-lang="uk">&#127482;&#127462; UK</button>
<button type="button" class="lang-btn" data-lang="pl">&#127477;&#127473; PL</button>
</div>
<div class="korr-doc-links">
<a href="/advocate-about.php" target="_blank">About this tool</a>
<span aria-hidden="true">&middot;</span>
<a href="/advocate-guide.php" target="_blank">User guide</a>
<span aria-hidden="true">&middot;</span>
<a href="/advocate-tech.php" target="_blank">How it works</a>
</div>
<!-- Role selector — the defining field for advocate mode -->
<div class="adv-role-row">
<label class="control-label" for="advRoleSelect">Who are you representing?</label>
<select id="advRoleSelect" class="adv-role-select" required>
<option value="">— Select a role —</option>
<option value="Biological mother">Biological mother</option>
<option value="Biological father">Biological father</option>
<option value="Both biological parents">Both biological parents</option>
<option value="Foster carer / long-term placement">Foster carer / long-term placement</option>
<option value="Adoptive parent">Adoptive parent</option>
<option value="Child (via representative)">Child (via representative)</option>
<option value="Extended family (grandparent, sibling, aunt/uncle)">Extended family (grandparent, sibling, aunt/uncle)</option>
<option value="Child welfare services (Barnevernet)">Child welfare services (Barnevernet)</option>
<option value="__other__">Other — describe below</option>
</select>
<input type="text" id="advRoleCustom" class="adv-role-custom is-hidden"
placeholder="Describe the party you represent…" maxlength="200">
<p class="upload-hint">The agent will frame every sub-question, retrieval pass, and the final brief to argue <em>for</em> the selected party, identifying weaknesses in the opposing position and citing Lovdata statutes, ECHR judgments, and Bufdir guidance.</p>
</div>
<label class="input-label" for="advInput">Describe the dispute or case situation</label>
<textarea id="advInput" name="advInput" rows="8" placeholder="Describe the facts of the case, the dispute, and what matters most to your client. The agent will generate adversarial sub-questions, retrieve from the legal corpus and your uploaded files, and synthesise a partisan brief with citations."></textarea>
<div class="adv-input-footer">
<small id="advInputCount" class="adv-char-count">0 / 4,000</small>
</div>
<div class="control-row" id="advTierControl">
<span class="control-label">Quality</span>
<label><input type="radio" name="advTier" value="quick" checked> Quick &#9733; <small class="control-hint">(Claude Haiku · fast · 3 credits)</small></label>
<label><input type="radio" name="advTier" value="pro"> Pro <small class="control-hint">(Claude Sonnet · best · 6 credits)</small></label>
</div>
<p class="upload-hint">Most of the time is spent on multiple question-answering passes — 610 sub-questions each requiring a full retrieval and answer cycle. Quick uses Claude Haiku 4.5 — faster and handles most cases well (3 credits). Pro uses Claude Sonnet 4.6 — a more thorough brief with deeper ECHR precedent analysis and stronger multi-party argumentation (6 credits).</p>
<div class="dr-slice-section">
<p class="control-label">Corpus slices</p>
<p class="upload-hint">Three core legal slices are on by default. Enable ECHR Article&nbsp;9, Hague Convention, Norwegian Courts, Bufdir guidance, or DBN&nbsp;Resources for more targeted research.</p>
<div class="dr-slice-grid">
<button type="button" class="adv-slice is-on" data-slice="family_core" aria-pressed="true">
<div class="dr-slice__head">
<span class="dr-slice__title">Family Law Core</span>
<span class="dr-slice__badge">on</span>
</div>
<p class="dr-slice__tagline">Barneloven, custody, samvær, mediation</p>
</button>
<button type="button" class="adv-slice is-on" data-slice="child_welfare" aria-pressed="true">
<div class="dr-slice__head">
<span class="dr-slice__title">Child Welfare</span>
<span class="dr-slice__badge">on</span>
</div>
<p class="dr-slice__tagline">Barnevern, omsorgsovertakelse, foster care</p>
</button>
<button type="button" class="adv-slice is-on" data-slice="echr" aria-pressed="true">
<div class="dr-slice__head">
<span class="dr-slice__title">ECHR</span>
<span class="dr-slice__badge">on</span>
</div>
<p class="dr-slice__tagline">Art.&nbsp;8 family life, Art.&nbsp;9 religion, HUDOC vs Norway</p>
</button>
<button type="button" class="adv-slice" data-slice="hague" aria-pressed="false">
<div class="dr-slice__head">
<span class="dr-slice__title">Hague Convention</span>
<span class="dr-slice__badge">off</span>
</div>
<p class="dr-slice__tagline">INCADAT, cross-border abduction, wrongful removal</p>
</button>
<button type="button" class="adv-slice" data-slice="norwegian_courts" aria-pressed="false">
<div class="dr-slice__head">
<span class="dr-slice__title">Norwegian Courts</span>
<span class="dr-slice__badge">off</span>
</div>
<p class="dr-slice__tagline">Høyesterett + Lagmannsrett family decisions</p>
</button>
<button type="button" class="adv-slice" data-slice="bufdir_guidance" aria-pressed="false">
<div class="dr-slice__head">
<span class="dr-slice__title">Bufdir Guidance</span>
<span class="dr-slice__badge">off</span>
</div>
<p class="dr-slice__tagline">Bufdir, Barneombudet, Statsforvalteren guidance</p>
</button>
<button type="button" class="adv-slice" data-slice="broader_legal" aria-pressed="false">
<div class="dr-slice__head">
<span class="dr-slice__title">Broader Legal Support</span>
<span class="dr-slice__badge">off</span>
</div>
<p class="dr-slice__tagline">Arbeidsmiljøloven, NOUer, statutes, government background</p>
</button>
<button type="button" class="adv-slice" data-slice="dbn_resources" aria-pressed="false">
<div class="dr-slice__head">
<span class="dr-slice__title">DBN Resources</span>
<span class="dr-slice__badge">off</span>
</div>
<p class="dr-slice__tagline">Do Better Norge guides, flashcards, resource directory</p>
</button>
</div>
</div>
<details class="advanced-panel" id="advAdvanced">
<summary class="advanced-toggle">Advanced controls</summary>
<div class="dr-control-grid">
<div class="dr-control-card">
<label>Sub-questions <span id="advSubQValue" class="dr-control-value">4</span></label>
<input type="range" id="advSubQ" min="3" max="5" step="1" value="4">
<small>How many angles the agent generates (each framed to strengthen your case).</small>
</div>
<div class="dr-control-card">
<label>Chunks / sub-Q <span id="advChunkLimitValue" class="dr-control-value">6</span></label>
<input type="range" id="advChunkLimit" min="4" max="10" step="1" value="6">
<small>Corpus chunks retrieved per sub-question.</small>
</div>
<div class="dr-control-card">
<label>Similarity floor <span id="advSimValue" class="dr-control-value">0.30</span></label>
<input type="range" id="advSim" min="0.20" max="0.60" step="0.05" value="0.30">
<small>Minimum similarity for uploaded-doc chunks to be included.</small>
</div>
<div class="dr-control-card">
<label>Sources kept <span id="advTopKValue" class="dr-control-value">12</span></label>
<input type="range" id="advTopK" min="8" max="14" step="1" value="12">
<small>Top sources kept after dedupe + rerank to feed synthesis.</small>
</div>
<div class="dr-control-card">
<label>Temperature <span id="advTempValue" class="dr-control-value">0.15</span></label>
<input type="range" id="advTemp" min="0.05" max="0.40" step="0.05" value="0.15">
<small>Keep low for grounded legal briefs.</small>
</div>
</div>
</details>
<div id="docPickerSection" class="doc-picker-section">
<button type="button" id="docPickerBtn" class="doc-picker-btn" aria-haspopup="dialog">
<svg class="doc-picker-btn__icon" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><rect x="2" y="1" width="9" height="12" rx="1.5" stroke="currentColor" stroke-width="1.4"/><path d="M5 5h5M5 8h3" stroke="currentColor" stroke-width="1.3" stroke-linecap="round"/><rect x="7" y="9" width="6" height="5" rx="1" fill="white" stroke="currentColor" stroke-width="1.3"/><path d="M9 11h2M9 12.5h1" stroke="currentColor" stroke-width="1" stroke-linecap="round"/></svg>
<span><?= htmlspecialchars(dbnToolsT('doc_picker_btn', $uiLang)) ?></span>
</button>
<div id="docPickerChips" class="doc-picker-chips" aria-label="Selected documents"></div>
<input type="hidden" id="docPickerIds" name="doc_ids" value="">
</div>
<div class="upload-zone" id="advUploadZone" role="region" aria-label="File upload">
<input type="file" id="advUploadInput" multiple accept=".pdf,.docx,.txt" aria-label="Choose files">
<div id="advUploadPrompt" class="upload-prompt">
<span class="upload-icon" aria-hidden="true">&#8679;</span>
<p>Drop case files here, or <label for="advUploadInput" class="upload-browse">browse</label></p>
<p class="upload-hint"><strong>PDF</strong>, <strong>DOCX</strong>, <strong>TXT</strong> &mdash; up to 5 files &mdash; chunked + embedded in memory only, never stored.</p>
</div>
<div id="advUploadFileInfo" class="upload-file is-hidden">
<ul id="advUploadFileList" class="upload-file-list"></ul>
<button type="button" id="advUploadClear" class="upload-clear">&times; Clear</button>
</div>
</div>
<div id="advBranchPanel" class="branch-panel is-hidden" aria-label="Branch context">
<div class="branch-panel__head">
<span class="branch-panel__label">Branching from sub-question</span>
<button type="button" id="advBranchClear" class="upload-clear">&times; Clear branch</button>
</div>
<p id="advBranchOrigin" class="branch-panel__origin"></p>
<details class="branch-panel__prior">
<summary>Prior brief summary</summary>
<div id="advBranchSummary" class="branch-panel__brief"></div>
</details>
<label class="input-label" for="advBranchNotes">Add your notes / context</label>
<textarea id="advBranchNotes" rows="3" placeholder="Optional: add observations, corrections, or additional context for this branch…"></textarea>
</div>
<!-- Sub-question preview panel (hidden until "Preview angles" is clicked) -->
<div id="advSubQPreview" class="adv-subq-preview is-hidden" aria-live="polite">
<h4 class="adv-subq-preview__head">Generated research angles — review &amp; edit</h4>
<p class="upload-hint">The agent will research these angles against the corpus. Edit any question before running to steer retrieval.</p>
<div id="advSubQPreviewList"></div>
<div class="adv-subq-preview__actions">
<button type="button" id="advRunWithAngles">Run with these angles</button>
<button type="button" id="advDiscardAngles" class="adv-discard-btn">Discard &amp; run fresh</button>
</div>
</div>
<?php require __DIR__ . '/includes/case_toggle.php'; ?>
<div class="form-footer">
<p id="advStatus" class="form-status" role="status" aria-live="polite"></p>
<div class="form-footer__btns">
<button id="advRunButton" type="submit">Research my case</button>
<button type="button" id="advPreviewAngles" class="adv-preview-btn">Preview research angles first</button>
</div>
</div>
</form>
<section id="advResults" class="results deep-research-results" aria-live="polite">
<div class="empty-state">
<h3>Ready</h3>
<p>Select who you are representing, describe the dispute, and optionally upload case documents. The agent will argue your side — identifying supporting statutes, ECHR judgments, and weaknesses in the opposing position.</p>
</div>
</section>
<!-- Source modal -->
<div id="advSourceModal" class="dr-source-modal is-hidden" role="dialog" aria-modal="true" aria-labelledby="advSourceModalTitle">
<div class="dr-source-modal__dialog">
<header class="dr-source-modal__head">
<div>
<p class="eyebrow" id="advSourceModalEyebrow">Source</p>
<h3 id="advSourceModalTitle"></h3>
</div>
<button type="button" id="advSourceModalClose" class="upload-clear" aria-label="Close">&times;</button>
</header>
<div class="dr-source-modal__body">
<aside class="dr-source-modal__meta" id="advSourceModalMeta"></aside>
<div class="dr-source-modal__text" id="advSourceModalText"></div>
</div>
</div>
</div>
<!-- Hidden stubs so tools.js element refs don't crash on this page -->
<div class="is-hidden" id="languageControl" aria-hidden="true">
<input type="radio" name="language" value="en" checked>
<input type="radio" name="language" value="no">
<input type="radio" name="language" value="uk">
<input type="radio" name="language" value="pl">
</div>
<div class="is-hidden" id="redactionControl" aria-hidden="true"></div>
<div class="is-hidden" id="audioZone" aria-hidden="true">
<input type="file" id="audioInput" style="display:none">
<div id="audioPrompt"></div>
<div id="audioFileInfo"><ol id="audioQueueList"></ol><button type="button" id="audioClear"></button></div>
</div>
<div class="is-hidden" id="diarizeControl" aria-hidden="true">
<input type="checkbox" id="diarizeCheck">
<input type="number" id="numSpeakersInput">
</div>
<div class="is-hidden" id="transcribeLangControl" aria-hidden="true"><input type="radio" name="transcribeLang" value="no" checked></div>
<div class="is-hidden" id="vocabControl" aria-hidden="true">
<div id="vocabPresets"></div>
<textarea id="initPromptInput"></textarea>
</div>
<div class="is-hidden" id="aliasSection" aria-hidden="true">
<button type="button" id="addAliasRow"></button>
<div id="aliasRows"></div>
</div>
<div class="is-hidden" id="exemptSection" aria-hidden="true">
<button type="button" id="addExemptRow"></button>
<div id="exemptRows"></div>
</div>
<?php require_once __DIR__ . '/includes/layout_footer.php'; ?>