human handoffs

This commit is contained in:
debu-2 2026-10-06 01:51:23 +05:30
parent 79188e0f59
commit 36a55930f9
35 changed files with 4039 additions and 139 deletions

View file

@ -224,6 +224,112 @@ class OrgSecuritySettingsRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
class HandoffEmailSample(BaseModel):
subject: str = ""
snippet: str = ""
body_text: str = ""
is_starred: bool = False
class HandoffRuleCreate(BaseModel):
owner_email: EmailStr
owner_name: str | None = None
labels: list[str] = Field(default_factory=list)
email_types: list[str] = Field(default_factory=list)
instructions: str = ""
allow_replies: bool = True
reply_as: Literal["agent", "human"] = "agent"
active: bool = True
class HandoffRuleUpdate(BaseModel):
owner_email: EmailStr | None = None
owner_name: str | None = None
labels: list[str] | None = None
email_types: list[str] | None = None
instructions: str | None = None
allow_replies: bool | None = None
reply_as: Literal["agent", "human"] | None = None
active: bool | None = None
class HandoffRuleRead(BaseModel):
id: str
owner_email: EmailStr
owner_name: str
labels: list[str]
email_types: list[str]
instructions: str
allow_replies: bool
reply_as: Literal["agent", "human"]
active: bool
created_at: datetime
updated_at: datetime
class HandoffTestRequest(BaseModel):
rule_id: str | None = None
owner_email: EmailStr | None = None
labels: list[str] = Field(default_factory=list)
email_types: list[str] = Field(default_factory=list)
instructions: str = ""
email: HandoffEmailSample
class HandoffTestResult(BaseModel):
owner_email: EmailStr | None = None
label_matched: bool
matched_labels: list[str]
type_matched: bool
matched_types: list[str]
instruction_matched: bool
instruction_reason: str
would_handoff: bool
result_path: str
reason: str
class HandoffLinkedExternal(BaseModel):
thread_id: str
subject: str
sender: str
class HandoffConversationSummary(BaseModel):
id: str
external_subject: str
external_sender: str
human_email: str
status: str
status_label: str
current_owner: str
snippet: str
message_count: int
updated_at: datetime
class HandoffMessageRead(BaseModel):
id: str
role: Literal["agent", "human"]
subject: str
snippet: str
body_text: str
event_at: datetime
class HandoffConversationRead(BaseModel):
id: str
external_subject: str
external_sender: str
human_email: str
handoff_reason: str
status: str
status_label: str
current_owner: str
linked_external: HandoffLinkedExternal
messages: list[HandoffMessageRead]
class OrgSecuritySettingsUpdate(BaseModel):
require_strong_passwords: bool | None = None
require_mfa_admins: bool | None = None
@ -236,12 +342,21 @@ class OrgMemberRoleUpdate(BaseModel):
class EmailCreate(BaseModel):
recipients: list[EmailStr] = Field(min_length=1)
recipients: list[EmailStr] = Field(default_factory=list)
cc: list[EmailStr] = Field(default_factory=list)
bcc: list[EmailStr] = Field(default_factory=list)
subject: str = Field(default="", max_length=998, pattern=r"^[^\r\n]*$")
body_text: str = ""
body_html: str | None = None
reply_to_message_id: str | None = None
reply_all: bool = False
thread_id: str | None = None
@model_validator(mode="after")
def recipients_required_without_reply(self):
if not self.reply_to_message_id and not self.recipients:
raise ValueError("At least one recipient is required.")
return self
class EmailRead(EmailCreate):
@ -278,6 +393,7 @@ class MessageSummary(BaseModel):
is_starred: bool = False
trashed_at: datetime | None = None
attachments: list[AttachmentRead] = Field(default_factory=list)
thread_id: str | None = None
class MessageRead(MessageSummary):
@ -287,6 +403,10 @@ class MessageRead(MessageSummary):
body_text: str = ""
body_html: str | None = None
message_id: str | None = None
in_reply_to: str | None = None
references: list[str] = Field(default_factory=list)
parent_message_id: str | None = None
thread_relation: str | None = None
class MessageReadState(BaseModel):
@ -322,6 +442,34 @@ class MessagePage(BaseModel):
next_cursor: str | None = None
class ThreadSummary(BaseModel):
id: str
mailbox: Literal["inbox", "sent", "trash"]
subject: str
snippet: str
participants: list[MailAddress] = Field(default_factory=list)
message_count: int
unread_count: int = 0
is_read: bool = True
is_starred: bool = False
has_attachments: bool = False
latest_message_id: str
latest_at: datetime
senders: list[MailAddress] = Field(default_factory=list)
class ThreadPage(BaseModel):
items: list[ThreadSummary]
next_cursor: str | None = None
class ThreadRead(BaseModel):
thread_id: str
subject: str
message_count: int
messages: list[MessageRead]
class DomainCreate(BaseModel):
domain_name: str = Field(min_length=3, max_length=253)
account_id: UUID | None = None