{"repo": "blhk0532/nds-old", "files": {"plugins/filaforge/filaforge-deepseek-chat/src/Pages/DeepseekChatPage.php": {"7d95a3a45e2b010bc4a64c24229a073f34424f36": {"t": 1785745789.235925, "n": 13850, "h": "732c5a7eedaf5da4", "c": " */\n public array $conversationList = [];\n\n /** @var array */\n public array $allConversationList = [];\n\n public bool $canViewAllChats = false;\n\n public function mount(): void\n {\n $this->messages = [];\n $this->conversationId = null;\n $this->canViewAllChats = $this->canViewAllChats();\n $this->loadConversations();\n if ($this->canViewAllChats) {\n $this->loadAllConversations();\n }\n }\n\n public function table(Table $table): Table\n {\n // Build a standard Filament table for conversations\n $query = Conversation::query();\n\n if (! $this->canViewAllChats) {\n $userId = (int) auth()->id();\n $query->where('user_id', $userId);\n } else {\n $query->with('user:id,name');\n }\n\n // Select a driver-aware messages_count so we can display/sort/filter by it\n $expr = $this->jsonArrayLengthExpr('messages');\n if ($expr) {\n $query->select($query->getModel()->getTable() . '.*')\n ->selectRaw($expr . ' as messages_count');\n }\n\n $query->latest('updated_at');\n\n return $table\n ->query($query)\n ->defaultPaginationPageOption(10)\n ->columns([\n TextColumn::make('title')\n ->label('Title')\n ->searchable()\n ->sortable()\n ->limit(60)\n ->wrap(false),\n TextColumn::make('user.name')\n ->label('User')\n ->visible($this->canViewAllChats),\n TextColumn::make('messages_count')\n ->label('Messages')\n ->visible((bool) $expr)\n ->sortable(['messages_count'])\n ->formatStateUsing(fn ($state) => (string) ($state ?? 0)),\n TextColumn::make('updated_at')\n ->label('Updated')\n ->dateTime()\n ->sortable(),\n ])\n ->filters([\n SelectFilter::make('user_id')\n ->label('User')\n ->relationship('user', 'name')\n ->visible($this->canViewAllChats),\n TernaryFilter::make('has_replies')\n ->label('Has replies')\n ->queries(\n true: function (Builder $q) use ($expr): Builder {\n return $expr\n ? $q->whereRaw($expr . ' > 1')\n : $q; // Fallback: skip if expr unsupported\n },\n false: function (Builder $q) use ($expr): Builder {\n return $expr\n ? $q->whereRaw($expr . ' <= 1')\n : $q; // Fallback\n }\n ),\n ])\n ->actions([\n Action::make('open')\n ->label('Open Chat')\n ->icon('heroicon-o-eye')\n ->color('gray')\n ->action(function (Conversation $record): void {\n $this->openConversation((int) $record->id);\n // Close the table view and return to chat (forward to Alpine/window listener)\n $this->dispatch('toggle-chats');\n }),\n Action::make('delete')\n ->label('Delete')\n ->icon('heroicon-o-trash')\n ->color('danger')\n ->requiresConfirmation()\n ->action(function (Conversation $record): void {\n if ($this->canViewAllChats) {\n $this->adminDeleteConversation((int) $record->id);\n } else {\n $this->deleteConversation((int) $record->id);\n }\n }),\n ])\n ->striped()\n ->emptyStateHeading('No chats found.');\n }\n\n private function jsonArrayLengthExpr(string $column): ?string\n {\n $driver = DB::connection()->getDriverName();\n\n return match ($driver) {\n 'mysql' => \"JSON_LENGTH($column)\",\n 'pgsql' => \"json_array_length(($column)::json)\",\n 'sqlite' => \"json_array_length($column)\",\n default => null,\n };\n }\n\n protected function loadConversations(): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n $this->conversationList = [];\n\n return;\n }\n\n $this->conversationList = Conversation::query()\n ->where('user_id', $userId)\n ->latest('updated_at')\n ->limit(50)\n ->get(['id', 'title', 'updated_at'])\n ->map(fn ($c) => [\n 'id' => (int) $c->id,\n 'title' => (string) ($c->title ?? 'Untitled'),\n 'updated_at' => (string) $c->updated_at,\n ])\n ->all();\n }\n\n protected function loadAllConversations(): void\n {\n if (! $this->canViewAllChats) {\n $this->allConversationList = [];\n\n return;\n }\n\n $this->allConversationList = Conversation::query()\n ->with('user:id,name')\n ->latest('updated_at')\n ->limit(200)\n ->get(['id', 'title', 'updated_at', 'user_id'])\n ->map(function ($c) {\n return [\n 'id' => (int) $c->id,\n 'title' => (string) ($c->title ?? 'Untitled'),\n 'updated_at' => (string) $c->updated_at,\n 'user_id' => (int) $c->user_id,\n 'user_name' => (string) optional($c->user)->name ?? 'User #' . $c->user_id,\n ];\n })\n ->all();\n }\n\n public function newConversation(): void\n {\n $this->conversationId = null;\n $this->messages = [];\n }\n\n public function openConversation(int $id): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n return;\n }\n\n $query = Conversation::query();\n if (! $this->canViewAllChats) {\n $query->where('user_id', $userId);\n }\n $conv = $query->find($id);\n\n if (! $conv) {\n return;\n }\n\n $this->conversationId = (int) $conv->id;\n $this->messages = (array) $conv->messages;\n }\n\n public function deleteConversation(int $id): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n return;\n }\n\n $query = Conversation::query()->where('user_id', $userId);\n $conv = $query->find($id);\n\n if (! $conv) {\n return;\n }\n\n $conv->delete();\n if ($this->conversationId === $id) {\n $this->newConversation();\n }\n $this->loadConversations();\n if ($this->canViewAllChats) {\n $this->loadAllConversations();\n }\n }\n\n public function adminDeleteConversation(int $id): void\n {\n if (! $this->canViewAllChats) {\n return;\n }\n\n $conv = Conversation::find($id);\n if (! $conv) {\n return;\n }\n\n $conv->delete();\n if ($this->conversationId === $id) {\n $this->newConversation();\n }\n $this->loadConversations();\n $this->loadAllConversations();\n }\n\n public function renameConversation(int $id, string $title): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n return;\n }\n\n $title = trim($title);\n if ($title === '') {\n return;\n }\n\n $conv = Conversation::query()\n ->where('user_id', $userId)\n ->find($id);\n\n if (! $conv) {\n return;\n }\n\n $conv->update([\n 'title' => str($title)->limit(60),\n ]);\n\n $this->loadConversations();\n }\n\n public function saveApiKey(string $deepseek_api_key): void\n {\n $user = auth()->user();\n if (! $user) {\n return;\n }\n $user->forceFill(['deepseek_api_key' => $deepseek_api_key])->save();\n }\n\n public function send(): void\n {\n $content = trim((string) $this->userInput);\n if ($content === '') {\n return;\n }\n\n $this->messages[] = ['role' => 'user', 'content' => $content];\n $this->userInput = '';\n\n // Emit event for frontend typing indicator\n $this->dispatch('messageSent');\n\n $apiKey = auth()->user()?->deepseek_api_key ?: config('deepseek-chat.api_key');\n $base = rtrim((string) config('deepseek-chat.base_url'), '/');\n\n if (! $apiKey) {\n $this->messages[] = ['role' => 'assistant', 'content' => 'Missing DeepSeek API key. Set it in config or .env.'];\n $this->dispatch('messageReceived');\n\n return;\n }\n\n try {\n $response = Http::withToken($apiKey)\n ->timeout((int) config('deepseek-chat.timeout', 60))\n ->post($base . '/v1/chat/completions', [\n 'model' => 'deepseek-chat',\n 'messages' => array_map(fn ($m) => ['role' => $m['role'], 'content' => $m['content']], $this->messages),\n 'stream' => false,\n ]);\n\n if ($response->failed()) {\n $this->messages[] = ['role' => 'assistant', 'content' => 'DeepSeek API error: ' . $response->status() . ' ' . $response->body()];\n $this->dispatch('messageReceived');\n\n return;\n }\n\n $data = $response->json();\n $reply = (string) data_get($data, 'choices.0.message.content', 'No response.');\n $this->messages[] = ['role' => 'assistant', 'content' => $reply];\n\n // Persist conversation\n $user = auth()->user();\n if ($user) {\n if ($this->conversationId) {\n $conv = Conversation::find($this->conversationId);\n if ($conv && $conv->user_id === $user->id) {\n $conv->update(['messages' => $this->messages]);\n }\n } else {\n $conv = Conversation::create([\n 'user_id' => $user->id,\n 'title' => str($content)->limit(60),\n 'messages' => $this->messages,\n ]);\n $this->conversationId = $conv->id;\n }\n $this->loadConversations();\n }\n\n // Emit event for frontend to handle completion\n $this->dispatch('messageReceived');\n } catch (Throwable $e) {\n $this->messages[] = ['role' => 'assistant', 'content' => 'Request failed: ' . $e->getMessage()];\n $this->dispatch('messageReceived');\n }\n }\n\n public static function canAccess(): bool\n {\n $user = auth()->user();\n if (! $user) {\n return false;\n }\n $allowed = (array) config('deepseek-chat.allow_roles', []);\n if (empty($allowed)) {\n return true;\n }\n if (method_exists($user, 'hasAnyRole')) {\n return $user->hasAnyRole($allowed);\n }\n $role = data_get($user, 'role');\n\n return $role ? in_array($role, $allowed, true) : false;\n }\n\n protected function canViewAllChats(): bool\n {\n $user = auth()->user();\n if (! $user) {\n return false;\n }\n\n $adminRoles = (array) config('deepseek-chat.admin_roles', []);\n if (! empty($adminRoles)) {\n if (method_exists($user, 'hasAnyRole')) {\n if ($user->hasAnyRole($adminRoles)) {\n return true;\n }\n }\n $role = data_get($user, 'role');\n if ($role && in_array($role, $adminRoles, true)) {\n return true;\n }\n }\n\n // Fallbacks\n if (property_exists($user, 'is_admin') && (bool) $user->is_admin) {\n return true;\n }\n\n return false;\n }\n\n protected function getHeaderActions(): array\n {\n return [\n // Keep registered but hidden; UI buttons live in the page body\n SetApiKey::make()->hidden(),\n ];\n }\n}\n"}, "a878a1715f84eb9800024606488c76dd6ddafbb3": {"t": 1785756516.0454843, "n": 13850, "h": "732c5a7eedaf5da4", "c": " */\n public array $conversationList = [];\n\n /** @var array */\n public array $allConversationList = [];\n\n public bool $canViewAllChats = false;\n\n public function mount(): void\n {\n $this->messages = [];\n $this->conversationId = null;\n $this->canViewAllChats = $this->canViewAllChats();\n $this->loadConversations();\n if ($this->canViewAllChats) {\n $this->loadAllConversations();\n }\n }\n\n public function table(Table $table): Table\n {\n // Build a standard Filament table for conversations\n $query = Conversation::query();\n\n if (! $this->canViewAllChats) {\n $userId = (int) auth()->id();\n $query->where('user_id', $userId);\n } else {\n $query->with('user:id,name');\n }\n\n // Select a driver-aware messages_count so we can display/sort/filter by it\n $expr = $this->jsonArrayLengthExpr('messages');\n if ($expr) {\n $query->select($query->getModel()->getTable() . '.*')\n ->selectRaw($expr . ' as messages_count');\n }\n\n $query->latest('updated_at');\n\n return $table\n ->query($query)\n ->defaultPaginationPageOption(10)\n ->columns([\n TextColumn::make('title')\n ->label('Title')\n ->searchable()\n ->sortable()\n ->limit(60)\n ->wrap(false),\n TextColumn::make('user.name')\n ->label('User')\n ->visible($this->canViewAllChats),\n TextColumn::make('messages_count')\n ->label('Messages')\n ->visible((bool) $expr)\n ->sortable(['messages_count'])\n ->formatStateUsing(fn ($state) => (string) ($state ?? 0)),\n TextColumn::make('updated_at')\n ->label('Updated')\n ->dateTime()\n ->sortable(),\n ])\n ->filters([\n SelectFilter::make('user_id')\n ->label('User')\n ->relationship('user', 'name')\n ->visible($this->canViewAllChats),\n TernaryFilter::make('has_replies')\n ->label('Has replies')\n ->queries(\n true: function (Builder $q) use ($expr): Builder {\n return $expr\n ? $q->whereRaw($expr . ' > 1')\n : $q; // Fallback: skip if expr unsupported\n },\n false: function (Builder $q) use ($expr): Builder {\n return $expr\n ? $q->whereRaw($expr . ' <= 1')\n : $q; // Fallback\n }\n ),\n ])\n ->actions([\n Action::make('open')\n ->label('Open Chat')\n ->icon('heroicon-o-eye')\n ->color('gray')\n ->action(function (Conversation $record): void {\n $this->openConversation((int) $record->id);\n // Close the table view and return to chat (forward to Alpine/window listener)\n $this->dispatch('toggle-chats');\n }),\n Action::make('delete')\n ->label('Delete')\n ->icon('heroicon-o-trash')\n ->color('danger')\n ->requiresConfirmation()\n ->action(function (Conversation $record): void {\n if ($this->canViewAllChats) {\n $this->adminDeleteConversation((int) $record->id);\n } else {\n $this->deleteConversation((int) $record->id);\n }\n }),\n ])\n ->striped()\n ->emptyStateHeading('No chats found.');\n }\n\n private function jsonArrayLengthExpr(string $column): ?string\n {\n $driver = DB::connection()->getDriverName();\n\n return match ($driver) {\n 'mysql' => \"JSON_LENGTH($column)\",\n 'pgsql' => \"json_array_length(($column)::json)\",\n 'sqlite' => \"json_array_length($column)\",\n default => null,\n };\n }\n\n protected function loadConversations(): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n $this->conversationList = [];\n\n return;\n }\n\n $this->conversationList = Conversation::query()\n ->where('user_id', $userId)\n ->latest('updated_at')\n ->limit(50)\n ->get(['id', 'title', 'updated_at'])\n ->map(fn ($c) => [\n 'id' => (int) $c->id,\n 'title' => (string) ($c->title ?? 'Untitled'),\n 'updated_at' => (string) $c->updated_at,\n ])\n ->all();\n }\n\n protected function loadAllConversations(): void\n {\n if (! $this->canViewAllChats) {\n $this->allConversationList = [];\n\n return;\n }\n\n $this->allConversationList = Conversation::query()\n ->with('user:id,name')\n ->latest('updated_at')\n ->limit(200)\n ->get(['id', 'title', 'updated_at', 'user_id'])\n ->map(function ($c) {\n return [\n 'id' => (int) $c->id,\n 'title' => (string) ($c->title ?? 'Untitled'),\n 'updated_at' => (string) $c->updated_at,\n 'user_id' => (int) $c->user_id,\n 'user_name' => (string) optional($c->user)->name ?? 'User #' . $c->user_id,\n ];\n })\n ->all();\n }\n\n public function newConversation(): void\n {\n $this->conversationId = null;\n $this->messages = [];\n }\n\n public function openConversation(int $id): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n return;\n }\n\n $query = Conversation::query();\n if (! $this->canViewAllChats) {\n $query->where('user_id', $userId);\n }\n $conv = $query->find($id);\n\n if (! $conv) {\n return;\n }\n\n $this->conversationId = (int) $conv->id;\n $this->messages = (array) $conv->messages;\n }\n\n public function deleteConversation(int $id): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n return;\n }\n\n $query = Conversation::query()->where('user_id', $userId);\n $conv = $query->find($id);\n\n if (! $conv) {\n return;\n }\n\n $conv->delete();\n if ($this->conversationId === $id) {\n $this->newConversation();\n }\n $this->loadConversations();\n if ($this->canViewAllChats) {\n $this->loadAllConversations();\n }\n }\n\n public function adminDeleteConversation(int $id): void\n {\n if (! $this->canViewAllChats) {\n return;\n }\n\n $conv = Conversation::find($id);\n if (! $conv) {\n return;\n }\n\n $conv->delete();\n if ($this->conversationId === $id) {\n $this->newConversation();\n }\n $this->loadConversations();\n $this->loadAllConversations();\n }\n\n public function renameConversation(int $id, string $title): void\n {\n $userId = (int) auth()->id();\n if (! $userId) {\n return;\n }\n\n $title = trim($title);\n if ($title === '') {\n return;\n }\n\n $conv = Conversation::query()\n ->where('user_id', $userId)\n ->find($id);\n\n if (! $conv) {\n return;\n }\n\n $conv->update([\n 'title' => str($title)->limit(60),\n ]);\n\n $this->loadConversations();\n }\n\n public function saveApiKey(string $deepseek_api_key): void\n {\n $user = auth()->user();\n if (! $user) {\n return;\n }\n $user->forceFill(['deepseek_api_key' => $deepseek_api_key])->save();\n }\n\n public function send(): void\n {\n $content = trim((string) $this->userInput);\n if ($content === '') {\n return;\n }\n\n $this->messages[] = ['role' => 'user', 'content' => $content];\n $this->userInput = '';\n\n // Emit event for frontend typing indicator\n $this->dispatch('messageSent');\n\n $apiKey = auth()->user()?->deepseek_api_key ?: config('deepseek-chat.api_key');\n $base = rtrim((string) config('deepseek-chat.base_url'), '/');\n\n if (! $apiKey) {\n $this->messages[] = ['role' => 'assistant', 'content' => 'Missing DeepSeek API key. Set it in config or .env.'];\n $this->dispatch('messageReceived');\n\n return;\n }\n\n try {\n $response = Http::withToken($apiKey)\n ->timeout((int) config('deepseek-chat.timeout', 60))\n ->post($base . '/v1/chat/completions', [\n 'model' => 'deepseek-chat',\n 'messages' => array_map(fn ($m) => ['role' => $m['role'], 'content' => $m['content']], $this->messages),\n 'stream' => false,\n ]);\n\n if ($response->failed()) {\n $this->messages[] = ['role' => 'assistant', 'content' => 'DeepSeek API error: ' . $response->status() . ' ' . $response->body()];\n $this->dispatch('messageReceived');\n\n return;\n }\n\n $data = $response->json();\n $reply = (string) data_get($data, 'choices.0.message.content', 'No response.');\n $this->messages[] = ['role' => 'assistant', 'content' => $reply];\n\n // Persist conversation\n $user = auth()->user();\n if ($user) {\n if ($this->conversationId) {\n $conv = Conversation::find($this->conversationId);\n if ($conv && $conv->user_id === $user->id) {\n $conv->update(['messages' => $this->messages]);\n }\n } else {\n $conv = Conversation::create([\n 'user_id' => $user->id,\n 'title' => str($content)->limit(60),\n 'messages' => $this->messages,\n ]);\n $this->conversationId = $conv->id;\n }\n $this->loadConversations();\n }\n\n // Emit event for frontend to handle completion\n $this->dispatch('messageReceived');\n } catch (Throwable $e) {\n $this->messages[] = ['role' => 'assistant', 'content' => 'Request failed: ' . $e->getMessage()];\n $this->dispatch('messageReceived');\n }\n }\n\n public static function canAccess(): bool\n {\n $user = auth()->user();\n if (! $user) {\n return false;\n }\n $allowed = (array) config('deepseek-chat.allow_roles', []);\n if (empty($allowed)) {\n return true;\n }\n if (method_exists($user, 'hasAnyRole')) {\n return $user->hasAnyRole($allowed);\n }\n $role = data_get($user, 'role');\n\n return $role ? in_array($role, $allowed, true) : false;\n }\n\n protected function canViewAllChats(): bool\n {\n $user = auth()->user();\n if (! $user) {\n return false;\n }\n\n $adminRoles = (array) config('deepseek-chat.admin_roles', []);\n if (! empty($adminRoles)) {\n if (method_exists($user, 'hasAnyRole')) {\n if ($user->hasAnyRole($adminRoles)) {\n return true;\n }\n }\n $role = data_get($user, 'role');\n if ($role && in_array($role, $adminRoles, true)) {\n return true;\n }\n }\n\n // Fallbacks\n if (property_exists($user, 'is_admin') && (bool) $user->is_admin) {\n return true;\n }\n\n return false;\n }\n\n protected function getHeaderActions(): array\n {\n return [\n // Keep registered but hidden; UI buttons live in the page body\n SetApiKey::make()->hidden(),\n ];\n }\n}\n"}}}}