a8b1bb87a6
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>
217 lines
16 KiB
PHP
217 lines
16 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
$toolName = 'korrespond';
|
||
$toolTitle = 'Korrespond';
|
||
$toolKind = 'Draft & reply to Norwegian authorities';
|
||
$toolBadge = 'Hard-RAG · Norsk + EN/PL/UK';
|
||
$extraScripts = ['assets/js/korrespond.js'];
|
||
require_once __DIR__ . '/includes/layout.php';
|
||
?>
|
||
<form id="korrForm" class="tool-form deep-research" enctype="multipart/form-data">
|
||
|
||
<div class="lang-switcher" id="korrLangSwitcher" role="group" aria-label="UI language">
|
||
<button type="button" class="lang-btn is-active" data-lang="en">🇬🇧 EN</button>
|
||
<button type="button" class="lang-btn" data-lang="no">🇳🇴 NO</button>
|
||
<button type="button" class="lang-btn" data-lang="uk">🇺🇦 UK</button>
|
||
<button type="button" class="lang-btn" data-lang="pl">🇵🇱 PL</button>
|
||
</div>
|
||
|
||
<div class="korr-doc-links">
|
||
<a href="/korrespond-about.php" target="_blank">About this tool</a>
|
||
<span aria-hidden="true">·</span>
|
||
<a href="/korrespond-guide.php" target="_blank">User guide</a>
|
||
<span aria-hidden="true">·</span>
|
||
<a href="/korrespond-tech.php" target="_blank">How it works</a>
|
||
</div>
|
||
|
||
<!-- Mode toggle -->
|
||
<div class="control-row" id="korrModeControl">
|
||
<span class="control-label">Mode</span>
|
||
<label><input type="radio" name="korrMode" value="reply"> <strong>Reply</strong> <small class="control-hint">to a letter / decision / notice you received</small></label>
|
||
<label><input type="radio" name="korrMode" value="initiate" checked> <strong>Initiate</strong> <small class="control-hint">start a new correspondence</small></label>
|
||
</div>
|
||
|
||
<!-- Recipient body -->
|
||
<div class="adv-role-row">
|
||
<label class="control-label" for="korrBody">Recipient body</label>
|
||
<select id="korrBody" class="adv-role-select" required>
|
||
<option value="">— Velg mottaker —</option>
|
||
<option value="barnehage">Barnehage</option>
|
||
<option value="school_1_10">Skole (1.–10. trinn)</option>
|
||
<option value="sfo">SFO</option>
|
||
<option value="nav">NAV</option>
|
||
<option value="bufdir">Bufdir (adopsjon, surrogati)</option>
|
||
<option value="barnevernet">Barnevernet</option>
|
||
<option value="kommune_other">Kommunen (annet)</option>
|
||
<option value="statsforvalter">Statsforvalteren</option>
|
||
<option value="trygderetten">Trygderetten</option>
|
||
<option value="tingrett">Tingretten</option>
|
||
<option value="other">Annet</option>
|
||
</select>
|
||
<p class="upload-hint">Each recipient preset pulls the relevant statute set into the hard-RAG retrieval (fvl, barnevernsloven, NAV-loven, opplæringslova, barnehageloven, EMK).</p>
|
||
</div>
|
||
|
||
<!-- Output type -->
|
||
<div class="control-row" id="korrOutputControl">
|
||
<span class="control-label">Output type</span>
|
||
<label><input type="radio" name="korrOutput" value="email" checked> Email</label>
|
||
<label><input type="radio" name="korrOutput" value="formal"> Formal letter</label>
|
||
<label><input type="radio" name="korrOutput" value="filing"> Court/tribunal filing</label>
|
||
<label><input type="radio" name="korrOutput" value="call_prep"> Phone-call prep</label>
|
||
</div>
|
||
|
||
<!-- Tone -->
|
||
<div class="control-row" id="korrToneControl">
|
||
<span class="control-label">Tone</span>
|
||
<label><input type="radio" name="korrTone" value="cooperative"> Cooperative</label>
|
||
<label><input type="radio" name="korrTone" value="neutral" checked> Neutral-professional ★</label>
|
||
<label><input type="radio" name="korrTone" value="firm"> Firm</label>
|
||
<label><input type="radio" name="korrTone" value="adversarial"> Adversarial</label>
|
||
<label><input type="radio" name="korrTone" value="warm"> Conciliatory-warm</label>
|
||
</div>
|
||
|
||
<!-- Letter length -->
|
||
<div class="control-row" id="korrLengthControl">
|
||
<span class="control-label">Letter length</span>
|
||
<label><input type="radio" name="korrLength" value="concise"> Concise</label>
|
||
<label><input type="radio" name="korrLength" value="standard" checked> Standard ★</label>
|
||
<label><input type="radio" name="korrLength" value="detailed"> Detailed</label>
|
||
</div>
|
||
<p class="upload-hint">Concise: short and to-the-point (2-3 paragraphs). Standard: balanced correspondence. Detailed: full background, all arguments, and complete legal reasoning.</p>
|
||
|
||
<!-- Quality tier -->
|
||
<div class="control-row" id="korrTierControl">
|
||
<span class="control-label">Quality</span>
|
||
<label><input type="radio" name="korrTier" value="quick" checked> Quick ★ <small class="control-hint">(Claude Haiku · fast · 3 credits)</small></label>
|
||
<label><input type="radio" name="korrTier" value="pro"> Pro <small class="control-hint">(Claude Sonnet · best · 6 credits)</small></label>
|
||
</div>
|
||
<p class="upload-hint">Quick uses Claude Haiku 4.5 for drafting — fast and solid for standard correspondence (3 credits). Pro uses Claude Sonnet 4.6 — better at multi-statute cases, complex appeal grounds, and ECHR framing (6 credits).</p>
|
||
|
||
<!-- Domain persona -->
|
||
<div class="control-row corpus-persona is-hidden" id="korrPersonaControl">
|
||
<span class="control-label">Domain</span>
|
||
<select id="korrPersonaSelect" class="adv-role-select" aria-label="Legal domain persona"></select>
|
||
<small class="control-hint">Scopes the hard-RAG law retrieval to a legal domain.</small>
|
||
</div>
|
||
|
||
<!-- Case context fields -->
|
||
<div class="dr-control-grid">
|
||
<div class="dr-control-card">
|
||
<label for="korrCaseRef">Case reference (saksnummer)</label>
|
||
<input type="text" id="korrCaseRef" maxlength="120" placeholder="e.g. 2026/12345">
|
||
</div>
|
||
<div class="dr-control-card">
|
||
<label for="korrWhere">Where (kommune / fylke)</label>
|
||
<input type="text" id="korrWhere" maxlength="200" placeholder="e.g. Trondheim kommune">
|
||
</div>
|
||
<div class="dr-control-card">
|
||
<label for="korrDeadline">Next deadline</label>
|
||
<input type="text" id="korrDeadline" maxlength="60" placeholder="YYYY-MM-DD or '3 weeks'">
|
||
</div>
|
||
</div>
|
||
|
||
<label class="input-label" for="korrParties">Who is involved? <span class="optional-hint">(names + roles)</span></label>
|
||
<textarea id="korrParties" rows="2" maxlength="2000" placeholder="e.g. Me (parent), caseworker Anna Hansen, child Ola (age 5). Use the Redact tool first if you'll share externally."></textarea>
|
||
|
||
<label class="input-label" for="korrNarrative">What happened / context <span class="required-hint">(required for initiate mode)</span></label>
|
||
<textarea id="korrNarrative" rows="6" maxlength="8000" placeholder="Describe the situation: what happened, when, who decided what, what you want."></textarea>
|
||
|
||
<label class="input-label" for="korrGoal">Goal of this letter <span class="optional-hint">(or pick a chip)</span></label>
|
||
<input type="text" id="korrGoal" maxlength="600" placeholder="e.g. request access to all case documents under forvaltningsloven §18">
|
||
<div class="korr-goal-chips" id="korrGoalChips" role="group" aria-label="Common goals">
|
||
<button type="button" class="korr-chip" data-goal="Request access to case documents (forvaltningsloven §18)">Access to docs (fvl §18)</button>
|
||
<button type="button" class="korr-chip" data-goal="Appeal the decision (forvaltningsloven §28)">Appeal (fvl §28)</button>
|
||
<button type="button" class="korr-chip" data-goal="Request a meeting">Request meeting</button>
|
||
<button type="button" class="korr-chip" data-goal="Request reasoned written decision (forvaltningsloven §24-25)">Reasoned decision (fvl §24-25)</button>
|
||
<button type="button" class="korr-chip" data-goal="Invoke right to be heard (forvaltningsloven §17)">Right to be heard (fvl §17)</button>
|
||
<button type="button" class="korr-chip" data-goal="Complaint about caseworker conduct">Complaint</button>
|
||
<button type="button" class="korr-chip" data-goal="Clarify status / timeline of the case">Clarify timeline</button>
|
||
</div>
|
||
|
||
<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>
|
||
|
||
<!-- File upload (required for Reply mode) -->
|
||
<div class="upload-zone" id="korrUploadZone" role="region" aria-label="File upload">
|
||
<input type="file" id="korrUploadInput" multiple accept=".pdf,.docx,.txt" aria-label="Choose received letter or attachments">
|
||
<div id="korrUploadPrompt" class="upload-prompt">
|
||
<span class="upload-icon" aria-hidden="true">⇧</span>
|
||
<p>Upload the received letter (reply mode) or supporting attachments, or <label for="korrUploadInput" class="upload-browse">browse</label></p>
|
||
<p class="upload-hint"><strong>PDF</strong>, <strong>DOCX</strong>, <strong>TXT</strong> — up to 4 files, max 8 MB each — processed in memory.</p>
|
||
</div>
|
||
<div id="korrUploadFileInfo" class="upload-file is-hidden">
|
||
<ul id="korrUploadFileList" class="upload-file-list"></ul>
|
||
<button type="button" id="korrUploadClear" class="upload-clear">× Clear</button>
|
||
</div>
|
||
</div>
|
||
|
||
<?php require __DIR__ . '/includes/case_toggle.php'; ?>
|
||
|
||
<div class="form-footer">
|
||
<p id="korrStatus" class="form-status" role="status" aria-live="polite"></p>
|
||
<button id="korrRunButton" type="submit">Draft</button>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- Clarify panel (shown if Pass 1 returns missing_facts) -->
|
||
<section id="korrClarifyPanel" class="korr-clarify-panel is-hidden" aria-labelledby="korrClarifyTitle">
|
||
<h3 id="korrClarifyTitle" data-i18n="clarify_title">Before we draft, clarify:</h3>
|
||
<p class="upload-hint" data-i18n="clarify_hint">Answer what you can, then click Continue draft. Or click Draft anyway to proceed with what we have.</p>
|
||
<div id="korrClarifyList" class="korr-clarify-list"></div>
|
||
<div class="korr-clarify-actions">
|
||
<button type="button" id="korrClarifyContinue" class="primary-button" data-i18n="clarify_continue">Continue draft</button>
|
||
<button type="button" id="korrClarifyForce" class="secondary-button" data-i18n="clarify_force">Draft anyway</button>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="korrResults" class="results deep-research-results" aria-live="polite">
|
||
<div class="empty-state">
|
||
<h3 data-i18n="ready_title">Ready</h3>
|
||
<p data-i18n="ready_desc">Pick a recipient body, describe the situation, choose an output type and tone, then run. Drafts always come back in Norwegian bokmål + your working language, side-by-side, with verified law citations.</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- 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>
|
||
<div class="is-hidden" id="uploadZone" aria-hidden="true">
|
||
<input type="file" id="uploadInput">
|
||
<div id="uploadPrompt"></div>
|
||
<div id="uploadFileInfo"><ul id="uploadFileList"></ul><button type="button" id="uploadClear"></button></div>
|
||
</div>
|
||
<textarea class="is-hidden" id="toolInput" aria-hidden="true"></textarea>
|
||
<?php require_once __DIR__ . '/includes/layout_footer.php'; ?>
|