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

91 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../includes/LegalTools.php';
require_once __DIR__ . '/../includes/ToolModels.php';
dbnToolsRequireMethod('POST');
dbnToolsRequireAuth();
$input = dbnToolsJsonInput(400000);
$language = dbnToolsNormalizeLanguage($input['language'] ?? 'en');
$run = dbnToolsResolveToolRun('summarize', $input);
$ftUid = $run['ftUid'];
$engine = $run['engine'];
$depth = in_array($input['depth'] ?? '', ['brief', 'standard', 'detailed'], true)
? (string)$input['depth'] : 'standard';
$slices = is_array($input['slices'] ?? null) ? array_values(array_filter($input['slices'])) : [];
// Streaming headers — flush each NDJSON line as it's written
header('Content-Type: application/x-ndjson; charset=utf-8');
header('X-Accel-Buffering: no');
header('Cache-Control: no-cache, no-store');
while (ob_get_level()) {
ob_end_clean();
}
$emit = static function (string $event, array $payload): void {
echo json_encode(['event' => $event] + $payload, JSON_UNESCAPED_UNICODE) . "\n";
flush();
};
try {
$text = dbnToolsInjectDocContent($input, dbnToolsString($input, 'text', 128000, false));
if (mb_strlen(trim($text), 'UTF-8') < 50) {
$emit('error', [
'error' => 'Paste text, upload a file, or select a document before running.',
'code' => 'empty_text',
]);
exit;
}
$emit('progress', [
'step' => 'text_ready',
'detail' => mb_strlen($text, 'UTF-8') . ' chars ready.',
]);
$corpusContext = '';
if (!empty($slices)) {
$sliceCount = count($slices);
$emit('progress', [
'step' => 'corpus_search',
'detail' => 'Searching legal corpus (' . $sliceCount . ' slice' . ($sliceCount === 1 ? '' : 's') . ')…',
]);
$svc = new DbnLegalToolsService();
$query = mb_substr(trim($text), 0, 600, 'UTF-8');
$persona = (isset($input['profile']) && is_string($input['profile']) && trim($input['profile']) !== '')
? trim($input['profile'])
: null;
$corpusContext = $svc->corpusContextForSummarize($query, 8, $persona);
$emit('progress', [
'step' => 'corpus_done',
'detail' => $corpusContext !== ''
? 'Legal context retrieved.'
: 'No matching passages found; summarising without corpus.',
]);
}
$emit('progress', [
'step' => 'generating',
'detail' => 'Generating summary…',
]);
$result = (new DbnLegalToolsService())->summarizeWithContext($text, $language, $engine, $corpusContext, $depth);
if ($ftUid > 0) {
$balance = $run['credits'] === null
? dbnToolsFreeTierDeduct($ftUid, 'summarize')
: dbnToolsFreeTierDeductAmount($ftUid, 'summarize', $run['credits'], $run['metadata']);
$result['balance'] = $balance;
}
$emit('final', $result);
} catch (DbnToolsHttpException $e) {
$emit('error', ['error' => $e->getMessage(), 'code' => (string)$e->getCode()]);
} catch (Throwable $e) {
error_log('summarize error: ' . $e->getMessage());
$emit('error', ['error' => 'An unexpected error occurred. Please try again.', 'code' => 'server_error']);
}