Implements the complete AI flow builder feature using a guided 4-stage wizard (Foundation → Scaffold → Branch Detail → Review & Assemble). AI assists at bounded points using Claude Haiku for cost-efficient structured JSON generation (~$0.01-0.03/flow). Backend: new models (ai_conversations, ai_usage), Alembic migration, quota enforcement with billing anchor, Anthropic API integration with prompt caching, tree validation, conversation CRUD with 24h TTL, APScheduler cleanup job, 5 API endpoints, Pydantic schemas. Frontend: TypeScript types, API client, Zustand store for wizard state, 7 components (modal, step indicator, foundation form, branch selector, branch detail view, tree preview, quota display), MyTreesPage integration with "Build with AI" button (hidden when AI not configured). Tests: 14 validator unit tests + 11 endpoint integration tests with mocked Anthropic (zero real API spend). All 25 tests passing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
21 lines
1.0 KiB
Python
21 lines
1.0 KiB
Python
from sqlalchemy import String, Integer, Boolean
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from app.core.database import Base
|
|
|
|
|
|
class PlanLimits(Base):
|
|
__tablename__ = "plan_limits"
|
|
|
|
plan: Mapped[str] = mapped_column(String(50), primary_key=True)
|
|
max_trees: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
max_sessions_per_month: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
max_users: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
custom_branding: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
priority_support: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
|
export_formats: Mapped[list] = mapped_column(JSONB, nullable=False, default=lambda: ["markdown", "text"])
|
|
|
|
# AI Flow Builder limits
|
|
max_ai_builds_per_month: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
max_ai_builds_per_24h: Mapped[int | None] = mapped_column(Integer, nullable=True)
|