Files
resolutionflow/backend/app/schemas/kb_accelerator.py
Michael Chihlas 71ff4a8c35 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>
2026-03-10 20:56:28 -04:00

143 lines
3.6 KiB
Python

"""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