from datetime import datetime from typing import Literal, Optional import uuid from pydantic import BaseModel, ConfigDict, Field class KBEntryCreate(BaseModel): title: str content: str = Field(min_length=1) source_url: Optional[str] = None class KBEntryResponse(BaseModel): # set it because we need to pass SQLAlchemy ORM object (KnowledgeBase) # into this model for validation model_config = ConfigDict(from_attributes=True) id: uuid.UUID created_at: datetime updated_at: datetime tenant_id: uuid.UUID title: str content: str source_url: Optional[str] = None origin: Literal["manual", "synced"] is_indexed: bool = False class KBEntryUpdate(BaseModel): title: Optional[str] = None content: Optional[str] = Field(default=None, min_length=1) source_url: Optional[str] = None class BulkUploadError(BaseModel): row: int reason: str class BulkUploadResponse(BaseModel): created: int errors: list[BulkUploadError] class WPSyncRequest(BaseModel): site_url: str post_type: str = "post" force: bool = False class WPSyncResponse(BaseModel): synced: int errors: list[str] = [] class WPSyncPreviewEntry(BaseModel): title: str content: str source_url: str warnings: list[str] = [] class WPSyncPreviewResponse(BaseModel): site_url: str entries: list[WPSyncPreviewEntry] errors: list[str] = [] class WPSyncConfirmResponse(BaseModel): job_id: uuid.UUID class WPSyncStatusResponse(BaseModel): job_id: uuid.UUID status: str source_url: str total: int processed: int failed: int error_message: Optional[str] = None created: int = 0 updated: int = 0 skipped: int = 0 class WPSingleSyncRequest(BaseModel): site_url: str source_url: str post_type: str = "post" force: bool = False class WPSingleSyncResponse(BaseModel): action: Literal["created", "updated", "skipped"] entry: KBEntryResponse class KBReindexResponse(BaseModel): task_id: str status: str = "queued" class WPSyncJobControlResponse(BaseModel): job_id: uuid.UUID status: str class WPSyncResumeResponse(BaseModel): original_job_id: uuid.UUID new_job_id: uuid.UUID