feat: KB Accelerator — convert KB articles into interactive flows #104

Merged
chihlasm merged 20 commits from feat/kb-accelerator into main 2026-03-12 21:29:08 +00:00
4 changed files with 49 additions and 4 deletions
Showing only changes of commit 03390ed59f - Show all commits

View File

@@ -0,0 +1,44 @@
"""Add md to kb_allowed_formats defaults
Revision ID: 056
Revises: 055
Create Date: 2026-03-12
"""
from alembic import op
import sqlalchemy as sa
revision = "056"
down_revision = "055"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Update server default for new rows
op.alter_column(
"plan_limits",
"kb_allowed_formats",
server_default=sa.text("'[\"txt\",\"paste\",\"md\"]'::jsonb"),
)
# Add "md" to existing rows that have the old default ["txt","paste"]
op.execute(
"""
UPDATE plan_limits
SET kb_allowed_formats = kb_allowed_formats || '["md"]'::jsonb
WHERE NOT kb_allowed_formats @> '"md"'::jsonb
"""
)
def downgrade() -> None:
op.alter_column(
"plan_limits",
"kb_allowed_formats",
server_default=sa.text("'[\"txt\",\"paste\"]'::jsonb"),
)
op.execute(
"""
UPDATE plan_limits
SET kb_allowed_formats = kb_allowed_formats - 'md'
"""
)

View File

@@ -57,6 +57,7 @@ MAX_UPLOAD_SIZE = 10 * 1024 * 1024
ALLOWED_EXTENSIONS = {
"txt": ["text/plain"],
"md": ["text/markdown", "text/plain"],
"docx": ["application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
}
@@ -64,7 +65,6 @@ ALLOWED_EXTENSIONS = {
PHASE2_EXTENSIONS = {
"pdf": ["application/pdf"],
"html": ["text/html"],
"md": ["text/markdown", "text/plain"],
}
@@ -110,7 +110,7 @@ async def _check_lifetime_limit(user: User, limits: PlanLimits, db: AsyncSession
async def _check_format_allowed(source_format: str, limits: PlanLimits) -> None:
allowed = limits.kb_allowed_formats or ["txt", "paste"]
allowed = limits.kb_allowed_formats or ["txt", "paste", "md"]
if source_format not in allowed:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
@@ -205,7 +205,7 @@ async def get_quota(
kb_accelerator_enabled=False,
lifetime_conversions_used=committed_count,
lifetime_conversions_limit=0,
allowed_formats=["txt", "paste"],
allowed_formats=["txt", "paste", "md"],
detailed_analysis=False,
conversational_refinement=False,
step_library_matching=False,

View File

@@ -159,6 +159,7 @@ def _extract_docx(content_bytes: bytes) -> ExtractResult:
# Registry of format handlers — extend for Phase 2
FORMAT_HANDLERS: dict[str, ExtractHandler] = {
"txt": _extract_txt,
"md": _extract_txt,
"paste": _extract_paste,
"docx": _extract_docx,
}

View File

@@ -23,7 +23,7 @@ class PlanLimits(Base):
kb_accelerator_enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("false"))
kb_max_lifetime_conversions: Mapped[int | None] = mapped_column(Integer, nullable=True)
kb_batch_max_size: Mapped[int | None] = mapped_column(Integer, nullable=True)
kb_allowed_formats: Mapped[list] = mapped_column(JSONB, nullable=False, default=lambda: ["txt", "paste"], server_default=text("'[\"txt\",\"paste\"]'::jsonb"))
kb_allowed_formats: Mapped[list] = mapped_column(JSONB, nullable=False, default=lambda: ["txt", "paste", "md"], server_default=text("'[\"txt\",\"paste\",\"md\"]'::jsonb"))
kb_detailed_analysis: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("false"))
kb_conversational_refinement: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("false"))
kb_step_library_matching: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("false"))