diff --git a/src/RAG/VectorStore/PhpVectorStore.php b/src/RAG/VectorStore/PhpVectorStore.php new file mode 100644 index 00000000..c91a3feb --- /dev/null +++ b/src/RAG/VectorStore/PhpVectorStore.php @@ -0,0 +1,154 @@ +store = $quantized + ? new \PHPVectorStore\QuantizedStore($directory, $dimensions) + : new \PHPVectorStore\VectorStore($directory, $dimensions); + + if (empty($this->stages)) { + $this->stages = self::defaultStages($dimensions); + } + } + + public function addDocument(Document $document): VectorStoreInterface + { + $this->store->set( + $this->collection, + (string) $document->getId(), + $document->getEmbedding(), + $this->documentToMeta($document), + ); + $this->store->flush(); + return $this; + } + + public function addDocuments(array $documents): VectorStoreInterface + { + foreach ($documents as $document) { + $this->store->set( + $this->collection, + (string) $document->getId(), + $document->getEmbedding(), + $this->documentToMeta($document), + ); + } + $this->store->flush(); + return $this; + } + + /** + * @deprecated Use deleteBy() instead. + */ + public function deleteBySource(string $sourceType, string $sourceName): VectorStoreInterface + { + return $this->deleteBy($sourceType, $sourceName); + } + + public function deleteBy(string $sourceType, ?string $sourceName = null): VectorStoreInterface + { + foreach ($this->store->ids($this->collection) as $id) { + $record = $this->store->get($this->collection, $id); + if (!$record) { + continue; + } + + $meta = $record['metadata'] ?? []; + if (($meta['sourceType'] ?? '') !== $sourceType) { + continue; + } + if ($sourceName !== null && ($meta['sourceName'] ?? '') !== $sourceName) { + continue; + } + + $this->store->remove($this->collection, $id); + } + + $this->store->flush(); + return $this; + } + + public function similaritySearch(array $embedding): iterable + { + $results = $this->matryoshka + ? $this->store->matryoshkaSearch($this->collection, $embedding, $this->topK, $this->stages) + : $this->store->search($this->collection, $embedding, $this->topK); + + $documents = []; + + foreach ($results as $result) { + $meta = $result['metadata'] ?? []; + + $document = new Document($meta['content'] ?? ''); + $document->id = $result['id']; + $document->sourceType = $meta['sourceType'] ?? 'manual'; + $document->sourceName = $meta['sourceName'] ?? 'manual'; + $document->metadata = $meta['userMeta'] ?? []; + $document->setScore($result['score']); + + $documents[] = $document; + } + + return $documents; + } + + private function documentToMeta(Document $document): array + { + return [ + 'content' => $document->getContent(), + 'sourceType' => $document->getSourceType(), + 'sourceName' => $document->getSourceName(), + 'userMeta' => $document->metadata, + ]; + } + + private static function defaultStages(int $dim): array + { + if ($dim <= 128) return [$dim]; + if ($dim <= 256) return [128, $dim]; + if ($dim <= 384) return [128, 256, $dim]; + return [128, 384, $dim]; + } +}