deploy
This commit is contained in:
101
agent.py
101
agent.py
@@ -42,6 +42,9 @@ ROOT_SECURITY = json.loads("[\n {\n \"Bearer\": []\n }\n]")
|
||||
SECURITY_SCHEMES = json.loads("{\n \"Bearer\": {\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"type\": \"apiKey\"\n }\n}")
|
||||
SECURITY_FIELDS = json.loads("{\n \"Bearer\": {\n \"field\": \"AUTHORIZATION\",\n \"kind\": \"apiKey\",\n \"location\": \"header\",\n \"name\": \"Authorization\"\n }\n}")
|
||||
PATH_PARAMETER_RE = re.compile(r"{([^}/]+)}")
|
||||
MAIL_HOST = "mail.a2acloud.io"
|
||||
SMTP_SUBMISSION_PORT = 587
|
||||
IMAPS_PORT = 993
|
||||
|
||||
|
||||
class OperationInput(BaseModel):
|
||||
@@ -53,49 +56,41 @@ class OperationInput(BaseModel):
|
||||
|
||||
|
||||
class SendEmailInput(BaseModel):
|
||||
email: str = Field(description="Mailbox email address to send as. A temporary Mailu token is minted internally.")
|
||||
mailbox_email: str = Field(description="Mailbox email address to send as. A temporary Mailu token is minted internally.")
|
||||
to: list[str] = Field(description="Recipient email addresses.")
|
||||
subject: str = Field(description="Message subject.")
|
||||
text_body: str = Field(description="Plain text message body.")
|
||||
cc: list[str] = Field(default_factory=list, description="CC recipients.")
|
||||
bcc: list[str] = Field(default_factory=list, description="BCC recipients.")
|
||||
reply_to: str | None = Field(default=None, description="Optional Reply-To address.")
|
||||
smtp_host: str = Field(default="mail.a2acloud.io", description="SMTP submission host.")
|
||||
smtp_port: int = Field(default=587, description="SMTP port. Use 587 for STARTTLS or 465 for implicit TLS.")
|
||||
|
||||
|
||||
class ReadInboxInput(BaseModel):
|
||||
email: str = Field(description="Mailbox email address to read. A temporary Mailu token is minted internally.")
|
||||
mailbox_email: str = Field(description="Mailbox email address to read. A temporary Mailu token is minted internally.")
|
||||
mailbox: str = Field(default="INBOX", description="IMAP mailbox name.")
|
||||
limit: int = Field(default=10, ge=1, le=50, description="Maximum number of newest messages to return.")
|
||||
unread_only: bool = Field(default=True, description="Only return unread messages.")
|
||||
mark_seen: bool = Field(default=False, description="Mark returned messages as read.")
|
||||
max_body_chars: int = Field(default=4000, ge=0, le=20000, description="Maximum plain text body characters per message.")
|
||||
imap_host: str = Field(default="mail.a2acloud.io", description="IMAPS host.")
|
||||
imap_port: int = Field(default=993, description="IMAPS port.")
|
||||
|
||||
|
||||
class ReadSentInput(BaseModel):
|
||||
email: str = Field(description="Mailbox email address to read. A temporary Mailu token is minted internally.")
|
||||
mailbox_email: str = Field(description="Mailbox email address to read. A temporary Mailu token is minted internally.")
|
||||
mailbox: str = Field(default="Sent", description="IMAP sent-mail mailbox name. Override if the client uses another folder.")
|
||||
limit: int = Field(default=10, ge=1, le=50, description="Maximum number of newest sent messages to return.")
|
||||
max_body_chars: int = Field(default=4000, ge=0, le=20000, description="Maximum plain text body characters per message.")
|
||||
imap_host: str = Field(default="mail.a2acloud.io", description="IMAPS host.")
|
||||
imap_port: int = Field(default=993, description="IMAPS port.")
|
||||
|
||||
|
||||
class MarkEmailReadInput(BaseModel):
|
||||
email: str = Field(description="Mailbox email address. A temporary Mailu token is minted internally.")
|
||||
mailbox_email: str = Field(description="Mailbox email address. A temporary Mailu token is minted internally.")
|
||||
uid: str = Field(description="IMAP UID returned by read_inbox.")
|
||||
mailbox: str = Field(default="INBOX", description="IMAP mailbox name.")
|
||||
imap_host: str = Field(default="mail.a2acloud.io", description="IMAPS host.")
|
||||
imap_port: int = Field(default=993, description="IMAPS port.")
|
||||
|
||||
|
||||
class MailuOpenapiAgent(A2AAgent):
|
||||
name = "mailu-openapi-agent"
|
||||
description = "Mailu administration agent generated from the live Mailu OpenAPI spec."
|
||||
version = "1.2"
|
||||
version = "1.3"
|
||||
consumer_setup = ConsumerSetup.from_fields(
|
||||
ConsumerSetupField.config("OPENAPI_BASE_URL", label="API base URL", description="Override the default API server (https://mail.a2acloud.io/api/v1).", required=False, input_type="url"),
|
||||
ConsumerSetupField.secret("AUTHORIZATION", label="Bearer API key", description="Sent as header parameter 'Authorization'. Also used to mint temporary per-user Mailu mailbox tokens for SMTP/IMAP skills.", required=True),
|
||||
@@ -173,22 +168,20 @@ class MailuOpenapiAgent(A2AAgent):
|
||||
async def send_email(
|
||||
self,
|
||||
ctx: RunContext,
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
to: list[str],
|
||||
subject: str,
|
||||
text_body: str,
|
||||
cc: list[str] | None = None,
|
||||
bcc: list[str] | None = None,
|
||||
reply_to: str | None = None,
|
||||
smtp_host: str = "mail.a2acloud.io",
|
||||
smtp_port: int = 587,
|
||||
) -> dict[str, Any]:
|
||||
if not to:
|
||||
return {"ok": False, "error": "recipient_required"}
|
||||
async with self._temporary_mail_token(ctx, email, "send_email") as token_info:
|
||||
async with self._temporary_mail_token(ctx, mailbox_email, "send_email") as token_info:
|
||||
return await asyncio.to_thread(
|
||||
_send_email_sync,
|
||||
email,
|
||||
mailbox_email,
|
||||
token_info["token"],
|
||||
to,
|
||||
subject,
|
||||
@@ -196,8 +189,8 @@ class MailuOpenapiAgent(A2AAgent):
|
||||
cc or [],
|
||||
bcc or [],
|
||||
reply_to,
|
||||
smtp_host,
|
||||
smtp_port,
|
||||
MAIL_HOST,
|
||||
SMTP_SUBMISSION_PORT,
|
||||
)
|
||||
|
||||
@skill(
|
||||
@@ -209,27 +202,25 @@ class MailuOpenapiAgent(A2AAgent):
|
||||
async def read_inbox(
|
||||
self,
|
||||
ctx: RunContext,
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
mailbox: str = "INBOX",
|
||||
limit: int = 10,
|
||||
unread_only: bool = True,
|
||||
mark_seen: bool = False,
|
||||
max_body_chars: int = 4000,
|
||||
imap_host: str = "mail.a2acloud.io",
|
||||
imap_port: int = 993,
|
||||
) -> dict[str, Any]:
|
||||
async with self._temporary_mail_token(ctx, email, "read_inbox") as token_info:
|
||||
async with self._temporary_mail_token(ctx, mailbox_email, "read_inbox") as token_info:
|
||||
return await asyncio.to_thread(
|
||||
_read_inbox_sync,
|
||||
email,
|
||||
mailbox_email,
|
||||
token_info["token"],
|
||||
mailbox,
|
||||
limit,
|
||||
unread_only,
|
||||
mark_seen,
|
||||
max_body_chars,
|
||||
imap_host,
|
||||
imap_port,
|
||||
MAIL_HOST,
|
||||
IMAPS_PORT,
|
||||
)
|
||||
|
||||
@skill(
|
||||
@@ -241,25 +232,23 @@ class MailuOpenapiAgent(A2AAgent):
|
||||
async def read_sent(
|
||||
self,
|
||||
ctx: RunContext,
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
mailbox: str = "Sent",
|
||||
limit: int = 10,
|
||||
max_body_chars: int = 4000,
|
||||
imap_host: str = "mail.a2acloud.io",
|
||||
imap_port: int = 993,
|
||||
) -> dict[str, Any]:
|
||||
async with self._temporary_mail_token(ctx, email, "read_sent") as token_info:
|
||||
async with self._temporary_mail_token(ctx, mailbox_email, "read_sent") as token_info:
|
||||
return await asyncio.to_thread(
|
||||
_read_mailbox_sync,
|
||||
email,
|
||||
mailbox_email,
|
||||
token_info["token"],
|
||||
mailbox,
|
||||
limit,
|
||||
"ALL",
|
||||
False,
|
||||
max_body_chars,
|
||||
imap_host,
|
||||
imap_port,
|
||||
MAIL_HOST,
|
||||
IMAPS_PORT,
|
||||
)
|
||||
|
||||
@skill(
|
||||
@@ -271,21 +260,19 @@ class MailuOpenapiAgent(A2AAgent):
|
||||
async def mark_email_read(
|
||||
self,
|
||||
ctx: RunContext,
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
uid: str,
|
||||
mailbox: str = "INBOX",
|
||||
imap_host: str = "mail.a2acloud.io",
|
||||
imap_port: int = 993,
|
||||
) -> dict[str, Any]:
|
||||
async with self._temporary_mail_token(ctx, email, "mark_email_read") as token_info:
|
||||
async with self._temporary_mail_token(ctx, mailbox_email, "mark_email_read") as token_info:
|
||||
return await asyncio.to_thread(
|
||||
_mark_email_read_sync,
|
||||
email,
|
||||
mailbox_email,
|
||||
token_info["token"],
|
||||
uid,
|
||||
mailbox,
|
||||
imap_host,
|
||||
imap_port,
|
||||
MAIL_HOST,
|
||||
IMAPS_PORT,
|
||||
)
|
||||
|
||||
@skill(
|
||||
@@ -1012,83 +999,67 @@ class MailuOpenapiAgent(A2AAgent):
|
||||
|
||||
def _operation_tools(self, ctx: RunContext) -> list[StructuredTool]:
|
||||
async def send_email_tool(
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
to: list[str],
|
||||
subject: str,
|
||||
text_body: str,
|
||||
cc: list[str] | None = None,
|
||||
bcc: list[str] | None = None,
|
||||
reply_to: str | None = None,
|
||||
smtp_host: str = "mail.a2acloud.io",
|
||||
smtp_port: int = 587,
|
||||
) -> dict[str, Any]:
|
||||
return await self.send_email(
|
||||
ctx,
|
||||
email=email,
|
||||
mailbox_email=mailbox_email,
|
||||
to=to,
|
||||
subject=subject,
|
||||
text_body=text_body,
|
||||
cc=cc,
|
||||
bcc=bcc,
|
||||
reply_to=reply_to,
|
||||
smtp_host=smtp_host,
|
||||
smtp_port=smtp_port,
|
||||
)
|
||||
|
||||
async def read_inbox_tool(
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
mailbox: str = "INBOX",
|
||||
limit: int = 10,
|
||||
unread_only: bool = True,
|
||||
mark_seen: bool = False,
|
||||
max_body_chars: int = 4000,
|
||||
imap_host: str = "mail.a2acloud.io",
|
||||
imap_port: int = 993,
|
||||
) -> dict[str, Any]:
|
||||
return await self.read_inbox(
|
||||
ctx,
|
||||
email=email,
|
||||
mailbox_email=mailbox_email,
|
||||
mailbox=mailbox,
|
||||
limit=limit,
|
||||
unread_only=unread_only,
|
||||
mark_seen=mark_seen,
|
||||
max_body_chars=max_body_chars,
|
||||
imap_host=imap_host,
|
||||
imap_port=imap_port,
|
||||
)
|
||||
|
||||
async def read_sent_tool(
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
mailbox: str = "Sent",
|
||||
limit: int = 10,
|
||||
max_body_chars: int = 4000,
|
||||
imap_host: str = "mail.a2acloud.io",
|
||||
imap_port: int = 993,
|
||||
) -> dict[str, Any]:
|
||||
return await self.read_sent(
|
||||
ctx,
|
||||
email=email,
|
||||
mailbox_email=mailbox_email,
|
||||
mailbox=mailbox,
|
||||
limit=limit,
|
||||
max_body_chars=max_body_chars,
|
||||
imap_host=imap_host,
|
||||
imap_port=imap_port,
|
||||
)
|
||||
|
||||
async def mark_email_read_tool(
|
||||
email: str,
|
||||
mailbox_email: str,
|
||||
uid: str,
|
||||
mailbox: str = "INBOX",
|
||||
imap_host: str = "mail.a2acloud.io",
|
||||
imap_port: int = 993,
|
||||
) -> dict[str, Any]:
|
||||
return await self.mark_email_read(
|
||||
ctx,
|
||||
email=email,
|
||||
mailbox_email=mailbox_email,
|
||||
uid=uid,
|
||||
mailbox=mailbox,
|
||||
imap_host=imap_host,
|
||||
imap_port=imap_port,
|
||||
)
|
||||
|
||||
tools: list[StructuredTool] = [
|
||||
|
||||
Reference in New Issue
Block a user