0) { @ob_end_clean(); } ob_implicit_flush(true); header('Content-Type: application/x-ndjson; charset=utf-8'); header('Cache-Control: no-store'); header('X-Accel-Buffering: no'); $startTime = microtime(true); $language = 'en'; $emit = function (string $event, array $payload = []) use ($startTime): void { $payload['event'] = $event; $payload['t_ms'] = (int)round((microtime(true) - $startTime) * 1000); echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n"; @flush(); }; try { $raw = file_get_contents('php://input'); if ($raw === false || strlen($raw) > 200000) { throw new DbnToolsHttpException('Request body unreadable or too large.', 413, 'body_too_large'); } $input = json_decode((string)$raw, true); if (!is_array($input)) { throw new DbnToolsHttpException('Request body must be valid JSON.', 400, 'invalid_json'); } $language = dbnToolsNormalizeLanguage($input['language'] ?? 'en'); $jurisdiction = $input['jurisdiction'] ?? 'norwegian'; if (!in_array($jurisdiction, ['norwegian', 'echr', 'both'], true)) { throw new DbnToolsHttpException('Invalid jurisdiction.', 422, 'invalid_jurisdiction'); } $originalDraftNo = trim((string)($input['original_draft_no'] ?? '')); if ($originalDraftNo === '') { throw new DbnToolsHttpException('original_draft_no is required.', 422, 'missing_original_draft'); } if (mb_strlen($originalDraftNo, 'UTF-8') > 12000) { throw new DbnToolsHttpException('original_draft_no is too large.', 413, 'original_too_large'); } $classify = is_array($input['classify'] ?? null) ? $input['classify'] : []; $intake = is_array($input['intake'] ?? null) ? $input['intake'] : []; // Sanitise intake to expected fields only $allowedBodies = ['barnehage','school_1_10','sfo','nav','bufdir','barnevernet', 'kommune_other','statsforvalter','trygderetten','tingrett','other']; $allowedOutput = ['email','formal','filing','call_prep']; $allowedTone = ['cooperative','neutral','firm','adversarial','warm']; $intake = [ 'recipient_body' => in_array($intake['recipient_body'] ?? '', $allowedBodies, true) ? $intake['recipient_body'] : 'other', 'output_type' => in_array($intake['output_type'] ?? '', $allowedOutput, true) ? $intake['output_type'] : 'email', 'tone' => in_array($intake['tone'] ?? '', $allowedTone, true) ? $intake['tone'] : 'neutral', 'language' => $language, 'goal' => mb_substr(trim((string)($intake['goal'] ?? '')), 0, 600, 'UTF-8'), ]; // Credit gate (refine is a paid pass) $ftUid = dbnToolsFreeTierCheck('korrespond_refine'); $emit('start', [ 'jurisdiction' => $jurisdiction, 'body' => $intake['recipient_body'], 'language' => $language, ]); $agent = new DbnKorrespondAgent(); $result = $agent->refine($intake, $classify, $originalDraftNo, $jurisdiction, $emit); $ftRemaining = dbnToolsFreeTierDeduct($ftUid, 'korrespond_refine'); $result['ok'] = true; $result['latency_ms'] = (int)round((microtime(true) - $startTime) * 1000); if ($ftRemaining >= 0) { $result['balance'] = $ftRemaining; } dbnToolsLogMetadata([ 'tool' => 'korrespond_refine', 'language' => $language, 'ok' => true, 'latency_ms' => $result['latency_ms'], 'source_count' => is_array($result['cited_law'] ?? null) ? count($result['cited_law']) : 0, 'deployment' => 'gpt-4o', 'jurisdiction' => $jurisdiction, ]); $emit('final', ['result' => $result]); } catch (DbnToolsHttpException $e) { $latency = (int)round((microtime(true) - $startTime) * 1000); dbnToolsLogMetadata([ 'tool' => 'korrespond_refine', 'language' => $language, 'ok' => false, 'latency_ms' => $latency, 'error_code' => $e->errorCode, ]); $emit('error', ['code' => $e->errorCode, 'message' => $e->getMessage(), 'status' => $e->status]); } catch (Throwable $e) { error_log('Korrespond refine fatal: ' . $e->getMessage()); $latency = (int)round((microtime(true) - $startTime) * 1000); dbnToolsLogMetadata([ 'tool' => 'korrespond_refine', 'language' => $language, 'ok' => false, 'latency_ms' => $latency, 'error_code' => 'internal_error', ]); $emit('error', ['code' => 'internal_error', 'message' => 'Korrespond refine could not complete this request.']); }