diff --git a/Boot.php b/Boot.php index 341e3c4..3889da9 100644 --- a/Boot.php +++ b/Boot.php @@ -71,6 +71,14 @@ class Boot extends ServiceProvider 'agentic' ); + // Register the dedicated brain database connection. + // Falls back to the app's default DB when no BRAIN_DB_* env vars are set. + $brainDb = config('mcp.brain.database'); + + if (is_array($brainDb) && ! empty($brainDb['host'])) { + config(['database.connections.brain' => $brainDb]); + } + $this->app->singleton(\Core\Mod\Agentic\Services\AgenticManager::class); $this->app->singleton(\Core\Mod\Agentic\Services\BrainService::class, function ($app) { diff --git a/Migrations/0001_01_01_000008_create_brain_memories_table.php b/Migrations/0001_01_01_000008_create_brain_memories_table.php index d134840..e6c680d 100644 --- a/Migrations/0001_01_01_000008_create_brain_memories_table.php +++ b/Migrations/0001_01_01_000008_create_brain_memories_table.php @@ -8,20 +8,26 @@ use Illuminate\Support\Facades\Schema; return new class extends Migration { + /** Use the dedicated brain connection so the table lands in the right database. */ + protected $connection = 'brain'; + /** * Create brain_memories table for OpenBrain shared knowledge store. * * Guarded with hasTable() so this migration is idempotent and * can coexist with the consolidated app-level migration. + * + * No FK to workspaces — the brain database may be remote and + * the workspaces table only exists in the app database. */ public function up(): void { - Schema::disableForeignKeyConstraints(); + $schema = Schema::connection($this->getConnection()); - if (! Schema::hasTable('brain_memories')) { - Schema::create('brain_memories', function (Blueprint $table) { + if (! $schema->hasTable('brain_memories')) { + $schema->create('brain_memories', function (Blueprint $table) { $table->uuid('id')->primary(); - $table->foreignId('workspace_id')->constrained()->cascadeOnDelete(); + $table->unsignedBigInteger('workspace_id'); $table->string('agent_id', 64); $table->string('type', 32)->index(); $table->text('content'); @@ -44,14 +50,10 @@ return new class extends Migration ->nullOnDelete(); }); } - - Schema::enableForeignKeyConstraints(); } public function down(): void { - Schema::disableForeignKeyConstraints(); - Schema::dropIfExists('brain_memories'); - Schema::enableForeignKeyConstraints(); + Schema::connection($this->getConnection())->dropIfExists('brain_memories'); } }; diff --git a/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php b/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php new file mode 100644 index 0000000..3f8ee38 --- /dev/null +++ b/Migrations/0001_01_01_000009_drop_brain_memories_workspace_fk.php @@ -0,0 +1,41 @@ +getConnection()); + + if (! $schema->hasTable('brain_memories')) { + return; + } + + $schema->table('brain_memories', function (Blueprint $table) { + try { + $table->dropForeign(['workspace_id']); + } catch (\Throwable) { + // FK doesn't exist — fresh install, nothing to drop. + } + }); + } + + public function down(): void + { + // Not re-adding the FK — it was only valid when brain and app shared a database. + } +}; diff --git a/Models/BrainMemory.php b/Models/BrainMemory.php index c7b7e0e..1181a25 100644 --- a/Models/BrainMemory.php +++ b/Models/BrainMemory.php @@ -51,6 +51,8 @@ class BrainMemory extends Model 'architecture', ]; + protected $connection = 'brain'; + protected $table = 'brain_memories'; protected $fillable = [ diff --git a/Services/BrainService.php b/Services/BrainService.php index 8cc9c87..e4f0d7e 100644 --- a/Services/BrainService.php +++ b/Services/BrainService.php @@ -75,7 +75,7 @@ class BrainService { $vector = $this->embed($attributes['content']); - return DB::transaction(function () use ($attributes, $vector) { + return DB::connection('brain')->transaction(function () use ($attributes, $vector) { $memory = BrainMemory::create($attributes); $payload = $this->buildQdrantPayload($memory->id, [ @@ -156,7 +156,7 @@ class BrainService */ public function forget(string $id): void { - DB::transaction(function () use ($id) { + DB::connection('brain')->transaction(function () use ($id) { BrainMemory::where('id', $id)->delete(); $this->qdrantDelete([$id]); }); diff --git a/config.php b/config.php index b71eac7..787c9ef 100644 --- a/config.php +++ b/config.php @@ -82,10 +82,26 @@ return [ */ 'brain' => [ - 'ollama_url' => env('BRAIN_OLLAMA_URL', 'https://ollama.lthn.lan'), - 'qdrant_url' => env('BRAIN_QDRANT_URL', 'https://qdrant.lthn.lan'), + 'ollama_url' => env('BRAIN_OLLAMA_URL', 'https://ollama.lthn.sh'), + 'qdrant_url' => env('BRAIN_QDRANT_URL', 'https://qdrant.lthn.sh'), 'collection' => env('BRAIN_COLLECTION', 'openbrain'), 'embedding_model' => env('BRAIN_EMBEDDING_MODEL', 'embeddinggemma'), + + // Dedicated database connection for brain_memories. + // Defaults to the app's main database when BRAIN_DB_* env vars are absent. + // Set BRAIN_DB_HOST to a remote MariaDB (e.g. the homelab) to co-locate + // DB rows with their Qdrant vectors. + 'database' => [ + 'driver' => env('BRAIN_DB_DRIVER', env('DB_CONNECTION', 'mariadb')), + 'host' => env('BRAIN_DB_HOST', env('DB_HOST', '127.0.0.1')), + 'port' => env('BRAIN_DB_PORT', env('DB_PORT', '3306')), + 'database' => env('BRAIN_DB_DATABASE', env('DB_DATABASE', 'forge')), + 'username' => env('BRAIN_DB_USERNAME', env('DB_USERNAME', 'forge')), + 'password' => env('BRAIN_DB_PASSWORD', env('DB_PASSWORD', '')), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + ], ], ];