662fbf7d6d
Generalize the family-locked legal tools into caveauAI persona profiles (client 57 chat profiles, resolved in-process via the chat_profiles bridge). Each tool accepts an optional `profile` slug that scopes the corpus package(s), search method, system prompt and synthesis model; omitting it falls back to the family-legal package so existing behaviour is unchanged. - dbnToolsResolvePersona / dbnToolsListPersonas / dbnToolsBootChatProfiles in bootstrap.php; new api/personas.php + dbn.list_personas MCP tool. - LegalTools search/ask/corpusContextForSummarize and the BvjAnalyzer / LegalAnalysis / translate paths take the persona's packages + prompt + model. - Persona <select> on ask/search/summarize (populated from api/personas.php). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
86 lines
3.0 KiB
PHP
86 lines
3.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../includes/LegalTools.php';
|
|
require_once __DIR__ . '/../includes/ToolModels.php';
|
|
|
|
dbnToolsRequireMethod('POST');
|
|
dbnToolsRequireAuth();
|
|
$ftUid = dbnToolsFreeTierCheck('summarize');
|
|
|
|
$input = dbnToolsJsonInput(400000);
|
|
$language = dbnToolsNormalizeLanguage($input['language'] ?? 'en');
|
|
$engine = ToolModels::engineForUser($ftUid, (string)($input['engine'] ?? 'azure_mini'));
|
|
$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);
|
|
|
|
if ($ftUid > 0) {
|
|
$balance = dbnToolsFreeTierDeduct($ftUid, 'summarize');
|
|
$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']);
|
|
}
|