chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
from fastapi import HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class APIErrorModel(BaseModel):
|
||||
status_code: int
|
||||
detail: str
|
||||
|
||||
@classmethod
|
||||
def from_exception(cls, e: Exception) -> "APIErrorModel":
|
||||
if isinstance(e, HTTPException):
|
||||
return APIErrorModel(status_code=e.status_code, detail=e.detail)
|
||||
return APIErrorModel(status_code=500, detail=str(e))
|
||||
@@ -0,0 +1,53 @@
|
||||
import uuid
|
||||
from typing import Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class ChatAttachment(BaseModel):
|
||||
type: Literal["document"] = "document"
|
||||
name: str = Field(min_length=1, max_length=255)
|
||||
file_path: str = Field(min_length=1, max_length=4000)
|
||||
mime_type: Optional[str] = Field(default=None, max_length=255)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChatMessageRequest(BaseModel):
|
||||
presentation_id: uuid.UUID
|
||||
message: str = Field(min_length=1, max_length=8000)
|
||||
conversation_id: Optional[uuid.UUID] = None
|
||||
attachments: list[ChatAttachment] = Field(default_factory=list, max_length=8)
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChatMessageResponse(BaseModel):
|
||||
conversation_id: uuid.UUID
|
||||
response: str
|
||||
tool_calls: list[str] = Field(default_factory=list)
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChatHistoryMessageItem(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
created_at: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChatHistoryResponse(BaseModel):
|
||||
presentation_id: uuid.UUID
|
||||
conversation_id: uuid.UUID
|
||||
messages: list[ChatHistoryMessageItem]
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
class ChatConversationListItem(BaseModel):
|
||||
conversation_id: uuid.UUID
|
||||
updated_at: Optional[str] = None
|
||||
last_message_preview: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
@@ -0,0 +1,6 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class DecomposedFileInfo(BaseModel):
|
||||
name: str
|
||||
file_path: str
|
||||
@@ -0,0 +1,13 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
from models.presentation_outline_model import SlideOutlineModel
|
||||
|
||||
|
||||
class DocumentChunk(BaseModel):
|
||||
heading: str
|
||||
content: str
|
||||
heading_index: int
|
||||
score: float
|
||||
|
||||
def to_slide_outline(self) -> SlideOutlineModel:
|
||||
return SlideOutlineModel(content=f"{self.heading}\n{self.content}")
|
||||
@@ -0,0 +1,46 @@
|
||||
from typing import List, Literal, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from enums.tone import Tone
|
||||
from enums.verbosity import Verbosity
|
||||
|
||||
|
||||
class GeneratePresentationRequest(BaseModel):
|
||||
content: str = Field(..., description="The content for generating the presentation")
|
||||
slides_markdown: Optional[List[str]] = Field(
|
||||
default=None, description="The markdown for the slides"
|
||||
)
|
||||
instructions: Optional[str] = Field(
|
||||
default=None, description="The instruction for generating the presentation"
|
||||
)
|
||||
tone: Tone = Field(default=Tone.DEFAULT, description="The tone to use for the text")
|
||||
verbosity: Verbosity = Field(
|
||||
default=Verbosity.STANDARD, description="How verbose the presentation should be"
|
||||
)
|
||||
web_search: bool = Field(default=False, description="Whether to enable web search")
|
||||
n_slides: Optional[int] = Field(
|
||||
default=None,
|
||||
description="Number of slides to generate. If omitted, model auto-detects slide count.",
|
||||
)
|
||||
language: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Language for the presentation. If omitted, model auto-detects language.",
|
||||
)
|
||||
template: str = Field(
|
||||
default="general", description="Template to use for the presentation"
|
||||
)
|
||||
include_table_of_contents: bool = Field(
|
||||
default=False, description="Whether to include a table of contents"
|
||||
)
|
||||
include_title_slide: bool = Field(
|
||||
default=True, description="Whether to include a title slide"
|
||||
)
|
||||
files: Optional[List[str]] = Field(
|
||||
default=None, description="Files to use for the presentation"
|
||||
)
|
||||
export_as: Literal["pptx", "pdf"] = Field(
|
||||
default="pptx", description="Export format"
|
||||
)
|
||||
trigger_webhook: bool = Field(
|
||||
default=False, description="Whether to trigger subscribed webhooks"
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ImagePrompt(BaseModel):
|
||||
prompt: str
|
||||
theme_prompt: Optional[str] = None
|
||||
|
||||
def get_image_prompt(self, with_theme: bool = False) -> str:
|
||||
return f"{self.prompt}, {self.theme_prompt}" if with_theme else self.prompt
|
||||
@@ -0,0 +1,14 @@
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class DictGuide(BaseModel):
|
||||
key: str
|
||||
|
||||
|
||||
class ListGuide(BaseModel):
|
||||
index: int
|
||||
|
||||
|
||||
class JsonPathGuide(BaseModel):
|
||||
guides: List[DictGuide | ListGuide]
|
||||
@@ -0,0 +1,8 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class OllamaModelMetadata(BaseModel):
|
||||
label: str
|
||||
value: str
|
||||
size: str
|
||||
tested: bool = True
|
||||
@@ -0,0 +1,12 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class OllamaModelStatus(BaseModel):
|
||||
name: str
|
||||
parameters: Optional[str] = None
|
||||
size: Optional[int] = None
|
||||
downloaded: Optional[int] = None
|
||||
status: str
|
||||
done: bool
|
||||
error: Optional[str] = None
|
||||
@@ -0,0 +1,11 @@
|
||||
from pydantic import BaseModel
|
||||
import uuid
|
||||
|
||||
|
||||
class PresentationAndPath(BaseModel):
|
||||
presentation_id: uuid.UUID
|
||||
path: str
|
||||
|
||||
|
||||
class PresentationPathAndEditPath(PresentationAndPath):
|
||||
edit_path: str
|
||||
@@ -0,0 +1,14 @@
|
||||
from typing import List, Literal
|
||||
from pydantic import BaseModel
|
||||
import uuid
|
||||
|
||||
|
||||
class SlideContentUpdate(BaseModel):
|
||||
index: int
|
||||
content: dict
|
||||
|
||||
|
||||
class EditPresentationRequest(BaseModel):
|
||||
presentation_id: uuid.UUID
|
||||
slides: List[SlideContentUpdate]
|
||||
export_as: Literal["pptx", "pdf"] = "pptx"
|
||||
@@ -0,0 +1,5 @@
|
||||
"""Re-export layout models defined in `templates.presentation_layout`."""
|
||||
|
||||
from templates.presentation_layout import PresentationLayoutModel, SlideLayoutModel
|
||||
|
||||
__all__ = ["PresentationLayoutModel", "SlideLayoutModel"]
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from constants.presentation import MAX_NUMBER_OF_SLIDES, MAX_OUTLINE_CONTENT_WORDS
|
||||
from utils.outline_limits import normalize_outline_content
|
||||
|
||||
|
||||
class SlideOutlineModel(BaseModel):
|
||||
content: str = Field(
|
||||
...,
|
||||
description=f"Markdown content for the slide. Maximum {MAX_OUTLINE_CONTENT_WORDS} words.",
|
||||
)
|
||||
|
||||
@field_validator("content", mode="before")
|
||||
@classmethod
|
||||
def limit_content_words(cls, value):
|
||||
return normalize_outline_content(value)
|
||||
|
||||
|
||||
class PresentationOutlineModel(BaseModel):
|
||||
slides: List[SlideOutlineModel] = Field(
|
||||
description="List of slide outlines",
|
||||
max_length=MAX_NUMBER_OF_SLIDES,
|
||||
)
|
||||
|
||||
def to_string(self):
|
||||
message = ""
|
||||
for i, slide in enumerate(self.slides):
|
||||
message += f"## Slide {i+1}:\n"
|
||||
message += f" - Content: {slide} \n"
|
||||
return message
|
||||
@@ -0,0 +1,6 @@
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class PresentationStructureModel(BaseModel):
|
||||
slides: List[int] = Field(description="List of slide layout indexes")
|
||||
@@ -0,0 +1,29 @@
|
||||
from typing import Any, List, Optional
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from models.sql.slide import SlideModel
|
||||
|
||||
|
||||
class PresentationWithSlides(BaseModel):
|
||||
id: uuid.UUID
|
||||
version: Optional[str] = None
|
||||
content: str
|
||||
n_slides: int
|
||||
language: str
|
||||
title: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
tone: Optional[str] = None
|
||||
verbosity: Optional[str] = None
|
||||
slides: List[SlideModel]
|
||||
layout: Optional[dict] = None
|
||||
structure: Optional[dict] = None
|
||||
theme: Optional[dict] = None
|
||||
fonts: Optional[Any] = None
|
||||
|
||||
|
||||
class PresentationDetailWithSlides(PresentationWithSlides):
|
||||
merged_components: Optional[Any] = None
|
||||
@@ -0,0 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SlideLayoutIndex(BaseModel):
|
||||
index: int
|
||||
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
import secrets
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
|
||||
class AsyncPresentationGenerationTaskModel(SQLModel, table=True):
|
||||
|
||||
__tablename__ = "async_presentation_generation_tasks"
|
||||
|
||||
id: str = Field(
|
||||
default_factory=lambda: f"task-{secrets.token_hex(32)}", primary_key=True
|
||||
)
|
||||
status: str
|
||||
message: Optional[str] = None
|
||||
error: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
created_at: datetime = Field(default_factory=datetime.now)
|
||||
updated_at: datetime = Field(default_factory=datetime.now)
|
||||
data: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
@@ -0,0 +1,37 @@
|
||||
from datetime import datetime
|
||||
import secrets
|
||||
from typing import Any, Optional
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class AsyncTaskModel(SQLModel, table=True):
|
||||
__tablename__ = "async_tasks"
|
||||
|
||||
id: str = Field(
|
||||
default_factory=lambda: f"task-{secrets.token_hex(32)}",
|
||||
primary_key=True,
|
||||
)
|
||||
type: str = Field(index=True)
|
||||
status: str = Field(index=True)
|
||||
message: Optional[str] = None
|
||||
error: Optional[dict[str, Any]] = Field(sa_column=Column(JSON), default=None)
|
||||
data: Optional[dict[str, Any]] = Field(sa_column=Column(JSON), default=None)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
)
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
onupdate=get_current_utc_datetime,
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime, ForeignKey, String, Text
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class ChatHistoryMessageModel(SQLModel, table=True):
|
||||
__tablename__ = "chat_history_messages"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
presentation_id: Optional[uuid.UUID] = Field(
|
||||
default=None,
|
||||
sa_column=Column(
|
||||
ForeignKey("presentations.id", ondelete="CASCADE"),
|
||||
index=True,
|
||||
nullable=True,
|
||||
)
|
||||
)
|
||||
template_v2_id: Optional[str] = Field(
|
||||
default=None,
|
||||
sa_column=Column(
|
||||
String,
|
||||
ForeignKey("template_v2.id", ondelete="CASCADE"),
|
||||
index=True,
|
||||
nullable=True,
|
||||
)
|
||||
)
|
||||
conversation_id: uuid.UUID = Field(index=True)
|
||||
position: int = Field(index=True, ge=1)
|
||||
role: str
|
||||
content: str = Field(sa_column=Column(Text, nullable=False))
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
)
|
||||
)
|
||||
tool_calls: Optional[list[str]] = Field(sa_column=Column(JSON), default=None)
|
||||
@@ -0,0 +1,31 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime, Integer, String
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class FontUpload(SQLModel, table=True):
|
||||
__tablename__ = "font_uploads"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
filename: str
|
||||
path: str
|
||||
normalized_family_name: str = Field(index=True)
|
||||
family_name: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
subfamily_name: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
full_name: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
postscript_name: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
weight_class: Optional[int] = Field(sa_column=Column(Integer), default=None)
|
||||
width_class: Optional[int] = Field(sa_column=Column(Integer), default=None)
|
||||
format: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
size_bytes: int
|
||||
extras: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
@@ -0,0 +1,20 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class ImageAsset(SQLModel, table=True):
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
is_uploaded: bool = Field(default=False)
|
||||
path: str
|
||||
extras: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
@@ -0,0 +1,8 @@
|
||||
import uuid
|
||||
from sqlmodel import Field, Column, JSON, SQLModel
|
||||
|
||||
|
||||
class KeyValueSqlModel(SQLModel, table=True):
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
key: str = Field(index=True)
|
||||
value: dict = Field(sa_column=Column(JSON))
|
||||
@@ -0,0 +1,9 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
from sqlmodel import Field, Column, JSON, SQLModel, DateTime
|
||||
|
||||
|
||||
class OllamaPullStatus(SQLModel, table=True):
|
||||
id: str = Field(primary_key=True)
|
||||
last_updated: datetime = Field(sa_column=Column(DateTime, default=datetime.now))
|
||||
status: dict = Field(sa_column=Column(JSON))
|
||||
@@ -0,0 +1,105 @@
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import List, Optional
|
||||
import uuid
|
||||
import copy
|
||||
from sqlalchemy import JSON, Column, DateTime, Enum as SAEnum, String
|
||||
from sqlmodel import Boolean, Field, SQLModel
|
||||
|
||||
from models.presentation_outline_model import PresentationOutlineModel
|
||||
from models.presentation_structure_model import PresentationStructureModel
|
||||
from models.presentation_layout import PresentationLayoutModel
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class PresentationVersion(str, Enum):
|
||||
V1_STANDARD = "v1-standard"
|
||||
V2_STANDARD = "v2-standard"
|
||||
|
||||
|
||||
class PresentationModel(SQLModel, table=True):
|
||||
__tablename__ = "presentations"
|
||||
|
||||
id: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4)
|
||||
version: PresentationVersion = Field(
|
||||
sa_column=Column(
|
||||
SAEnum(
|
||||
PresentationVersion,
|
||||
values_callable=lambda enum: [item.value for item in enum],
|
||||
name="presentation_version",
|
||||
native_enum=False,
|
||||
create_constraint=True,
|
||||
),
|
||||
nullable=False,
|
||||
),
|
||||
)
|
||||
content: str
|
||||
n_slides: int
|
||||
language: str
|
||||
title: Optional[str] = None
|
||||
file_paths: Optional[List[str]] = Field(sa_column=Column(JSON), default=None)
|
||||
outlines: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
onupdate=get_current_utc_datetime,
|
||||
),
|
||||
)
|
||||
layout: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
structure: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
instructions: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
tone: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
verbosity: Optional[str] = Field(sa_column=Column(String), default=None)
|
||||
include_table_of_contents: bool = Field(sa_column=Column(Boolean), default=False)
|
||||
include_title_slide: bool = Field(sa_column=Column(Boolean), default=True)
|
||||
web_search: bool = Field(sa_column=Column(Boolean), default=False)
|
||||
theme: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
fonts: Optional[dict] = Field(sa_column=Column(JSON), default=None)
|
||||
|
||||
def get_new_presentation(self):
|
||||
return PresentationModel(
|
||||
id=uuid.uuid4(),
|
||||
version=self.version,
|
||||
content=self.content,
|
||||
n_slides=self.n_slides,
|
||||
language=self.language,
|
||||
title=self.title,
|
||||
file_paths=copy.deepcopy(self.file_paths),
|
||||
outlines=copy.deepcopy(self.outlines),
|
||||
layout=copy.deepcopy(self.layout),
|
||||
structure=copy.deepcopy(self.structure),
|
||||
instructions=self.instructions,
|
||||
tone=self.tone,
|
||||
verbosity=self.verbosity,
|
||||
include_table_of_contents=self.include_table_of_contents,
|
||||
include_title_slide=self.include_title_slide,
|
||||
web_search=self.web_search,
|
||||
theme=copy.deepcopy(self.theme),
|
||||
fonts=copy.deepcopy(self.fonts),
|
||||
)
|
||||
|
||||
def get_presentation_outline(self):
|
||||
if not self.outlines:
|
||||
return None
|
||||
return PresentationOutlineModel(**self.outlines)
|
||||
|
||||
def get_layout(self):
|
||||
return PresentationLayoutModel(**self.layout)
|
||||
|
||||
def set_layout(self, layout: PresentationLayoutModel):
|
||||
self.layout = layout.model_dump()
|
||||
|
||||
def get_structure(self):
|
||||
if not self.structure:
|
||||
return None
|
||||
return PresentationStructureModel(**self.structure)
|
||||
|
||||
def set_structure(self, structure: PresentationStructureModel):
|
||||
self.structure = structure.model_dump()
|
||||
@@ -0,0 +1,38 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime, Text
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class PresentationLayoutCodeModel(SQLModel, table=True):
|
||||
__tablename__ = "presentation_layout_codes"
|
||||
|
||||
id: Optional[int] = Field(default=None, primary_key=True)
|
||||
presentation: uuid.UUID = Field(index=True, description="UUID of the presentation")
|
||||
layout_id: str = Field(description="Unique identifier for the layout")
|
||||
layout_name: str = Field(description="Display name of the layout")
|
||||
layout_code: str = Field(
|
||||
sa_column=Column(Text), description="TSX/React component code for the layout"
|
||||
)
|
||||
fonts: Optional[dict[str, str] | list[str]] = Field(
|
||||
default=None,
|
||||
sa_column=Column(JSON, nullable=True),
|
||||
description="Optional font metadata associated with the layout",
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
)
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
onupdate=get_current_utc_datetime,
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
from typing import Optional
|
||||
import uuid
|
||||
import copy
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlmodel import Field, Column, JSON, SQLModel
|
||||
|
||||
|
||||
class SlideModel(SQLModel, table=True):
|
||||
__tablename__ = "slides"
|
||||
|
||||
id: uuid.UUID = Field(primary_key=True, default_factory=uuid.uuid4)
|
||||
presentation: uuid.UUID = Field(
|
||||
sa_column=Column(ForeignKey("presentations.id", ondelete="CASCADE"), index=True)
|
||||
)
|
||||
layout_group: str
|
||||
layout: str
|
||||
index: int
|
||||
content: dict = Field(sa_column=Column(JSON))
|
||||
html_content: Optional[str] = None
|
||||
speaker_note: Optional[str] = None
|
||||
properties: Optional[dict] = Field(sa_column=Column(JSON))
|
||||
ui: Optional[dict] = Field(default=None, sa_column=Column(JSON, nullable=True))
|
||||
|
||||
def get_new_slide(self, presentation: uuid.UUID, content: Optional[dict] = None):
|
||||
return SlideModel(
|
||||
id=uuid.uuid4(),
|
||||
presentation=presentation,
|
||||
layout_group=self.layout_group,
|
||||
layout=self.layout,
|
||||
index=self.index,
|
||||
speaker_note=self.speaker_note,
|
||||
content=copy.deepcopy(content or self.content),
|
||||
html_content=self.html_content,
|
||||
properties=copy.deepcopy(self.properties),
|
||||
ui=copy.deepcopy(self.ui),
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import Column, DateTime
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class TemplateModel(SQLModel, table=True):
|
||||
__tablename__ = "templates"
|
||||
|
||||
id: uuid.UUID = Field(
|
||||
default_factory=uuid.uuid4,
|
||||
primary_key=True,
|
||||
description="UUID for the template (matches presentation_id)",
|
||||
)
|
||||
name: str = Field(description="Human friendly template name")
|
||||
description: Optional[str] = Field(
|
||||
default=None, description="Optional template description"
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import JSON, Column, DateTime
|
||||
from sqlmodel import Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class TemplateCreateInfoModel(SQLModel, table=True):
|
||||
__tablename__ = "template_create_infos"
|
||||
|
||||
id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True)
|
||||
fonts: dict[str, str] | None = Field(
|
||||
default=None,
|
||||
sa_column=Column(JSON, nullable=True),
|
||||
)
|
||||
pptx_url: str | None = Field(default=None)
|
||||
slide_htmls: list[str] = Field(sa_column=Column(JSON, nullable=False))
|
||||
slide_image_urls: list[str] = Field(sa_column=Column(JSON, nullable=False))
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,49 @@
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
|
||||
from sqlmodel import Field, SQLModel
|
||||
from sqlalchemy import JSON, Column, DateTime
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
def _new_template_v2_id() -> str:
|
||||
return str(uuid.uuid4())
|
||||
|
||||
|
||||
class TemplateV2(SQLModel, table=True):
|
||||
__tablename__ = "template_v2"
|
||||
|
||||
id: str = Field(primary_key=True, default_factory=_new_template_v2_id)
|
||||
name: str = Field(nullable=False)
|
||||
description: Optional[str] = Field(default=None, nullable=True)
|
||||
raw_layouts: Optional[dict] = Field(
|
||||
default=None, sa_column=Column(JSON, nullable=True)
|
||||
)
|
||||
components: Optional[dict] = Field(
|
||||
default=None, sa_column=Column(JSON, nullable=True)
|
||||
)
|
||||
merged_components: Optional[dict] = Field(
|
||||
default=None, sa_column=Column(JSON, nullable=True)
|
||||
)
|
||||
layouts: Optional[dict] = Field(
|
||||
default=None, sa_column=Column(JSON, nullable=True)
|
||||
)
|
||||
assets: Optional[dict] = Field(
|
||||
default=None, sa_column=Column(JSON, nullable=True)
|
||||
)
|
||||
is_default: bool = Field(default=False, nullable=False)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True), nullable=False, default=get_current_utc_datetime
|
||||
)
|
||||
)
|
||||
updated_at: datetime = Field(
|
||||
sa_column=Column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
default=get_current_utc_datetime,
|
||||
onupdate=get_current_utc_datetime,
|
||||
)
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
import secrets
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from sqlmodel import Column, DateTime, Field, SQLModel
|
||||
|
||||
from utils.datetime_utils import get_current_utc_datetime
|
||||
|
||||
|
||||
class WebhookSubscription(SQLModel, table=True):
|
||||
__tablename__ = "webhook_subscriptions"
|
||||
|
||||
id: str = Field(
|
||||
default_factory=lambda: f"webhook-{secrets.token_hex(32)}", primary_key=True
|
||||
)
|
||||
created_at: datetime = Field(
|
||||
sa_column=Column(DateTime(timezone=True), nullable=False),
|
||||
default_factory=get_current_utc_datetime,
|
||||
)
|
||||
url: str
|
||||
secret: Optional[str] = None
|
||||
event: str = Field(index=True)
|
||||
@@ -0,0 +1,49 @@
|
||||
import json
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class SSEResponse(BaseModel):
|
||||
event: str
|
||||
data: str
|
||||
|
||||
def to_string(self):
|
||||
return f"event: {self.event}\ndata: {self.data}\n\n"
|
||||
|
||||
|
||||
class SSEStatusResponse(BaseModel):
|
||||
status: str
|
||||
|
||||
def to_string(self):
|
||||
return SSEResponse(
|
||||
event="response", data=json.dumps({"type": "status", "status": self.status})
|
||||
).to_string()
|
||||
|
||||
|
||||
class SSETraceResponse(BaseModel):
|
||||
trace: object
|
||||
|
||||
def to_string(self):
|
||||
return SSEResponse(
|
||||
event="response", data=json.dumps({"type": "trace", "trace": self.trace})
|
||||
).to_string()
|
||||
|
||||
|
||||
class SSEErrorResponse(BaseModel):
|
||||
detail: str
|
||||
|
||||
def to_string(self):
|
||||
return SSEResponse(
|
||||
event="response", data=json.dumps({"type": "error", "detail": self.detail})
|
||||
).to_string()
|
||||
|
||||
|
||||
class SSECompleteResponse(BaseModel):
|
||||
key: str
|
||||
value: object
|
||||
|
||||
def to_string(self):
|
||||
return SSEResponse(
|
||||
event="response",
|
||||
data=json.dumps({"type": "complete", self.key: self.value}),
|
||||
).to_string()
|
||||
@@ -0,0 +1,41 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Dict
|
||||
|
||||
|
||||
class ThemeData(BaseModel):
|
||||
primary: str
|
||||
background: str
|
||||
card: str
|
||||
stroke: str
|
||||
background_text: str
|
||||
primary_text: str
|
||||
graph_0: str
|
||||
graph_1: str
|
||||
graph_2: str
|
||||
graph_3: str
|
||||
graph_4: str
|
||||
graph_5: str
|
||||
graph_6: str
|
||||
graph_7: str
|
||||
graph_8: str
|
||||
graph_9: str
|
||||
|
||||
|
||||
class GeneratedColorPalette(BaseModel):
|
||||
primary: str
|
||||
background: str
|
||||
accent_1: str
|
||||
accent_2: str
|
||||
text_1: str
|
||||
text_2: str
|
||||
primary_variations: Dict[str, str]
|
||||
background_variations: Dict[str, str]
|
||||
accent_1_variations: Dict[str, str]
|
||||
accent_2_variations: Dict[str, str]
|
||||
primary_lightness: float
|
||||
background_lightness: float
|
||||
accent_1_lightness: float
|
||||
accent_2_lightness: float
|
||||
text_1_lightness: float
|
||||
text_2_lightness: float
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class UserConfig(BaseModel):
|
||||
LLM: Optional[str] = None
|
||||
|
||||
# OpenAI
|
||||
OPENAI_API_KEY: Optional[str] = None
|
||||
OPENAI_MODEL: Optional[str] = None
|
||||
|
||||
# Google
|
||||
GOOGLE_API_KEY: Optional[str] = None
|
||||
GOOGLE_MODEL: Optional[str] = None
|
||||
|
||||
# Vertex AI
|
||||
VERTEX_API_KEY: Optional[str] = None
|
||||
VERTEX_MODEL: Optional[str] = None
|
||||
VERTEX_PROJECT: Optional[str] = None
|
||||
VERTEX_LOCATION: Optional[str] = None
|
||||
VERTEX_BASE_URL: Optional[str] = None
|
||||
|
||||
# Azure OpenAI
|
||||
AZURE_OPENAI_API_KEY: Optional[str] = None
|
||||
AZURE_OPENAI_MODEL: Optional[str] = None
|
||||
AZURE_OPENAI_ENDPOINT: Optional[str] = None
|
||||
AZURE_OPENAI_BASE_URL: Optional[str] = None
|
||||
AZURE_OPENAI_API_VERSION: Optional[str] = None
|
||||
AZURE_OPENAI_DEPLOYMENT: Optional[str] = None
|
||||
|
||||
# Amazon Bedrock
|
||||
BEDROCK_REGION: Optional[str] = None
|
||||
BEDROCK_API_KEY: Optional[str] = None
|
||||
BEDROCK_AWS_ACCESS_KEY_ID: Optional[str] = None
|
||||
BEDROCK_AWS_SECRET_ACCESS_KEY: Optional[str] = None
|
||||
BEDROCK_AWS_SESSION_TOKEN: Optional[str] = None
|
||||
BEDROCK_PROFILE_NAME: Optional[str] = None
|
||||
BEDROCK_MODEL: Optional[str] = None
|
||||
|
||||
# OpenRouter
|
||||
OPENROUTER_API_KEY: Optional[str] = None
|
||||
OPENROUTER_MODEL: Optional[str] = None
|
||||
OPENROUTER_BASE_URL: Optional[str] = None
|
||||
|
||||
# Fireworks
|
||||
FIREWORKS_API_KEY: Optional[str] = None
|
||||
FIREWORKS_MODEL: Optional[str] = None
|
||||
FIREWORKS_BASE_URL: Optional[str] = None
|
||||
|
||||
# Together AI
|
||||
TOGETHER_API_KEY: Optional[str] = None
|
||||
TOGETHER_MODEL: Optional[str] = None
|
||||
TOGETHER_BASE_URL: Optional[str] = None
|
||||
|
||||
# Cerebras
|
||||
CEREBRAS_API_KEY: Optional[str] = None
|
||||
CEREBRAS_MODEL: Optional[str] = None
|
||||
CEREBRAS_BASE_URL: Optional[str] = None
|
||||
|
||||
# LiteLLM (OpenAI-compatible gateway / proxy)
|
||||
LITELLM_BASE_URL: Optional[str] = None
|
||||
LITELLM_API_KEY: Optional[str] = None
|
||||
LITELLM_MODEL: Optional[str] = None
|
||||
|
||||
# LM Studio (local OpenAI-compatible server)
|
||||
LMSTUDIO_BASE_URL: Optional[str] = None
|
||||
LMSTUDIO_API_KEY: Optional[str] = None
|
||||
LMSTUDIO_MODEL: Optional[str] = None
|
||||
|
||||
# Anthropic
|
||||
ANTHROPIC_API_KEY: Optional[str] = None
|
||||
ANTHROPIC_MODEL: Optional[str] = None
|
||||
|
||||
# Ollama
|
||||
OLLAMA_URL: Optional[str] = None
|
||||
OLLAMA_MODEL: Optional[str] = None
|
||||
|
||||
# Custom LLM
|
||||
CUSTOM_LLM_URL: Optional[str] = None
|
||||
CUSTOM_LLM_API_KEY: Optional[str] = None
|
||||
CUSTOM_MODEL: Optional[str] = None
|
||||
|
||||
# DeepSeek
|
||||
DEEPSEEK_BASE_URL: Optional[str] = None
|
||||
DEEPSEEK_API_KEY: Optional[str] = None
|
||||
DEEPSEEK_MODEL: Optional[str] = None
|
||||
|
||||
# Image Provider
|
||||
DISABLE_IMAGE_GENERATION: Optional[bool] = None
|
||||
IMAGE_PROVIDER: Optional[str] = None
|
||||
PEXELS_API_KEY: Optional[str] = None
|
||||
PIXABAY_API_KEY: Optional[str] = None
|
||||
|
||||
# ComfyUI
|
||||
COMFYUI_URL: Optional[str] = None
|
||||
COMFYUI_WORKFLOW: Optional[str] = None
|
||||
|
||||
# Open WebUI Image Provider
|
||||
OPEN_WEBUI_IMAGE_URL: Optional[str] = None
|
||||
OPEN_WEBUI_IMAGE_API_KEY: Optional[str] = None
|
||||
|
||||
# OpenAI Compatible Image Provider
|
||||
OPENAI_COMPAT_IMAGE_BASE_URL: Optional[str] = None
|
||||
OPENAI_COMPAT_IMAGE_API_KEY: Optional[str] = None
|
||||
OPENAI_COMPAT_IMAGE_MODEL: Optional[str] = None
|
||||
|
||||
# Dalle 3 Quality
|
||||
DALL_E_3_QUALITY: Optional[str] = None
|
||||
# Gpt Image 1.5 Quality
|
||||
GPT_IMAGE_1_5_QUALITY: Optional[str] = None
|
||||
|
||||
# Reasoning
|
||||
DISABLE_THINKING: Optional[bool] = None
|
||||
EXTENDED_REASONING: Optional[bool] = None
|
||||
|
||||
# Web Search
|
||||
WEB_GROUNDING: Optional[bool] = None
|
||||
WEB_SEARCH_PROVIDER: Optional[str] = None
|
||||
WEB_SEARCH_MAX_RESULTS: Optional[str] = None
|
||||
SEARXNG_BASE_URL: Optional[str] = None
|
||||
TAVILY_API_KEY: Optional[str] = None
|
||||
EXA_API_KEY: Optional[str] = None
|
||||
BRAVE_SEARCH_API_KEY: Optional[str] = None
|
||||
SERPER_API_KEY: Optional[str] = None
|
||||
|
||||
# Codex OAuth (ChatGPT)
|
||||
CODEX_MODEL: Optional[str] = None
|
||||
CODEX_ACCESS_TOKEN: Optional[str] = None
|
||||
CODEX_REFRESH_TOKEN: Optional[str] = None
|
||||
CODEX_TOKEN_EXPIRES: Optional[str] = None
|
||||
CODEX_ACCOUNT_ID: Optional[str] = None
|
||||
CODEX_USERNAME: Optional[str] = None
|
||||
CODEX_EMAIL: Optional[str] = None
|
||||
CODEX_IS_PRO: Optional[bool] = None
|
||||
Reference in New Issue
Block a user