feat: add dedicated brain database connection for remote MariaDB
Brain memories can now be stored in a separate database, co-located with Qdrant vectors on the homelab. Defaults to the app's main DB when no BRAIN_DB_* env vars are set (zero-config for existing installs). - Add brain.database config with BRAIN_DB_* env var support - Register 'brain' database connection in Boot.php - Set BrainMemory model to use 'brain' connection - Update BrainService transactions to use brain connection - Update migration to use brain connection, drop workspace FK (cross-database FKs not supported) - Add migration to drop FK on existing installs - Update default URLs from *.lthn.lan to *.lthn.sh Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
ad0ee04b83
commit
331796c1da
6 changed files with 82 additions and 13 deletions
8
Boot.php
8
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) {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Drop the workspace_id foreign key from brain_memories.
|
||||
*
|
||||
* The brain database may be remote (co-located with Qdrant on the homelab),
|
||||
* so cross-database FK constraints to the app's workspaces table are not
|
||||
* possible. The column stays as a plain indexed integer.
|
||||
*/
|
||||
return new class extends Migration
|
||||
{
|
||||
protected $connection = 'brain';
|
||||
|
||||
public function up(): void
|
||||
{
|
||||
$schema = Schema::connection($this->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.
|
||||
}
|
||||
};
|
||||
|
|
@ -51,6 +51,8 @@ class BrainMemory extends Model
|
|||
'architecture',
|
||||
];
|
||||
|
||||
protected $connection = 'brain';
|
||||
|
||||
protected $table = 'brain_memories';
|
||||
|
||||
protected $fillable = [
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
});
|
||||
|
|
|
|||
20
config.php
20
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' => '',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
|
|
|||
Reference in a new issue