ok($this->projectSearch->listSources()); } public function indexStatus(): JsonResponse { if (! $this->projectSearch->isAvailable()) { return $this->fail('Indeks project_search belum tersedia.', 'service_unavailable', 503); } return $this->ok($this->projectSearch->indexStatus()); } public function ask(Request $request): JsonResponse { $validated = $request->validate([ 'query' => ['required', 'string', 'min:3', 'max:500'], 'sources' => ['sometimes', 'array'], 'sources.*' => ['string', 'max:64'], 'limit' => ['sometimes', 'integer', 'min:1', 'max:50'], ]); try { $result = $this->projectSearch->ask( $validated['query'], $validated['sources'] ?? [], (int) ($validated['limit'] ?? config('project_search.default_limit', 20)) ); $actor = $request->attributes->get('api_actor'); $username = $actor instanceof ApiActor ? $actor->identifier : null; $client = $actor instanceof ApiActor ? $actor->type : null; $this->auditLog->log( 'project_search.ask', 'api', $username, $client, 'project_search', $request, 200, [ 'query' => $validated['query'], 'result_count' => $result['meta']['result_count'] ?? 0, 'sources' => $result['meta']['sources'] ?? [], ] ); return $this->ok([ 'answer' => $result['answer'], 'results' => $result['results'], ], $result['meta']); } catch (InvalidArgumentException|RuntimeException $e) { return $this->fail($e->getMessage(), 'validation_error', 422); } catch (Throwable) { return $this->fail('Gagal memproses pencarian proyek.', 'server_error', 500); } } }