feat: KB Accelerator — convert KB articles into interactive flows
Full-stack implementation of the KB Accelerator feature that converts static MSP knowledge base articles into interactive troubleshooting and procedural flows using AI. Backend: - Migrations 054/055: kb_imports, kb_import_nodes tables + plan_limits KB columns - SQLAlchemy models with relationships and self-referential node hierarchy - Text extraction service (txt, paste, docx with structural metadata) - AI conversion service with MSP-specialist prompts for both flow types - 8 API endpoints: upload, get, list, convert, edit node, commit, delete, quota - Tier-gated access via plan_limits (free: 3 lifetime, pro/team: unlimited) - 8 integration tests covering upload, get/list, quota, commit, delete Frontend: - TypeScript types and API client for all KB Accelerator endpoints - Multi-step wizard page: upload → processing → review → success - Upload screen with paste/file tabs, drag-drop, target type selector - Two-panel review screen with source highlighting and node cards - Per-node actions: approve, edit, regenerate, insert, delete - Confidence color indicators (green/amber/red) - Sidebar navigation with Sparkles icon - Code-split lazy-loaded route at /kb-accelerator Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
142
backend/app/schemas/kb_accelerator.py
Normal file
142
backend/app/schemas/kb_accelerator.py
Normal file
@@ -0,0 +1,142 @@
|
||||
"""Pydantic schemas for KB Accelerator."""
|
||||
from typing import Any, Literal, Optional
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# ── Requests ──
|
||||
|
||||
|
||||
class KBUploadTextRequest(BaseModel):
|
||||
"""Upload KB article via text paste."""
|
||||
|
||||
content: str = Field(..., min_length=10, max_length=500_000)
|
||||
title: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
target_type: Optional[Literal["troubleshooting", "procedural"]] = Field(
|
||||
None, description="Target flow type. If omitted, AI decides."
|
||||
)
|
||||
|
||||
|
||||
class KBNodeEditRequest(BaseModel):
|
||||
"""Edit a specific KB import node during review."""
|
||||
|
||||
operation: Literal[
|
||||
"approve", "reject", "edit", "delete", "regenerate", "insert_after"
|
||||
]
|
||||
content: Optional[dict[str, Any]] = Field(
|
||||
None, description="Updated node content (required for 'edit' and 'insert_after')"
|
||||
)
|
||||
guidance: Optional[str] = Field(
|
||||
None,
|
||||
max_length=2000,
|
||||
description="User guidance for 'regenerate' operation",
|
||||
)
|
||||
|
||||
|
||||
class KBCommitRequest(BaseModel):
|
||||
"""Optional overrides when committing a KB import to the flow library."""
|
||||
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
description: Optional[str] = Field(None, max_length=2000)
|
||||
category_id: Optional[UUID] = None
|
||||
|
||||
|
||||
# ── Responses ──
|
||||
|
||||
|
||||
class KBImportNodeResponse(BaseModel):
|
||||
"""A single generated node in a KB import."""
|
||||
|
||||
id: UUID
|
||||
kb_import_id: UUID
|
||||
node_order: int
|
||||
node_type: str
|
||||
content: dict[str, Any]
|
||||
parent_node_id: Optional[UUID] = None
|
||||
source_excerpt: Optional[str] = None
|
||||
confidence_score: float
|
||||
user_edited: bool
|
||||
user_approved: bool
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class KBUploadResponse(BaseModel):
|
||||
"""Response after uploading a KB article."""
|
||||
|
||||
id: UUID
|
||||
status: str
|
||||
source_format: str
|
||||
|
||||
|
||||
class KBImportResponse(BaseModel):
|
||||
"""Full KB import detail with nodes."""
|
||||
|
||||
id: UUID
|
||||
account_id: UUID
|
||||
created_by: UUID
|
||||
source_filename: Optional[str] = None
|
||||
source_format: str
|
||||
source_text: str
|
||||
source_metadata: Optional[dict[str, Any]] = None
|
||||
target_type: str
|
||||
status: str
|
||||
confidence_avg: Optional[float] = None
|
||||
error_message: Optional[str] = None
|
||||
processing_time_ms: Optional[int] = None
|
||||
ai_tokens_input: Optional[int] = None
|
||||
ai_tokens_output: Optional[int] = None
|
||||
tree_id: Optional[UUID] = None
|
||||
nodes: list[KBImportNodeResponse] = []
|
||||
created_at: str
|
||||
updated_at: str
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class KBImportSummary(BaseModel):
|
||||
"""Lightweight import item for list view."""
|
||||
|
||||
id: UUID
|
||||
source_filename: Optional[str] = None
|
||||
source_format: str
|
||||
target_type: str
|
||||
status: str
|
||||
confidence_avg: Optional[float] = None
|
||||
node_count: int = 0
|
||||
created_at: str
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class KBImportListResponse(BaseModel):
|
||||
"""Paginated list of KB imports."""
|
||||
|
||||
items: list[KBImportSummary]
|
||||
total: int
|
||||
skip: int
|
||||
limit: int
|
||||
|
||||
|
||||
class KBCommitResponse(BaseModel):
|
||||
"""Response after committing a KB import to the flow library."""
|
||||
|
||||
tree_id: UUID
|
||||
import_id: UUID
|
||||
tree_type: str
|
||||
|
||||
|
||||
class KBQuotaResponse(BaseModel):
|
||||
"""Current KB Accelerator entitlements and usage for the user's account."""
|
||||
|
||||
plan: str
|
||||
kb_accelerator_enabled: bool
|
||||
lifetime_conversions_used: int
|
||||
lifetime_conversions_limit: Optional[int] = None
|
||||
allowed_formats: list[str]
|
||||
detailed_analysis: bool
|
||||
conversational_refinement: bool
|
||||
step_library_matching: bool
|
||||
history_limit: Optional[int] = None
|
||||
can_convert: bool
|
||||
Reference in New Issue
Block a user