06d01a3bce
Full private corpus dashboard for tools.dobetternorge.no users — each SSO
account gets an auto-provisioned CaveauAI tenant (clients row, corpus) on
first visit. Includes upload (file/paste/URL), RAG chat with SSE streaming
and citation chips, document CRUD, FalkorDB graph relations tab, and
improved save-from-tool flow with tag/preview support.
- dashboard/{index,documents,document,upload,chat,settings}.php
- api/dashboard/{corpus-init,documents,upload,ingest-status,chat-stream,
save-from-tool,graph}.php
- includes/{CorpusProvision,layout_dashboard,layout_dashboard_footer}.php
- assets/css/dashboard.css assets/js/corpus-save.js (routing upgrade)
- includes/{bootstrap,layout}.php extended for dashboard provisioning
Migration 141 (clients.dbn_sso_uid + import_method enum) applied on chloe.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
82 lines
2.7 KiB
PHP
82 lines
2.7 KiB
PHP
<?php
|
|
/**
|
|
* GET /api/dashboard/graph.php?action=cites|cited_by|implements|chain&doc_id=N&limit=20&depth=2
|
|
*
|
|
* Wraps ai-portal/lib/ai/LegalGraphAgent for the dashboard. Reads the FalkorDB
|
|
* `bnl_legal` graph on Colin (10.0.2.10:6379). Public graph metadata — no
|
|
* sensitive content — but we still gate on dashboard auth to avoid being a
|
|
* generic open proxy.
|
|
*
|
|
* Response shape mirrors ai-portal/api/graph-search.php:
|
|
* { ok, action, doc_id, count, results: [ {rel_type, doc_id, title, ...}, ...] }
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once dirname(__DIR__, 2) . '/includes/bootstrap.php';
|
|
|
|
dbnToolsRequireMethod('GET');
|
|
dbnToolsRequireAuth();
|
|
|
|
// Don't require dashboard provisioning here — graph is public metadata.
|
|
|
|
$action = trim((string)($_GET['action'] ?? ''));
|
|
$docId = (int)($_GET['doc_id'] ?? 0);
|
|
$limit = max(1, min(100, (int)($_GET['limit'] ?? 20)));
|
|
$depth = max(1, min(3, (int)($_GET['depth'] ?? 2)));
|
|
|
|
$validActions = ['cites', 'cited_by', 'implements', 'chain'];
|
|
if (!in_array($action, $validActions, true)) {
|
|
dbnToolsError(
|
|
'action must be one of: ' . implode(', ', $validActions),
|
|
400, 'invalid_action', ['actions' => $validActions]
|
|
);
|
|
}
|
|
if ($docId <= 0) {
|
|
dbnToolsError('doc_id must be a positive integer.', 400, 'missing_doc_id');
|
|
}
|
|
|
|
$root = dbnToolsAiPortalRoot();
|
|
$graphFile = $root . '/lib/ai/GraphClient.php';
|
|
$agentFile = $root . '/lib/ai/LegalGraphAgent.php';
|
|
|
|
if (!is_file($graphFile) || !is_file($agentFile)) {
|
|
dbnToolsError('Graph backend not installed.', 503, 'graph_unavailable');
|
|
}
|
|
require_once $graphFile;
|
|
require_once $agentFile;
|
|
|
|
try {
|
|
$config = file_exists('/etc/bnl/config.php') ? include '/etc/bnl/config.php' : [];
|
|
$host = (string)($config['falkordb']['host'] ?? dbnToolsEnv('DBN_FALKORDB_HOST', '10.0.2.10'));
|
|
$port = (int) ($config['falkordb']['port'] ?? (int)dbnToolsEnv('DBN_FALKORDB_PORT', '6379'));
|
|
$pass = (string)($config['falkordb']['password'] ?? dbnToolsEnv('DBN_FALKORDB_PASSWORD', ''));
|
|
|
|
$client = new GraphClient($host, $port, $pass);
|
|
$agent = new LegalGraphAgent($client);
|
|
|
|
$results = match ($action) {
|
|
'cites' => $agent->cites($docId, $limit),
|
|
'cited_by' => $agent->citedBy($docId, $limit),
|
|
'implements' => $agent->implements($docId, $limit),
|
|
'chain' => $agent->chain($docId, $depth),
|
|
};
|
|
} catch (Throwable $e) {
|
|
dbnToolsRespond([
|
|
'ok' => true,
|
|
'action' => $action,
|
|
'doc_id' => $docId,
|
|
'count' => 0,
|
|
'results' => [],
|
|
'warning' => 'Graph backend unavailable: ' . $e->getMessage(),
|
|
]);
|
|
}
|
|
|
|
dbnToolsRespond([
|
|
'ok' => true,
|
|
'action' => $action,
|
|
'doc_id' => $docId,
|
|
'count' => count($results),
|
|
'results' => $results,
|
|
]);
|