| from __future__ import annotations |
|
|
| from typing import Any, Literal |
|
|
| from pydantic import BaseModel, Field |
|
|
|
|
| Platform = Literal["tiktok", "instagram_reels", "facebook_shorts", "youtube_shorts"] |
| ApprovalState = Literal["draft", "needs_review", "approved", "rejected", "published"] |
| JobState = Literal["queued", "planned", "running", "rendering", "ready", "scheduled", "published", "failed", "cancelled"] |
| WorkspaceRole = Literal["owner", "admin", "editor", "viewer", "publisher"] |
| IntegrationKind = Literal["n8n", "zapier", "make", "slack", "telegram", "email", "google_drive", "dropbox", "airtable", "notion", "rss", "shopify", "webhook"] |
|
|
|
|
| class BrandKit(BaseModel): |
| id: str = "" |
| name: str |
| logo_url: str | None = None |
| primary_color: str = "#111111" |
| secondary_color: str = "#ffffff" |
| accent_color: str = "#16a34a" |
| font_family: str = "DejaVu Sans" |
| watermark_enabled: bool = True |
| intro_asset: str | None = None |
| outro_asset: str | None = None |
| voice: str | None = None |
| caption_style: str = "bold_word_highlight" |
| safe_zone: dict[str, int] = Field(default_factory=lambda: {"top": 160, "bottom": 280, "left": 64, "right": 64}) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class CampaignCreate(BaseModel): |
| name: str |
| topic: str |
| niche: str = "" |
| source_url: str | None = None |
| source_asset: str | None = None |
| source_text: str | None = None |
| brand_kit_id: str | None = None |
| platforms: list[Platform] = Field(default_factory=lambda: ["tiktok", "instagram_reels", "facebook_shorts"]) |
| quantity: int = Field(default=5, ge=1, le=100) |
| language: str = "en" |
| tone: str = "clear, punchy, high-retention" |
| target_audience: str = "" |
| publish_window: dict[str, Any] = Field(default_factory=dict) |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| workspace_id: str | None = None |
|
|
|
|
| class Campaign(BaseModel): |
| id: str |
| name: str |
| topic: str |
| niche: str = "" |
| source_url: str | None = None |
| source_asset: str | None = None |
| source_text: str | None = None |
| brand_kit_id: str | None = None |
| platforms: list[Platform] |
| quantity: int |
| language: str |
| tone: str |
| target_audience: str = "" |
| publish_window: dict[str, Any] = Field(default_factory=dict) |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| workspace_id: str | None = None |
| status: str = "draft" |
| created_at: str |
| updated_at: str |
|
|
|
|
| class Variant(BaseModel): |
| id: str |
| campaign_id: str |
| platform: Platform |
| title: str |
| hook: str |
| script: str |
| caption: str |
| hashtags: list[str] |
| cta: str |
| template: str |
| creative_style: str |
| duration_seconds: int |
| aspect_ratio: str = "9:16" |
| safe_zone: dict[str, int] = Field(default_factory=dict) |
| render_payload: dict[str, Any] = Field(default_factory=dict) |
| approval_state: ApprovalState = "draft" |
| job_state: JobState = "planned" |
| scheduled_at: str | None = None |
| publish_targets: list[Platform] = Field(default_factory=list) |
| analytics: dict[str, Any] = Field(default_factory=dict) |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| revision: int = 1 |
| created_at: str |
| updated_at: str |
|
|
|
|
| class ApprovalDecision(BaseModel): |
| state: ApprovalState |
| reviewer: str = "system" |
| notes: str = "" |
|
|
|
|
| class CalendarEntry(BaseModel): |
| id: str |
| variant_id: str |
| platform: Platform |
| scheduled_at: str |
| status: str = "scheduled" |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str |
| updated_at: str |
|
|
|
|
| class AnalyticsEvent(BaseModel): |
| variant_id: str |
| platform: Platform |
| views: int = 0 |
| likes: int = 0 |
| comments: int = 0 |
| shares: int = 0 |
| saves: int = 0 |
| watch_time_seconds: float = 0 |
| completion_rate: float = 0 |
| source: str = "manual" |
| captured_at: str | None = None |
|
|
|
|
| class PublishRequest(BaseModel): |
| variant_id: str |
| platforms: list[Platform] = Field(default_factory=list) |
| scheduled_at: str | None = None |
| draft: bool = True |
| webhook_url: str | None = None |
|
|
|
|
| class AutomationJob(BaseModel): |
| id: str |
| kind: str |
| state: JobState = "queued" |
| payload: dict[str, Any] = Field(default_factory=dict) |
| result: dict[str, Any] = Field(default_factory=dict) |
| logs: list[str] = Field(default_factory=list) |
| attempts: int = 0 |
| max_attempts: int = 3 |
| lease_id: str | None = None |
| idempotency_key: str | None = None |
| scheduled_at: str | None = None |
| created_at: str |
| updated_at: str |
|
|
|
|
| class Workspace(BaseModel): |
| id: str = "" |
| name: str |
| client_name: str = "" |
| plan: str = "standard" |
| monthly_quota: int = 1000 |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class WorkspaceMember(BaseModel): |
| id: str = "" |
| workspace_id: str |
| email: str |
| role: WorkspaceRole = "viewer" |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class SocialAccount(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| platform: Platform |
| handle: str |
| display_name: str = "" |
| daily_post_limit: int = 3 |
| warmup_enabled: bool = True |
| active: bool = True |
| auth_status: str = "not_connected" |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class CrossPostRule(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| name: str |
| source_platform: Platform = "tiktok" |
| target_platforms: list[Platform] = Field(default_factory=lambda: ["instagram_reels", "facebook_shorts"]) |
| delay_minutes: int = 30 |
| repost_winners_after_days: int = 14 |
| min_viral_score: float = 20 |
| active: bool = True |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class TrendSignal(BaseModel): |
| id: str = "" |
| platform: Platform |
| keyword: str |
| niche: str = "" |
| score: float = 0 |
| velocity: float = 0 |
| source: str = "manual" |
| examples: list[str] = Field(default_factory=list) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class CompetitorProfile(BaseModel): |
| id: str = "" |
| platform: Platform |
| handle: str |
| niche: str = "" |
| url: str | None = None |
| posting_frequency: str = "" |
| observed_hooks: list[str] = Field(default_factory=list) |
| observed_formats: list[str] = Field(default_factory=list) |
| notes: str = "" |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class Idea(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| source: str = "manual" |
| text: str |
| url: str | None = None |
| niche: str = "" |
| tags: list[str] = Field(default_factory=list) |
| priority: int = Field(default=3, ge=1, le=5) |
| status: str = "new" |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class ContentCalendarPlanRequest(BaseModel): |
| workspace_id: str | None = None |
| niche: str |
| days: int = Field(default=30, ge=1, le=90) |
| posts_per_day: int = Field(default=2, ge=1, le=10) |
| platforms: list[Platform] = Field(default_factory=lambda: ["tiktok", "instagram_reels", "facebook_shorts"]) |
| start_at: str | None = None |
|
|
|
|
| class ComplianceCheckRequest(BaseModel): |
| variant_id: str | None = None |
| platform: Platform |
| title: str = "" |
| caption: str = "" |
| hashtags: list[str] = Field(default_factory=list) |
| duration_seconds: int = 30 |
| has_disclosure: bool = False |
| metadata: dict[str, Any] = Field(default_factory=dict) |
|
|
|
|
| class Integration(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| kind: IntegrationKind |
| name: str |
| endpoint_url: str | None = None |
| active: bool = True |
| secret_ref: str | None = None |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class UsageEvent(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| kind: str |
| quantity: int = 1 |
| cost_units: float = 0 |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class ReportRequest(BaseModel): |
| workspace_id: str | None = None |
| campaign_id: str | None = None |
| format: str = "json" |
| include_recommendations: bool = True |
|
|
|
|
| class VariantUpdate(BaseModel): |
| title: str | None = None |
| hook: str | None = None |
| script: str | None = None |
| caption: str | None = None |
| hashtags: list[str] | None = None |
| cta: str | None = None |
| template: str | None = None |
| creative_style: str | None = None |
| duration_seconds: int | None = Field(default=None, ge=3, le=600) |
| safe_zone: dict[str, int] | None = None |
| render_payload: dict[str, Any] | None = None |
| metadata: dict[str, Any] | None = None |
|
|
|
|
| class RepurposeRequest(BaseModel): |
| name: str |
| transcript: str = "" |
| source_url: str | None = None |
| source_asset: str | None = None |
| niche: str = "" |
| target_audience: str = "" |
| quantity: int = Field(default=5, ge=1, le=30) |
| platforms: list[Platform] = Field(default_factory=lambda: ["tiktok", "instagram_reels", "youtube_shorts"]) |
| workspace_id: str | None = None |
| brand_kit_id: str | None = None |
| use_ai: bool = False |
|
|
|
|
| class ProviderGenerateRequest(BaseModel): |
| provider: Literal["openai", "gemini", "openrouter"] | None = None |
| model: str | None = None |
| instructions: str = "" |
|
|
|
|
| class AssetRecord(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| filename: str |
| path: str |
| kind: Literal["video", "audio", "image", "subtitle", "font", "document", "other"] = "other" |
| content_type: str = "application/octet-stream" |
| size_bytes: int = 0 |
| checksum: str = "" |
| source_url: str | None = None |
| license: str = "unknown" |
| attribution: str = "" |
| tags: list[str] = Field(default_factory=list) |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class Experiment(BaseModel): |
| id: str = "" |
| campaign_id: str |
| name: str = "Campaign experiment" |
| variable: Literal["hook", "caption", "thumbnail", "duration", "template"] = "hook" |
| variant_ids: list[str] = Field(default_factory=list) |
| success_metric: str = "viral_score" |
| minimum_views: int = 1000 |
| confidence_target: float = Field(default=0.9, ge=0.5, le=0.999) |
| status: Literal["draft", "running", "completed"] = "draft" |
| winner_variant_id: str | None = None |
| results: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class ReviewLink(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| variant_ids: list[str] = Field(default_factory=list) |
| token_hash: str = "" |
| expires_at: str |
| created_by: str = "system" |
| active: bool = True |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class ReviewComment(BaseModel): |
| id: str = "" |
| review_id: str = "" |
| variant_id: str |
| author: str = "Client" |
| body: str |
| timestamp_seconds: float | None = Field(default=None, ge=0) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class AutomationRule(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| name: str |
| event: Literal["variant_approved", "render_completed", "job_failed", "analytics_winner", "publish_due"] |
| conditions: dict[str, Any] = Field(default_factory=dict) |
| actions: list[dict[str, Any]] = Field(default_factory=list) |
| active: bool = True |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class Notification(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| channel: str = "in_app" |
| subject: str |
| message: str |
| status: Literal["queued", "sent", "failed"] = "queued" |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|
|
|
| class AuditEvent(BaseModel): |
| id: str = "" |
| workspace_id: str | None = None |
| actor: str = "system" |
| action: str |
| resource_type: str |
| resource_id: str |
| metadata: dict[str, Any] = Field(default_factory=dict) |
| created_at: str = "" |
| updated_at: str = "" |
|
|