feat(#82): implement Personality Module

- Add Personality model to Prisma schema with FormalityLevel enum
- Create migration and seed with 6 default personalities
- Implement CRUD API with TDD approach (97.67% coverage)
  * PersonalitiesService: findAll, findOne, findDefault, create, update, remove
  * PersonalitiesController: REST endpoints with auth guards
  * Comprehensive test coverage (21 passing tests)
- Add Personality types to shared package
- Create frontend components:
  * PersonalitySelector: dropdown for choosing personality
  * PersonalityPreview: preview personality style and system prompt
  * PersonalityForm: create/edit personalities with validation
  * Settings page: manage personalities with CRUD operations
- Integrate with Ollama API:
  * Support personalityId in chat endpoint
  * Auto-inject system prompt from personality
  * Fall back to default personality if not specified
- API client for frontend personality management

All tests passing with 97.67% backend coverage (exceeds 85% requirement)
This commit is contained in:
Jason Woltje
2026-01-29 17:57:54 -06:00
parent 95833fb4ea
commit 5dd46c85af
43 changed files with 4782 additions and 2 deletions

View File

@@ -15,6 +15,7 @@ import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { Workspace, Permission, RequirePermission } from "../common/decorators";
import { CurrentUser } from "../auth/decorators/current-user.decorator";
import { LinkSyncService } from "./services/link-sync.service";
/**
* Controller for knowledge entry endpoints
@@ -24,7 +25,10 @@ import { CurrentUser } from "../auth/decorators/current-user.decorator";
@Controller("knowledge/entries")
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
export class KnowledgeController {
constructor(private readonly knowledgeService: KnowledgeService) {}
constructor(
private readonly knowledgeService: KnowledgeService,
private readonly linkSync: LinkSyncService
) {}
/**
* GET /api/knowledge/entries
@@ -100,4 +104,32 @@ export class KnowledgeController {
await this.knowledgeService.remove(workspaceId, slug, user.id);
return { message: "Entry archived successfully" };
}
/**
* GET /api/knowledge/entries/:slug/backlinks
* Get all backlinks for an entry
* Requires: Any workspace member
*/
@Get(":slug/backlinks")
@RequirePermission(Permission.WORKSPACE_ANY)
async getBacklinks(
@Workspace() workspaceId: string,
@Param("slug") slug: string
) {
// First find the entry to get its ID
const entry = await this.knowledgeService.findOne(workspaceId, slug);
// Get backlinks
const backlinks = await this.linkSync.getBacklinks(entry.id);
return {
entry: {
id: entry.id,
slug: entry.slug,
title: entry.title,
},
backlinks,
count: backlinks.length,
};
}
}