Files
letta-server/letta/orm/job.py
Kian Jones 78600fac23 chore: index suggestions from planetscale suggestions (#6111)
suggestions from ps dashboard
2025-11-13 15:36:56 -08:00

66 lines
3.3 KiB
Python

from datetime import datetime
from typing import TYPE_CHECKING, List, Optional
from sqlalchemy import JSON, BigInteger, Boolean, ForeignKey, Index, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from letta.orm.mixins import UserMixin
from letta.orm.sqlalchemy_base import SqlalchemyBase
from letta.schemas.enums import JobStatus, JobType
from letta.schemas.job import Job as PydanticJob, LettaRequestConfig
from letta.schemas.letta_stop_reason import StopReasonType
if TYPE_CHECKING:
from letta.orm.message import Message
from letta.orm.organization import Organization
from letta.orm.user import User
class Job(SqlalchemyBase, UserMixin):
"""Jobs run in the background and are owned by a user.
Typical jobs involve loading and processing sources etc.
"""
__tablename__ = "jobs"
__pydantic_model__ = PydanticJob
__table_args__ = (Index("ix_jobs_user_id", "user_id"),)
status: Mapped[JobStatus] = mapped_column(String, default=JobStatus.created, doc="The current status of the job.")
completed_at: Mapped[Optional[datetime]] = mapped_column(nullable=True, doc="The unix timestamp of when the job was completed.")
stop_reason: Mapped[Optional[StopReasonType]] = mapped_column(String, nullable=True, doc="The reason why the job was stopped.")
background: Mapped[Optional[bool]] = mapped_column(
Boolean, nullable=True, default=False, doc="Whether the job was created in background mode."
)
metadata_: Mapped[Optional[dict]] = mapped_column(JSON, doc="The metadata of the job.")
job_type: Mapped[JobType] = mapped_column(
String,
default=JobType.JOB,
doc="The type of job. This affects whether or not we generate json_schema and source_code on the fly.",
)
request_config: Mapped[Optional[LettaRequestConfig]] = mapped_column(
JSON, nullable=True, doc="The request configuration for the job, stored as JSON."
)
organization_id: Mapped[Optional[str]] = mapped_column(String, ForeignKey("organizations.id"))
# callback related columns
callback_url: Mapped[Optional[str]] = mapped_column(String, nullable=True, doc="When set, POST to this URL after job completion.")
callback_sent_at: Mapped[Optional[datetime]] = mapped_column(nullable=True, doc="Timestamp when the callback was last attempted.")
callback_status_code: Mapped[Optional[int]] = mapped_column(nullable=True, doc="HTTP status code returned by the callback endpoint.")
callback_error: Mapped[Optional[str]] = mapped_column(
nullable=True, doc="Optional error message from attempting to POST the callback endpoint."
)
# timing metrics (in nanoseconds for precision)
ttft_ns: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True, doc="Time to first token in nanoseconds")
total_duration_ns: Mapped[Optional[int]] = mapped_column(BigInteger, nullable=True, doc="Total run duration in nanoseconds")
# relationships
user: Mapped["User"] = relationship("User", back_populates="jobs")
# organization relationship (nullable for backward compatibility)
organization: Mapped[Optional["Organization"]] = relationship("Organization", back_populates="jobs")
@property
def messages(self) -> List["Message"]:
"""Get all messages associated with this job."""
return [jm.message for jm in self.job_messages]