getSchemaBuilder()->hasTable('project_search_chat_sessions'); } catch (Throwable) { return false; } } /** * @return array|null */ public function activeSession(string $username): ?array { $uuid = session(self::SESSION_KEY); if (! is_string($uuid) || $uuid === '') { return null; } $row = DB::connection('project_search') ->table('project_search_chat_sessions') ->where('session_uuid', $uuid) ->where('username', $username) ->where('status', 'active') ->first(); return $row ? $this->mapSession($row) : null; } /** * @param array $sources * @return array */ public function getOrCreateActiveSession(string $username, array $sources = []): array { if (! $this->isAvailable()) { throw new RuntimeException('Tabel chat project search belum tersedia.'); } $existing = $this->activeSession($username); if ($existing !== null) { return $existing; } $uuid = (string) Str::uuid(); $id = DB::connection('project_search') ->table('project_search_chat_sessions') ->insertGetId([ 'session_uuid' => $uuid, 'username' => $username, 'status' => 'active', 'sources' => json_encode($sources), 'started_at' => now(), 'message_count' => 0, ]); session([self::SESSION_KEY => $uuid]); return [ 'id' => (int) $id, 'session_uuid' => $uuid, 'username' => $username, 'status' => 'active', 'sources' => $sources, 'started_at' => now()->toIso8601String(), 'ended_at' => null, 'message_count' => 0, ]; } /** * @return array> */ public function messagesForActiveSession(string $username): array { $session = $this->activeSession($username); if ($session === null) { return []; } return $this->messagesForSessionId((int) $session['id']); } /** * @return array> */ public function messagesForSessionId(int $sessionId): array { return DB::connection('project_search') ->table('project_search_chat_messages') ->where('session_id', $sessionId) ->orderBy('id') ->get() ->map(fn ($row) => $this->mapMessage($row)) ->all(); } /** * @return array */ public function conversationHistoryForOllama(int $sessionId): array { return DB::connection('project_search') ->table('project_search_chat_messages') ->where('session_id', $sessionId) ->whereIn('role', ['user', 'assistant']) ->orderBy('id') ->get(['role', 'content']) ->map(fn ($row) => [ 'role' => $row->role === 'assistant' ? 'assistant' : 'user', 'content' => (string) $row->content, ]) ->all(); } /** * @return array */ public function addUserMessage(int $sessionId, string $content): array { $message = $this->insertMessage($sessionId, 'user', $content); DB::connection('project_search') ->table('project_search_chat_sessions') ->where('id', $sessionId) ->increment('message_count'); return $message; } /** * @param array> $results * @param array $meta * @return array */ public function addAssistantMessage(int $sessionId, string $content, array $results = [], array $meta = []): array { $message = $this->insertMessage($sessionId, 'assistant', $content, $results, $meta); DB::connection('project_search') ->table('project_search_chat_sessions') ->where('id', $sessionId) ->increment('message_count'); return $message; } public function endActiveSession(string $username): bool { $session = $this->activeSession($username); if ($session === null) { session()->forget(self::SESSION_KEY); return false; } DB::connection('project_search') ->table('project_search_chat_sessions') ->where('id', $session['id']) ->update([ 'status' => 'ended', 'ended_at' => now(), ]); session()->forget(self::SESSION_KEY); return true; } /** * @return array> */ public function recentEndedSessions(string $username, int $limit = 10): array { if (! $this->isAvailable()) { return []; } return DB::connection('project_search') ->table('project_search_chat_sessions') ->where('username', $username) ->where('status', 'ended') ->orderByDesc('ended_at') ->limit($limit) ->get() ->map(fn ($row) => $this->mapSession($row)) ->all(); } /** * @param array> $results * @param array $meta * @return array */ private function insertMessage(int $sessionId, string $role, string $content, array $results = [], array $meta = []): array { $id = DB::connection('project_search') ->table('project_search_chat_messages') ->insertGetId([ 'session_id' => $sessionId, 'role' => $role, 'content' => $content, 'results' => $results !== [] ? json_encode($results) : null, 'meta' => $meta !== [] ? json_encode($meta) : null, 'created_at' => now(), ]); return [ 'id' => (int) $id, 'session_id' => $sessionId, 'role' => $role, 'content' => $content, 'results' => $results, 'meta' => $meta, 'created_at' => now()->toIso8601String(), ]; } /** * @return array */ private function mapSession(object $row): array { $sources = $row->sources ?? null; if (is_string($sources)) { $sources = json_decode($sources, true); } return [ 'id' => (int) $row->id, 'session_uuid' => (string) $row->session_uuid, 'username' => (string) $row->username, 'status' => (string) $row->status, 'sources' => is_array($sources) ? $sources : [], 'started_at' => $row->started_at, 'ended_at' => $row->ended_at, 'message_count' => (int) $row->message_count, ]; } /** * @return array */ private function mapMessage(object $row): array { $results = $row->results ?? null; $meta = $row->meta ?? null; if (is_string($results)) { $results = json_decode($results, true); } if (is_string($meta)) { $meta = json_decode($meta, true); } return [ 'id' => (int) $row->id, 'session_id' => (int) $row->session_id, 'role' => (string) $row->role, 'content' => (string) $row->content, 'results' => is_array($results) ? $results : [], 'meta' => is_array($meta) ? $meta : [], 'created_at' => $row->created_at, ]; } }