From ac940048d6b8d7dbea81742a01cee2763fc89a35 Mon Sep 17 00:00:00 2001 From: Meghdad Date: Sun, 10 May 2026 17:56:01 +0330 Subject: [PATCH] add icons and welcome page and readme --- .env.example | 2 +- README.md | 125 ++++++++ public/apple-touch-icon.png | Bin 1662 -> 25584 bytes public/favicon.ico | Bin 4286 -> 184150 bytes public/favicon.svg | 9 +- resources/views/partials/head.blade.php | 4 - resources/views/welcome.blade.php | 369 ++++++++++++------------ tests/Feature/ExampleTest.php | 10 +- 8 files changed, 329 insertions(+), 190 deletions(-) create mode 100644 README.md diff --git a/.env.example b/.env.example index 274aae3..9858632 100644 --- a/.env.example +++ b/.env.example @@ -22,7 +22,7 @@ LOG_LEVEL=debug DB_CONNECTION=pgsql DB_HOST=127.0.0.1 -DB_PORT=3306 +DB_PORT=5432 DB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD= diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1f5813 --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# Embedd HUB + +Embedd HUB is a local-first Laravel workbench for exploring text embeddings. It lets users generate vectors, search similar stored phrases, and compare phrase proximity with Ollama and PostgreSQL vector storage. + +## Features + +- Generate embeddings for words, phrases, or multiline text. +- Store vectors in PostgreSQL with vector indexes when using pgvector. +- Search existing embeddings by semantic similarity. +- Compare multiple phrases with a pairwise similarity matrix and ranked pairs. +- Pick embedding models from the live Ollama `/api/tags` response. +- Show clear Ollama online/offline status before running embedding actions. +- Track per-user usage on the dashboard. + +## Stack + +- Laravel 13 +- Livewire 4 single-file components +- Flux UI 2 +- Laravel AI SDK +- PostgreSQL with vector storage +- Ollama for local embedding generation +- Tailwind CSS 4 and Vite +- Pest 4 + +## Requirements + +- PHP 8.4 +- Composer +- Node.js and npm +- PostgreSQL 18 or another PostgreSQL version with vector extension support +- Ollama running locally or on a reachable host + +## Setup + +Install PHP and JavaScript dependencies: + +```bash +composer install +npm install +``` + +Create the environment file and app key: + +```bash +cp .env.example .env +php artisan key:generate +``` + +Configure PostgreSQL in `.env`: + +```dotenv +DB_CONNECTION=pgsql +DB_HOST=127.0.0.1 +DB_PORT=5432 +DB_DATABASE=embedd_hub +DB_USERNAME=postgres +DB_PASSWORD= +``` + +Configure Ollama: + +```dotenv +OLLAMA_URL=http://localhost:11434 +OLLAMA_EMBEDDINGS_DIMENSIONS=768 +``` + +The dimensions must match the embedding model you plan to use. Set this before running migrations so the vector column is created with the correct size. + +Run migrations and build assets: + +```bash +php artisan migrate +npm run build +``` + +## Ollama + +Start Ollama and pull at least one embedding-capable model: + +```bash +ollama serve +ollama pull nomic-embed-text +``` + +Embedd HUB does not use a configured default model. The tool pages call the Ollama API and list the models returned from `/api/tags`. If Ollama cannot return that model list, embedding actions are disabled and the UI shows an offline status. + +## Development + +For Laravel Herd, the app is available at: + +```text +https://embedd-hub.test +``` + +Common commands: + +```bash +npm run dev +php artisan test --compact +vendor/bin/pint --dirty --format agent +``` + +## Project Map + +- `app/Services/EmbeddingWorkbench.php` - embedding generation, search, comparison, and Ollama model discovery. +- `app/Concerns/InteractsWithEmbeddingWorkbench.php` - shared Livewire state for model selection and availability. +- `resources/views/pages/embeddings` - the Embed, Search, and Compare pages. +- `resources/views/dashboard.blade.php` - usage dashboard. +- `database/migrations/*embedding*` - vector storage and usage event tables. +- `public/favicon.svg` - source SVG favicon asset. It is intentionally not referenced in templates. + +## Testing + +Run the full suite: + +```bash +php artisan test --compact +``` + +Run only embedding tool tests: + +```bash +php artisan test --compact tests/Feature/EmbeddingToolsTest.php +``` diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index c2efef6de4482ad4ec770787891fb3ab7f7b1f58..6a41216a73d9034cfa58a3afffc4178b1c337b7a 100644 GIT binary patch literal 25584 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 IM6N<$f;hnhc>n+a diff --git a/public/favicon.ico b/public/favicon.ico index 236fadb94188e9189807c50aa8480b3070a0cc8d..0e4dbffbf53ea10a3f47301fc45669b3dc97825c 100644 GIT binary patch literal 184150 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%0A~XxXagTj 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 - + + + + + + + diff --git a/resources/views/partials/head.blade.php b/resources/views/partials/head.blade.php index 8e9a491..aa6238b 100644 --- a/resources/views/partials/head.blade.php +++ b/resources/views/partials/head.blade.php @@ -5,10 +5,6 @@ {{ filled($title ?? null) ? $title.' - '.config('app.name', 'Laravel') : config('app.name', 'Laravel') }} - - - - @fonts @vite(['resources/css/app.css', 'resources/js/app.js']) diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php index 13545d0..8376301 100644 --- a/resources/views/welcome.blade.php +++ b/resources/views/welcome.blade.php @@ -1,202 +1,209 @@ - + - {{ __('Welcome') }} - {{ config('app.name', 'Laravel') }} - - - - + {{ config('app.name', 'Embedd HUB') }} - Local embedding workbench @fonts - - - + @vite(['resources/css/app.css', 'resources/js/app.js']) - -
- @if (Route::has('login')) -
+ +
+
+
+

