AI provider management

* OpenAI-compatible provider adapter
* OpenAI support
* LM Studio support
* Ollama OpenAI-compatible support
* Encrypted API-key storage
* Default provider and model selection
* Provider connection testing
* Reusable prompt library
* Prompt variables and templates
* Prompt version field
* AI execution history
* Token-usage storage
* Duration and failure tracking
* Manual knowledge sources
* Knowledge-source chunking
* Knowledge indexing status
* Authenticated AI REST endpoint
* AI administration dashboard
* AI permissions
* Module registration and boot test
This commit is contained in:
Mark Gregory
2026-07-24 13:01:56 +01:00
parent bf37548faa
commit 5dbb04e3ad
26 changed files with 377 additions and 1 deletions
@@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void
{
Schema::create('ai_providers', function (Blueprint $table) {
$table->id(); $table->string('name'); $table->string('driver', 40)->default('openai_compatible');
$table->string('base_url')->nullable(); $table->text('api_key')->nullable(); $table->string('default_model')->nullable();
$table->json('models')->nullable(); $table->json('options')->nullable(); $table->boolean('is_active')->default(true)->index();
$table->boolean('is_default')->default(false)->index(); $table->timestamp('last_tested_at')->nullable();
$table->string('last_test_status', 20)->nullable(); $table->text('last_test_message')->nullable(); $table->timestamps();
});
Schema::create('ai_prompts', function (Blueprint $table) {
$table->id(); $table->string('name'); $table->string('slug')->unique(); $table->string('purpose')->nullable()->index();
$table->text('system_prompt')->nullable(); $table->longText('user_template'); $table->json('variables')->nullable();
$table->unsignedInteger('version')->default(1); $table->boolean('is_active')->default(true)->index();
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete(); $table->timestamps();
});
Schema::create('ai_executions', function (Blueprint $table) {
$table->id(); $table->uuid('execution_id')->unique(); $table->foreignId('ai_provider_id')->nullable()->constrained('ai_providers')->nullOnDelete();
$table->foreignId('ai_prompt_id')->nullable()->constrained('ai_prompts')->nullOnDelete(); $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
$table->string('model')->nullable(); $table->string('purpose')->nullable()->index(); $table->longText('input')->nullable();
$table->longText('output')->nullable(); $table->json('context')->nullable(); $table->json('usage')->nullable();
$table->string('status', 20)->default('pending')->index(); $table->text('error')->nullable();
$table->unsignedInteger('duration_ms')->nullable(); $table->timestamp('completed_at')->nullable(); $table->timestamps();
});
Schema::create('ai_knowledge_sources', function (Blueprint $table) {
$table->id(); $table->string('name'); $table->string('source_kind', 40)->default('manual')->index();
$table->nullableMorphs('source'); $table->longText('content')->nullable(); $table->string('checksum', 64)->nullable()->index();
$table->json('metadata')->nullable(); $table->string('status', 20)->default('pending')->index();
$table->unsignedInteger('chunk_count')->default(0); $table->timestamp('indexed_at')->nullable(); $table->timestamps();
});
Schema::create('ai_knowledge_chunks', function (Blueprint $table) {
$table->id(); $table->foreignId('ai_knowledge_source_id')->constrained('ai_knowledge_sources')->cascadeOnDelete();
$table->unsignedInteger('position'); $table->longText('content'); $table->json('metadata')->nullable(); $table->timestamps();
$table->unique(['ai_knowledge_source_id','position']);
});
}
public function down(): void
{
Schema::dropIfExists('ai_knowledge_chunks'); Schema::dropIfExists('ai_knowledge_sources');
Schema::dropIfExists('ai_executions'); Schema::dropIfExists('ai_prompts'); Schema::dropIfExists('ai_providers');
}
};