Skip to content

Instantly share code, notes, and snippets.

@renalpha
Last active March 19, 2024 01:34
Show Gist options
  • Save renalpha/f37fd7ef7b8e3be904976ebd313d0e2b to your computer and use it in GitHub Desktop.
Save renalpha/f37fd7ef7b8e3be904976ebd313d0e2b to your computer and use it in GitHub Desktop.
RedisVectorConnector.php
<?php
private function indexVector(SplFileInfo $file, string $content): void
{
$metaData = $this->getMetaData($file, $content);
/** @var RedisVector $redisVectorService */
$redisVectorService = app(RedisVector::class);
/** @var \Exdeliver\LaraPal\Connectors\VoyageAIEmbedding $voyage */
$voyage = app(VoyageAIEmbedding::class);
$redisVectorService->store([
'id' => Str::uuid()->toString(),
'embedding' => $voyage->generateEmbedding($content),
'metaData' => json_encode($metaData, JSON_THROW_ON_ERROR),
'file' => $file->getFilename(),
]);
}
<?php
/** @var RedisVector $redisVector */
$redisVector = app(RedisVector::class);
$searchResults = $redisVector->search($embedding, 5);
<?php
namespace Exdeliver\LaraPal\Connectors;
use Redis;
final class RedisVector
{
private ?string $index;
private Redis $redis;
public function __construct(?string $index = null)
{
$this->index = $index ?? config('services.redis.index');
$this->redis = new Redis();
$this->redis->connect(config('database.redis.default.host'), config('database.redis.default.port'));
$this->redis->auth(config('database.redis.default.password'));
$this->redis->select(config('database.redis.default.database'));
}
public function index(): array
{
$keys = $this->redis->keys("{$this->index}:*");
$vectors = [];
foreach ($keys as $key) {
$vector = $this->redis->hGetAll($key);
$vectors[] = $vector;
}
return $vectors;
}
public function store($data): bool|array|Redis
{
$namespace = config('services.redis.namespace');
$key = "{$this->index}:{$namespace}:{$data['id']}";
// Convert the embedding array to a string representation
$embeddingString = implode(',', $data['embedding']);
// Store the embedding string as the value for the 'embedding' field
$this->redis->hSet($key, 'embedding', $embeddingString);
// Store other fields in the hash, if any
foreach ($data as $field => $value) {
if ($field !== 'id' && $field !== 'embedding') {
$this->redis->hSet($key, $field, $value);
}
}
return $this->redis->hGetAll($key);
}
public function query(array $data): Redis|array|bool
{
$namespace = $data['namespace'] ?? config('services.redis.namespace');
$key = "{$this->index}:{$namespace}:{$data['id']}";
return $this->redis->hGetAll($key);
}
public function empty($data = ['deleteAll' => true]): array
{
$namespace = config('services.redis.namespace');
if ($data['deleteAll']) {
$keys = $this->redis->keys("{$this->index}:{$namespace}:*");
$this->redis->del($keys);
} else {
$key = "{$this->index}:{$namespace}:{$data['id']}";
$this->redis->del($key);
}
return ['success' => true];
}
public function search($queryEmbedding, $topK = 5): array
{
$namespace = config('services.redis.namespace');
$keys = $this->redis->keys("{$this->index}:{$namespace}:*");
$similarities = [];
foreach ($keys as $key) {
$embeddingString = $this->redis->hGet($key, 'embedding');
$embedding = explode(',', $embeddingString);
$similarity = $this->cosineSimilarity($queryEmbedding, $embedding);
$similarities[$key] = $similarity;
}
arsort($similarities);
$results = array_slice($similarities, 0, $topK, true);
$searchResults = [];
foreach ($results as $key => $similarity) {
$data = $this->redis->hGetAll($key);
$data['similarity'] = $similarity;
$searchResults[] = $data;
}
return $searchResults;
}
private function cosineSimilarity($embedding1, $embedding2): float|int
{
$dotProduct = 0;
$magnitude1 = 0;
$magnitude2 = 0;
foreach ($embedding1 as $i => $value1) {
$value2 = $embedding2[$i];
$dotProduct += $value1 * $value2;
$magnitude1 += $value1 ** 2;
$magnitude2 += $value2 ** 2;
}
$magnitude1 = sqrt($magnitude1);
$magnitude2 = sqrt($magnitude2);
if ($magnitude1 == 0 || $magnitude2 == 0) {
return 0;
}
return $dotProduct / ($magnitude1 * $magnitude2);
}
}
@renalpha
Copy link
Author

Build with/for laravel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment