Accept native Outlook message shape in classifier request
This commit is contained in:
57
README.md
57
README.md
@@ -33,7 +33,11 @@ export EMAIL_CLASSIFIER_DB_PATH=.data/email_classifier.db
|
||||
|
||||
## Input shape
|
||||
|
||||
Designed around real Outlook message payloads. Relevant fields:
|
||||
The request model accepts either:
|
||||
- simplified input via `email_data`
|
||||
- or native Outlook-style fields directly
|
||||
|
||||
Full Outlook-shaped example:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -42,38 +46,55 @@ Designed around real Outlook message payloads. Relevant fields:
|
||||
"conversationId": "AAQk...",
|
||||
"subject": "MB Printer",
|
||||
"bodyPreview": "Good morning, ...",
|
||||
"body": {
|
||||
"contentType": "html",
|
||||
"content": "<html>...(full HTML body)</html>"
|
||||
},
|
||||
"sender": {
|
||||
"emailAddress": {
|
||||
"name": "Bobbi Johnson",
|
||||
"address": "bobbi.johnson@grandportage.com"
|
||||
}
|
||||
},
|
||||
"from": {
|
||||
"emailAddress": {
|
||||
"name": "Bobbi Johnson",
|
||||
"address": "bobbi.johnson@grandportage.com"
|
||||
}
|
||||
},
|
||||
"toRecipients": [
|
||||
{
|
||||
"emailAddress": {
|
||||
"name": "IT Helpdesk Mail",
|
||||
"address": "helpdeskmail@grandportage.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ccRecipients": [],
|
||||
"bccRecipients": [],
|
||||
"replyTo": [],
|
||||
"receivedDateTime": "2026-02-19T15:27:35Z",
|
||||
"sentDateTime": "2026-02-19T15:27:32Z",
|
||||
"hasAttachments": false,
|
||||
"importance": "normal",
|
||||
"isRead": false,
|
||||
"body": {
|
||||
"contentType": "html",
|
||||
"content": "..."
|
||||
}
|
||||
"flag": { "flagStatus": "notFlagged" },
|
||||
"provider": "anthropic",
|
||||
"base_url": "https://api.minimax.io/anthropic",
|
||||
"model": "MiniMax-M2.7"
|
||||
}
|
||||
```
|
||||
|
||||
API request example:
|
||||
Simplified request example:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "AAMk...",
|
||||
"internetMessageId": "<...@...>",
|
||||
"conversationId": "AAQk...",
|
||||
"bodyPreview": "Good morning, ...",
|
||||
"receivedDateTime": "2026-02-19T15:27:35Z",
|
||||
"sentDateTime": "2026-02-19T15:27:32Z",
|
||||
"hasAttachments": false,
|
||||
"importance": "normal",
|
||||
"isRead": false,
|
||||
"email_data": {
|
||||
"subject": "MB Printer",
|
||||
"body": "<html>...</html>"
|
||||
},
|
||||
"provider": "anthropic",
|
||||
"base_url": "https://api.minimax.io/anthropic",
|
||||
"model": "MiniMax-M2.7"
|
||||
"id": "AAMk...",
|
||||
"conversationId": "AAQk..."
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
|
||||
class EmailData(BaseModel):
|
||||
@@ -10,8 +10,26 @@ class EmailData(BaseModel):
|
||||
body: str
|
||||
|
||||
|
||||
class EmailAddress(BaseModel):
|
||||
name: str | None = None
|
||||
address: str | None = None
|
||||
|
||||
|
||||
class Recipient(BaseModel):
|
||||
emailAddress: EmailAddress | None = None
|
||||
|
||||
|
||||
class EmailBody(BaseModel):
|
||||
contentType: str | None = None
|
||||
content: str | None = None
|
||||
|
||||
|
||||
class Flag(BaseModel):
|
||||
flagStatus: str | None = None
|
||||
|
||||
|
||||
class ClassifyRequest(BaseModel):
|
||||
email_data: EmailData
|
||||
email_data: EmailData | None = None
|
||||
provider: Literal["openai", "anthropic"] | None = None
|
||||
model: str | None = None
|
||||
base_url: str | None = None
|
||||
@@ -21,14 +39,39 @@ class ClassifyRequest(BaseModel):
|
||||
id: str | None = None
|
||||
internetMessageId: str | None = None
|
||||
conversationId: str | None = None
|
||||
subject: str | None = None
|
||||
bodyPreview: str | None = None
|
||||
body: EmailBody | None = None
|
||||
sender: Recipient | None = None
|
||||
from_: Recipient | None = Field(default=None, alias="from")
|
||||
toRecipients: list[Recipient] = Field(default_factory=list)
|
||||
ccRecipients: list[Recipient] = Field(default_factory=list)
|
||||
bccRecipients: list[Recipient] = Field(default_factory=list)
|
||||
replyTo: list[Recipient] = Field(default_factory=list)
|
||||
receivedDateTime: str | None = None
|
||||
sentDateTime: str | None = None
|
||||
hasAttachments: bool | None = None
|
||||
importance: str | None = None
|
||||
isRead: bool | None = None
|
||||
flag: Flag | None = None
|
||||
from_address: str | None = None
|
||||
|
||||
@model_validator(mode="after")
|
||||
def populate_email_data(self) -> "ClassifyRequest":
|
||||
subject = self.email_data.subject if self.email_data else self.subject
|
||||
body = self.email_data.body if self.email_data else (self.body.content if self.body and self.body.content else None)
|
||||
if not subject or not body:
|
||||
raise ValueError("Request must include either email_data or Outlook subject/body.content fields")
|
||||
self.email_data = EmailData(subject=subject, body=body)
|
||||
if not self.from_address:
|
||||
self.from_address = (
|
||||
(self.from_.emailAddress.address if self.from_ and self.from_.emailAddress else None)
|
||||
or (self.sender.emailAddress.address if self.sender and self.sender.emailAddress else None)
|
||||
)
|
||||
return self
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class ClassificationDetails(BaseModel):
|
||||
summary: str | None = None
|
||||
|
||||
Reference in New Issue
Block a user