improve interacting with ollama api and models
This commit is contained in:
@@ -5,27 +5,39 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<flux:badge :color="$ollamaAvailable ? 'emerald' : 'red'" :icon="$ollamaAvailable ? 'check-circle' : 'x-circle'">
|
||||
{{ $ollamaAvailable ? __('Ollama online') : __('Ollama offline') }}
|
||||
</flux:badge>
|
||||
<flux:badge icon="cpu-chip">{{ $providerSummary }}</flux:badge>
|
||||
<flux:badge icon="circle-stack">{{ __(':count stored', ['count' => $storedCount]) }}</flux:badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<div class="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-end">
|
||||
<div class="w-full max-w-sm">
|
||||
<flux:select wire:model="embeddingModel" :label="__('Embedding model')">
|
||||
@foreach ($modelOptions as $option)
|
||||
<flux:select wire:model="embeddingModel" :label="__('Ollama model')" :disabled="! $ollamaAvailable">
|
||||
@forelse ($modelOptions as $option)
|
||||
<flux:select.option value="{{ $option['name'] }}">
|
||||
{{ $option['name'] }} ({{ $option['dimensions'] }}d)
|
||||
</flux:select.option>
|
||||
@endforeach
|
||||
@empty
|
||||
<flux:select.option value="" disabled>{{ __('No models returned') }}</flux:select.option>
|
||||
@endforelse
|
||||
</flux:select>
|
||||
<flux:error name="embeddingModel" />
|
||||
</div>
|
||||
|
||||
<flux:button type="button" variant="filled" icon="arrow-path" wire:click="refreshOllama" class="w-full lg:w-auto data-loading:opacity-60">
|
||||
<span wire:loading.remove wire:target="refreshOllama">{{ __('Refresh') }}</span>
|
||||
<span wire:loading wire:target="refreshOllama">{{ __('Checking') }}</span>
|
||||
</flux:button>
|
||||
</div>
|
||||
|
||||
@if (! $ollamaAvailable)
|
||||
<flux:callout variant="danger" icon="x-circle" :heading="__('Ollama is unavailable')" :text="$ollamaStatusMessage" />
|
||||
@endif
|
||||
|
||||
@if (! $databaseReady)
|
||||
<div class="rounded-lg border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-700 dark:bg-amber-950 dark:text-amber-100">
|
||||
{{ __('Embedding storage is waiting for the pgvector migration.') }}
|
||||
</div>
|
||||
<flux:callout variant="warning" icon="exclamation-circle" :heading="__('Embedding storage is unavailable')" :text="__('Embedding storage is waiting for the pgvector migration.')" />
|
||||
@endif
|
||||
</header>
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Embeddings\RecordEmbeddingUsageEvent;
|
||||
use App\Concerns\InteractsWithEmbeddingWorkbench;
|
||||
use App\Models\EmbeddingEntry;
|
||||
use App\Services\EmbeddingVector;
|
||||
use App\Services\EmbeddingWorkbench;
|
||||
use Flux\Flux;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
new #[Title('Embedding comparison')] class extends Component
|
||||
{
|
||||
use InteractsWithEmbeddingWorkbench;
|
||||
|
||||
/** @var array<int, string> */
|
||||
public array $comparisonPhrases = ['', '', ''];
|
||||
|
||||
public string $embeddingModel = '';
|
||||
|
||||
/** @var array<int, array{name: string, dimensions: int, default: bool}> */
|
||||
public array $modelOptions = [];
|
||||
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
public array $comparisonEntries = [];
|
||||
|
||||
@@ -32,12 +29,6 @@ new #[Title('Embedding comparison')] class extends Component
|
||||
/** @var array<string, mixed>|null */
|
||||
public ?array $closestPair = null;
|
||||
|
||||
public string $providerSummary = '';
|
||||
|
||||
public int $storedCount = 0;
|
||||
|
||||
public bool $databaseReady = true;
|
||||
|
||||
public function mount(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$this->initializeWorkbench($workbench);
|
||||
@@ -45,6 +36,10 @@ new #[Title('Embedding comparison')] class extends Component
|
||||
|
||||
public function comparePhrases(EmbeddingWorkbench $workbench, RecordEmbeddingUsageEvent $recordUsageEvent): void
|
||||
{
|
||||
if (! $this->ensureWorkbenchAvailable($workbench, 'comparisonPhrases')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'comparisonPhrases' => ['required', 'array', 'min:2', 'max:12'],
|
||||
'comparisonPhrases.*' => ['nullable', 'string', 'max:1000'],
|
||||
@@ -59,12 +54,6 @@ new #[Title('Embedding comparison')] class extends Component
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->databaseReady) {
|
||||
$this->addError('comparisonPhrases', __('The embedding table is not ready.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$comparison = $workbench->compare($phrases, $this->embeddingModel);
|
||||
|
||||
@@ -125,36 +114,6 @@ new #[Title('Embedding comparison')] class extends Component
|
||||
$this->resetComparisonResults();
|
||||
}
|
||||
|
||||
protected function initializeWorkbench(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$this->modelOptions = $workbench->modelOptions();
|
||||
$this->embeddingModel = $this->embeddingModel ?: $workbench->modelName();
|
||||
|
||||
$this->refreshSummary($workbench);
|
||||
}
|
||||
|
||||
protected function refreshSummary(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$provider = $workbench->providerName();
|
||||
$model = $workbench->modelName($this->embeddingModel);
|
||||
$dimensions = $workbench->dimensions($model);
|
||||
|
||||
$this->providerSummary = "{$provider} / {$model} / {$dimensions}d";
|
||||
$this->databaseReady = Schema::hasTable('embedding_entries');
|
||||
|
||||
if (! $this->databaseReady) {
|
||||
$this->storedCount = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->storedCount = EmbeddingEntry::query()
|
||||
->where('provider', $provider)
|
||||
->where('model', $model)
|
||||
->where('embedding_dimensions', $dimensions)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
@@ -230,7 +189,7 @@ new #[Title('Embedding comparison')] class extends Component
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<flux:button type="submit" variant="primary" icon="arrows-right-left" :disabled="! $databaseReady" class="data-loading:opacity-60">
|
||||
<flux:button type="submit" variant="primary" icon="arrows-right-left" :disabled="! $databaseReady || ! $ollamaAvailable" class="data-loading:opacity-60">
|
||||
<span wire:loading.remove wire:target="comparePhrases">{{ __('Compare') }}</span>
|
||||
<span wire:loading wire:target="comparePhrases">{{ __('Comparing') }}</span>
|
||||
</flux:button>
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Embeddings\RecordEmbeddingUsageEvent;
|
||||
use App\Concerns\InteractsWithEmbeddingWorkbench;
|
||||
use App\Models\EmbeddingEntry;
|
||||
use App\Services\EmbeddingVector;
|
||||
use App\Services\EmbeddingWorkbench;
|
||||
use Flux\Flux;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
new #[Title('Embed')] class extends Component
|
||||
{
|
||||
use InteractsWithEmbeddingWorkbench;
|
||||
|
||||
public string $embeddingInput = '';
|
||||
|
||||
public string $embeddingModel = '';
|
||||
|
||||
/** @var array<int, array{name: string, dimensions: int, default: bool}> */
|
||||
public array $modelOptions = [];
|
||||
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
public array $embeddingResults = [];
|
||||
|
||||
public string $providerSummary = '';
|
||||
|
||||
public int $storedCount = 0;
|
||||
|
||||
public bool $databaseReady = true;
|
||||
|
||||
public function mount(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$this->initializeWorkbench($workbench);
|
||||
@@ -34,6 +25,10 @@ new #[Title('Embed')] class extends Component
|
||||
|
||||
public function generateEmbeddings(EmbeddingWorkbench $workbench, RecordEmbeddingUsageEvent $recordUsageEvent): void
|
||||
{
|
||||
if (! $this->ensureWorkbenchAvailable($workbench, 'embeddingInput')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'embeddingInput' => ['required', 'string', 'max:12000'],
|
||||
'embeddingModel' => ['required', 'string'],
|
||||
@@ -47,12 +42,6 @@ new #[Title('Embed')] class extends Component
|
||||
return;
|
||||
}
|
||||
|
||||
if (! $this->databaseReady) {
|
||||
$this->addError('embeddingInput', __('The embedding table is not ready.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$entries = $workbench->embed($phrases, $this->embeddingModel);
|
||||
$tokens = (int) $entries->sum(fn (EmbeddingEntry $entry): int => (int) ($entry->tokens ?? 0));
|
||||
@@ -78,37 +67,6 @@ new #[Title('Embed')] class extends Component
|
||||
$this->addError('embeddingModel', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function initializeWorkbench(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$this->modelOptions = $workbench->modelOptions();
|
||||
$this->embeddingModel = $this->embeddingModel ?: $workbench->modelName();
|
||||
|
||||
$this->refreshSummary($workbench);
|
||||
}
|
||||
|
||||
protected function refreshSummary(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$provider = $workbench->providerName();
|
||||
$model = $workbench->modelName($this->embeddingModel);
|
||||
$dimensions = $workbench->dimensions($model);
|
||||
|
||||
$this->providerSummary = "{$provider} / {$model} / {$dimensions}d";
|
||||
$this->databaseReady = Schema::hasTable('embedding_entries');
|
||||
|
||||
if (! $this->databaseReady) {
|
||||
$this->storedCount = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->storedCount = EmbeddingEntry::query()
|
||||
->where('provider', $provider)
|
||||
->where('model', $model)
|
||||
->where('embedding_dimensions', $dimensions)
|
||||
->count();
|
||||
}
|
||||
|
||||
};
|
||||
?>
|
||||
|
||||
@@ -124,7 +82,7 @@ new #[Title('Embed')] class extends Component
|
||||
</flux:field>
|
||||
|
||||
<div>
|
||||
<flux:button type="submit" variant="primary" icon="sparkles" :disabled="! $databaseReady" class="data-loading:opacity-60">
|
||||
<flux:button type="submit" variant="primary" icon="sparkles" :disabled="! $databaseReady || ! $ollamaAvailable" class="data-loading:opacity-60">
|
||||
<span wire:loading.remove wire:target="generateEmbeddings">{{ __('Generate') }}</span>
|
||||
<span wire:loading wire:target="generateEmbeddings">{{ __('Generating') }}</span>
|
||||
</flux:button>
|
||||
|
||||
@@ -1,34 +1,24 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Embeddings\RecordEmbeddingUsageEvent;
|
||||
use App\Models\EmbeddingEntry;
|
||||
use App\Concerns\InteractsWithEmbeddingWorkbench;
|
||||
use App\Services\EmbeddingWorkbench;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
new #[Title('Embedding search')] class extends Component
|
||||
{
|
||||
use InteractsWithEmbeddingWorkbench;
|
||||
|
||||
public string $similarityQuery = '';
|
||||
|
||||
public int $similarityLimit = 10;
|
||||
|
||||
public string $minimumSimilarity = '0.30';
|
||||
|
||||
public string $embeddingModel = '';
|
||||
|
||||
/** @var array<int, array{name: string, dimensions: int, default: bool}> */
|
||||
public array $modelOptions = [];
|
||||
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
public array $similarityResults = [];
|
||||
|
||||
public string $providerSummary = '';
|
||||
|
||||
public int $storedCount = 0;
|
||||
|
||||
public bool $databaseReady = true;
|
||||
|
||||
public function mount(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$this->initializeWorkbench($workbench);
|
||||
@@ -36,6 +26,10 @@ new #[Title('Embedding search')] class extends Component
|
||||
|
||||
public function searchSimilar(EmbeddingWorkbench $workbench, RecordEmbeddingUsageEvent $recordUsageEvent): void
|
||||
{
|
||||
if (! $this->ensureWorkbenchAvailable($workbench, 'similarityQuery')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->validate([
|
||||
'similarityQuery' => ['required', 'string', 'max:1000'],
|
||||
'similarityLimit' => ['required', 'integer', 'min:1', 'max:50'],
|
||||
@@ -43,12 +37,6 @@ new #[Title('Embedding search')] class extends Component
|
||||
'embeddingModel' => ['required', 'string'],
|
||||
]);
|
||||
|
||||
if (! $this->databaseReady) {
|
||||
$this->addError('similarityQuery', __('The embedding table is not ready.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->similarityResults = $workbench
|
||||
->search($this->similarityQuery, $this->similarityLimit, (float) $this->minimumSimilarity, $this->embeddingModel)
|
||||
@@ -75,37 +63,6 @@ new #[Title('Embedding search')] class extends Component
|
||||
$this->addError('embeddingModel', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected function initializeWorkbench(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$this->modelOptions = $workbench->modelOptions();
|
||||
$this->embeddingModel = $this->embeddingModel ?: $workbench->modelName();
|
||||
|
||||
$this->refreshSummary($workbench);
|
||||
}
|
||||
|
||||
protected function refreshSummary(EmbeddingWorkbench $workbench): void
|
||||
{
|
||||
$provider = $workbench->providerName();
|
||||
$model = $workbench->modelName($this->embeddingModel);
|
||||
$dimensions = $workbench->dimensions($model);
|
||||
|
||||
$this->providerSummary = "{$provider} / {$model} / {$dimensions}d";
|
||||
$this->databaseReady = Schema::hasTable('embedding_entries');
|
||||
|
||||
if (! $this->databaseReady) {
|
||||
$this->storedCount = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->storedCount = EmbeddingEntry::query()
|
||||
->where('provider', $provider)
|
||||
->where('model', $model)
|
||||
->where('embedding_dimensions', $dimensions)
|
||||
->count();
|
||||
}
|
||||
|
||||
};
|
||||
?>
|
||||
|
||||
@@ -126,7 +83,7 @@ new #[Title('Embedding search')] class extends Component
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<flux:button type="submit" variant="primary" icon="magnifying-glass" :disabled="! $databaseReady" class="data-loading:opacity-60">
|
||||
<flux:button type="submit" variant="primary" icon="magnifying-glass" :disabled="! $databaseReady || ! $ollamaAvailable" class="data-loading:opacity-60">
|
||||
<span wire:loading.remove wire:target="searchSimilar">{{ __('Search') }}</span>
|
||||
<span wire:loading wire:target="searchSimilar">{{ __('Searching') }}</span>
|
||||
</flux:button>
|
||||
|
||||
Reference in New Issue
Block a user