feat(#27): implement intent classification service
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Implement intent classification for natural language queries in the brain module.

Features:
- Hybrid classification approach: rule-based (fast, <100ms) with optional LLM fallback
- 10 intent types: query_tasks, query_events, query_projects, create_task, create_event, update_task, update_event, briefing, search, unknown
- Entity extraction: dates, times, priorities, statuses, people
- Pattern-based matching with priority system (higher priority = checked first)
- Optional LLM classification for ambiguous queries
- POST /api/brain/classify endpoint

Implementation:
- IntentClassificationService with classify(), classifyWithRules(), classifyWithLlm(), extractEntities()
- Comprehensive regex patterns for common query types
- Entity extraction for dates, times, priorities, statuses, mentions
- Type-safe interfaces for IntentType, IntentClassification, ExtractedEntity, IntentPattern
- ClassifyIntentDto and IntentClassificationResultDto for API validation
- Integrated with existing LlmService (optional dependency)

Testing:
- 60 comprehensive tests covering all intent types
- Edge cases: empty queries, special characters, case sensitivity, multiple whitespace
- Entity extraction tests with position tracking
- LLM fallback tests with error handling
- 100% test coverage
- All tests passing (60/60)
- TDD approach: tests written first

Quality:
- No explicit any types
- Explicit return types on all functions
- No TypeScript errors
- Build successful
- Follows existing code patterns
- Quality Rails compliance: All lint checks pass

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 15:41:10 -06:00
parent 403aba4cd3
commit d7f04d1148
9 changed files with 1257 additions and 14 deletions

View File

@@ -1,6 +1,12 @@
import { Controller, Get, Post, Body, Query, UseGuards } from "@nestjs/common";
import { BrainService } from "./brain.service";
import { BrainQueryDto, BrainContextDto } from "./dto";
import { IntentClassificationService } from "./intent-classification.service";
import {
BrainQueryDto,
BrainContextDto,
ClassifyIntentDto,
IntentClassificationResultDto,
} from "./dto";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { Workspace, Permission, RequirePermission } from "../common/decorators";
@@ -13,7 +19,10 @@ import { Workspace, Permission, RequirePermission } from "../common/decorators";
@Controller("brain")
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
export class BrainController {
constructor(private readonly brainService: BrainService) {}
constructor(
private readonly brainService: BrainService,
private readonly intentClassificationService: IntentClassificationService
) {}
/**
* @description Query workspace entities with flexible filtering options.
@@ -66,4 +75,18 @@ export class BrainController {
const parsedLimit = limit ? Math.min(parseInt(limit, 10) || 20, 100) : 20;
return this.brainService.search(workspaceId, searchTerm || "", parsedLimit);
}
/**
* @description Classify a natural language query into a structured intent.
* Uses hybrid classification: rule-based (fast) with optional LLM fallback.
* @param dto - Classification request with query and optional useLlm flag
* @returns Intent classification with confidence, entities, and method used
* @throws UnauthorizedException if user lacks workspace access
* @throws ForbiddenException if user lacks required permissions
*/
@Post("classify")
@RequirePermission(Permission.WORKSPACE_ANY)
async classifyIntent(@Body() dto: ClassifyIntentDto): Promise<IntentClassificationResultDto> {
return this.intentClassificationService.classify(dto.query, dto.useLlm);
}
}