Add public showcase landing, doc summary cards, and chunk toggle

- index.php: public showcase landing page (hero, how-it-works, capabilities,
  evidence mock, login form) visible to unauthenticated visitors; full OG/SEO
  meta; app shell hidden behind auth as before
- tools.css: showcase section styles (gradient hero, step cards, capability
  grid, CTA button, evidence mock, footer)
- LegalTools.php: sourceFromChunk() batch-fetches doc_summaries from RAG DB
  for non-private chunks; excerpt shows doc summary when available, falls back
  to raw chunk text; chunk_text field always carries the raw excerpt
- tools.js: renderEvidenceItem() shows doc summary as card body; adds a
  collapsible "View chunk" toggle when summary differs from raw chunk text

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 08:37:36 +02:00
parent 3442b1b6d3
commit 1f4f01bda3
4 changed files with 554 additions and 34 deletions
+47 -10
View File
@@ -70,7 +70,23 @@ final class DbnLegalToolsService
}
}
$hits = array_map(fn(array $chunk): array => $this->sourceFromChunk($chunk), array_slice($chunks, 0, $limit));
$sharedDocIds = [];
foreach (array_slice($chunks, 0, $limit) as $chunk) {
if (($chunk['source_type'] ?? '') !== 'private' && isset($chunk['document_id'])) {
$sharedDocIds[(int)$chunk['document_id']] = true;
}
}
$docSummaries = $sharedDocIds ? $this->fetchDocSummaries(array_keys($sharedDocIds)) : [];
$hits = array_map(
fn(array $chunk): array => $this->sourceFromChunk(
$chunk,
($chunk['source_type'] ?? '') !== 'private'
? ($docSummaries[(int)($chunk['document_id'] ?? 0)] ?? null)
: null
),
array_slice($chunks, 0, $limit)
);
$confidence = $this->citationConfidence($hits);
$trace[1] = $this->trace('Search tools used', $retrievalNote . '; returned ' . count($hits) . ' source hit(s).', 'complete');
@@ -461,23 +477,44 @@ PROMPT;
return array_values(array_filter($trail, 'is_array'));
}
private function sourceFromChunk(array $chunk): array
private function sourceFromChunk(array $chunk, ?string $docSummary = null): array
{
$title = (string)($chunk['document_title'] ?? $chunk['title'] ?? 'Untitled source');
$score = isset($chunk['similarity']) ? round((float)$chunk['similarity'], 4) : null;
$rawExcerpt = dbnToolsExcerpt((string)($chunk['content'] ?? ''), 620);
return [
'title' => $title,
'excerpt' => dbnToolsExcerpt((string)($chunk['content'] ?? ''), 620),
'title' => $title,
'excerpt' => $docSummary ?? $rawExcerpt,
'chunk_text' => $rawExcerpt,
'package_or_corpus' => (string)($chunk['source_name'] ?? $chunk['source_type'] ?? 'Do Better Norge'),
'score' => $score,
'document_id' => isset($chunk['document_id']) ? (int)$chunk['document_id'] : null,
'chunk_id' => isset($chunk['id']) ? (int)$chunk['id'] : null,
'section' => $chunk['section_title'] ?? null,
'authority_type' => $chunk['authority_type'] ?? null,
'jurisdiction' => $chunk['jurisdiction'] ?? null,
'score' => $score,
'document_id' => isset($chunk['document_id']) ? (int)$chunk['document_id'] : null,
'chunk_id' => isset($chunk['id']) ? (int)$chunk['id'] : null,
'section' => $chunk['section_title'] ?? null,
'authority_type' => $chunk['authority_type'] ?? null,
'jurisdiction' => $chunk['jurisdiction'] ?? null,
];
}
private function fetchDocSummaries(array $docIds): array
{
if (!$docIds) {
return [];
}
try {
$db = dbnToolsRagDb();
$placeholders = implode(',', array_fill(0, count($docIds), '?'));
$stmt = $db->prepare(
"SELECT document_id, summary FROM doc_summaries
WHERE document_id IN ({$placeholders}) AND summary != ''"
);
$stmt->execute(array_values($docIds));
return array_column($stmt->fetchAll(PDO::FETCH_ASSOC), 'summary', 'document_id');
} catch (Throwable) {
return [];
}
}
private function citationConfidence(array $hits): string
{
if (!$hits) {