diff --git a/apps/api/prisma/migrations/20260131115600_add_llm_provider_instance/migration.sql b/apps/api/prisma/migrations/20260131115600_add_llm_provider_instance/migration.sql new file mode 100644 index 0000000..ab87a30 --- /dev/null +++ b/apps/api/prisma/migrations/20260131115600_add_llm_provider_instance/migration.sql @@ -0,0 +1,29 @@ +-- CreateTable +CREATE TABLE "llm_provider_instances" ( + "id" UUID NOT NULL, + "provider_type" TEXT NOT NULL, + "display_name" TEXT NOT NULL, + "user_id" UUID, + "config" JSONB NOT NULL, + "is_default" BOOLEAN NOT NULL DEFAULT false, + "is_enabled" BOOLEAN NOT NULL DEFAULT true, + "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ NOT NULL, + + CONSTRAINT "llm_provider_instances_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "llm_provider_instances_user_id_idx" ON "llm_provider_instances"("user_id"); + +-- CreateIndex +CREATE INDEX "llm_provider_instances_provider_type_idx" ON "llm_provider_instances"("provider_type"); + +-- CreateIndex +CREATE INDEX "llm_provider_instances_is_default_idx" ON "llm_provider_instances"("is_default"); + +-- CreateIndex +CREATE INDEX "llm_provider_instances_is_enabled_idx" ON "llm_provider_instances"("is_enabled"); + +-- AddForeignKey +ALTER TABLE "llm_provider_instances" ADD CONSTRAINT "llm_provider_instances_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 4b16c6b..c1fdc16 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -168,6 +168,7 @@ model User { userLayouts UserLayout[] userPreference UserPreference? knowledgeEntryVersions KnowledgeEntryVersion[] @relation("EntryVersionAuthor") + llmProviders LlmProviderInstance[] @relation("UserLlmProviders") @@map("users") } @@ -944,3 +945,28 @@ model Personality { @@index([workspaceId, isActive]) @@map("personalities") } + +// ============================================ +// LLM PROVIDER MODULE +// ============================================ + +model LlmProviderInstance { + id String @id @default(uuid()) @db.Uuid + providerType String @map("provider_type") // "ollama" | "claude" | "openai" + displayName String @map("display_name") + userId String? @map("user_id") @db.Uuid // NULL = system-level, UUID = user-level + config Json // Provider-specific configuration + isDefault Boolean @default(false) @map("is_default") + isEnabled Boolean @default(true) @map("is_enabled") + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz + updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz + + // Relations + user User? @relation("UserLlmProviders", fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([providerType]) + @@index([isDefault]) + @@index([isEnabled]) + @@map("llm_provider_instances") +} diff --git a/docs/scratchpads/128-llm-provider-schema.md b/docs/scratchpads/128-llm-provider-schema.md new file mode 100644 index 0000000..b5a63b3 --- /dev/null +++ b/docs/scratchpads/128-llm-provider-schema.md @@ -0,0 +1,120 @@ +# Issue #128: Add LlmProviderInstance Prisma Schema + +## Objective + +Create database schema for LLM provider instance configuration to support multi-provider LLM backend. + +## Approach + +### Schema Design + +```prisma +model LlmProviderInstance { + id String @id @default(uuid()) + providerType String @map("provider_type") // "ollama" | "claude" | "openai" + displayName String @map("display_name") + userId String? @map("user_id") // NULL = system-level, UUID = user-level + config Json // Provider-specific configuration + isDefault Boolean @default(false) @map("is_default") + isEnabled Boolean @default(true) @map("is_enabled") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([providerType]) + @@index([isDefault]) + @@map("llm_provider_instances") +} +``` + +### Configuration Patterns + +**System-level instances** (user_id = NULL): + +- Available to all users +- Managed by admins only +- Example: Shared Ollama server + +**User-level instances** (user_id = UUID): + +- Private to specific user +- User manages their own API keys +- Example: Personal OpenAI API key + +### Config Field Structure + +```json +{ + "endpoint": "http://localhost:11434", + "timeout": 30000, + "apiKey": "sk-...", // encrypted in production + "organization": "org-...", + "customHeaders": {} +} +``` + +## Progress + +- [x] Read current Prisma schema +- [x] Add LlmProviderInstance model +- [x] Add relation to User model +- [x] Add indexes for performance +- [x] Create migration (20260131115600_add_llm_provider_instance) +- [x] Update Prisma client types (prisma generate) +- [ ] Run migration on dev database (manual step, requires DB connection) +- [ ] Verify migration succeeded (manual step, requires DB connection) + +## Testing + +- Verify model is accessible via Prisma client +- Verify indexes exist in database +- Verify foreign key constraints work +- Test both system and user-level instances + +## Notes + +- Using snake_case for database columns (project convention) +- JSON field for flexible provider configs +- Cascade delete when user is deleted +- `isDefault` allows marking one provider as default per user + +## Implementation Details + +**Model Added:** + +- `LlmProviderInstance` with all required fields +- Foreign key relation to `User` model (nullable for system-level instances) +- Added `llmProviders` relation array to User model + +**Indexes Created:** + +- `user_id` - Fast lookup by user +- `provider_type` - Fast filtering by provider +- `is_default` - Quick default provider lookup +- `is_enabled` - Filter enabled/disabled providers + +**Migration File:** `20260131115600_add_llm_provider_instance` + +- Creates table with proper PostgreSQL types +- Sets up foreign key constraint with CASCADE delete +- Creates all performance indexes + +## Acceptance Criteria Status + +- ✅ Migration created successfully +- ✅ Model accessible via Prisma client (prisma generate succeeded) +- ✅ Indexes defined correctly +- ⏸️ Migration not run (requires database connection) +- ⏸️ No data loss risk (new table, no existing data) + +## Next Steps + +When database is available: + +```bash +pnpm --filter @mosaic/api prisma migrate deploy +# OR for development: +pnpm --filter @mosaic/api prisma migrate dev +```