fox/app/Services/MemoryService.php

89 lines
2.4 KiB
PHP

<?php
namespace App\Services;
use App\Models\Memory;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Pgvector\Laravel\Distance;
use Pgvector\Laravel\Vector;
class MemoryService
{
public function __construct(
protected ?string $ollamaUrl = null,
protected ?string $embedModel = null,
) {
$this->ollamaUrl ??= config('services.ollama.url');
$this->embedModel ??= config('services.ollama.embedding_model');
}
/**
* Erstellt ein Embedding via Ollama und persistiert die Memory.
*/
public function remember(string $content, array $metadata = []): Memory
{
$embedding = $this->embed($content);
return Memory::create([
'content' => $content,
'metadata' => $metadata,
'embedding' => $embedding ? new Vector($embedding) : null,
]);
}
/**
* @return array<int, Memory>
*/
public function recall(string $query, int $limit = 5): array
{
$embedding = $this->embed($query);
if ($embedding === null) {
return Memory::latest()->limit($limit)->get()->all();
}
return Memory::query()
->nearestNeighbors('embedding', new Vector($embedding), Distance::Cosine)
->limit($limit)
->get()
->all();
}
/**
* Erzeugt ein Embedding über die Ollama /api/embeddings Schnittstelle.
*
* @return array<int, float>|null
*/
public function embed(string $text): ?array
{
try {
$response = Http::timeout(30)
->post(rtrim($this->ollamaUrl, '/').'/api/embeddings', [
'model' => $this->embedModel,
'prompt' => $text,
]);
if (! $response->successful()) {
Log::warning('Embedding-Request fehlgeschlagen', ['status' => $response->status()]);
return null;
}
return $response->json('embedding');
} catch (\Throwable $e) {
Log::error('Embedding-Fehler', ['exception' => $e->getMessage()]);
return null;
}
}
public function summarizeRecent(int $limit = 10): string
{
$memories = Memory::latest()->limit($limit)->get();
return $memories
->map(fn (Memory $m) => '- '.str($m->content)->limit(160))
->implode("\n");
}
}