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>
143 lines
3.6 KiB
Python
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
|