+ {{ __('Ollama, pgvector, and Livewire in one local workflow') }} +

+ +

+ {{ config('app.name', 'Embedd HUB') }} +

+ +

+ {{ __('Generate embeddings, search stored phrases, and compare semantic proximity with a local Ollama provider and PostgreSQL vector storage.') }} +

+ +
+ @auth + + {{ __('Open embedding tools') }} - - -
  • - - - - - - - Watch video tutorials at - - Laracasts - - - - - -
  • - - -
    -
    - {{-- Laravel Logo --}} - - - - - - - - - + @else + @if (Route::has('register')) + + {{ __('Start exploring') }} + + @endif - {{-- 13 --}} - - - - - - - - - - - - + @if (Route::has('login')) + + {{ __('Sign in') }} + + @endif + @endauth +
    - - - - - - - - - - - +
    +
    +
    {{ __('Tools') }}
    +
    3
    +
    +
    +
    {{ __('Provider') }}
    +
    {{ __('Local') }}
    +
    +
    +
    {{ __('Storage') }}
    +
    {{ __('Vector') }}
    +
    +
    +
    - - - - - - - - - - - +
    +
    +
    + + + +
    +
    + {{ __('Ollama online') }} +
    +
    - - - - - - - - - - - +
    +
    + + +
    +
    + {{ __('Embed') }} + {{ __('Search') }} + {{ __('Compare') }} +
    + +
    +

    {{ __('Input phrases') }}

    +
    +
    {{ __('fast semantic search') }}
    +
    {{ __('local vector database') }}
    +
    {{ __('embedding comparison') }}
    +
    +
    + +
    +
    + {{ __('Phrase') }} + {{ __('Similarity') }} + {{ __('Distance') }} +
    +
    +
    + {{ __('fast semantic search') }} + 0.982 + 0.018 +
    +
    + {{ __('embedding comparison') }} + 0.741 + 0.259 +
    +
    + {{ __('local vector database') }} + 0.604 + 0.396 +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +

    {{ __('Generate') }}

    +

    {{ __('Inspect raw embeddings') }}

    +

    {{ __('Paste words or phrases, generate vectors, and inspect previews before storing them.') }}

    +
    + +
    +

    {{ __('Search') }}

    +

    {{ __('Find similar stored text') }}

    +

    {{ __('Query your saved embeddings and rank matching phrases by semantic similarity.') }}

    +
    + +
    +

    {{ __('Compare') }}

    +

    {{ __('Read pairwise proximity') }}

    +

    {{ __('Enter multiple phrases and review clear phrase-to-phrase scores in a matrix and sorted pair list.') }}

    +
    +
    +
    - - @if (Route::has('login')) - - @endif diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php index a576279..1a34a3b 100644 --- a/tests/Feature/ExampleTest.php +++ b/tests/Feature/ExampleTest.php @@ -3,5 +3,11 @@ test('returns a successful response', function () { $response = $this->get(route('home')); - $response->assertOk(); -}); \ No newline at end of file + $response + ->assertOk() + ->assertSee('Embedd HUB') + ->assertSee('Local embedding workbench') + ->assertSee('Ollama') + ->assertDontSee('favicon.svg') + ->assertDontSee('favicon.ico'); +});