1069 lines
66 KiB
Python
1069 lines
66 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
import json
|
|
import re
|
|
from typing import Any
|
|
|
|
import httpx
|
|
from deepagents import create_deep_agent
|
|
from langchain_core.messages import BaseMessage
|
|
from langchain_core.tools import StructuredTool
|
|
from langchain_openai import ChatOpenAI
|
|
from pydantic import BaseModel, Field
|
|
|
|
from a2a_pack import (
|
|
A2AAgent,
|
|
ConsumerSetup,
|
|
ConsumerSetupField,
|
|
ConsumerSetupMissing,
|
|
EgressPolicy,
|
|
LLMProvisioning,
|
|
Pricing,
|
|
Resources,
|
|
RunContext,
|
|
skill,
|
|
)
|
|
|
|
|
|
DEFAULT_BASE_URL = "https://mail.a2acloud.io/api/v1"
|
|
OPERATIONS = json.loads("{\n \"create_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new alias\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_alias\",\n \"parameters\": [],\n \"path\": \"/alias\",\n \"request_body\": {\n \"$ref\": \"#/definitions/Alias\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"create_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new alternative (for domain)\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_alternative\",\n \"parameters\": [],\n \"path\": \"/alternative\",\n \"request_body\": {\n \"$ref\": \"#/definitions/AlternativeDomain\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new alternative (for domain)\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"create_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new domain\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_domain\",\n \"parameters\": [],\n \"path\": \"/domain\",\n \"request_body\": {\n \"$ref\": \"#/definitions/Domain\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"create_manager\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new domain manager for the specified domain\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_manager\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager\",\n \"request_body\": {\n \"$ref\": \"#/definitions/ManagerCreate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_manager\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new domain manager for the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"create_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new relay\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_relay\",\n \"parameters\": [],\n \"path\": \"/relay\",\n \"request_body\": {\n \"$ref\": \"#/definitions/Relay\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"create_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new token\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_token\",\n \"parameters\": [],\n \"path\": \"/token\",\n \"request_body\": {\n \"$ref\": \"#/definitions/TokenPost\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"create_token_2\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new token for the specified user\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_token_2\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/tokenuser/{email}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/TokenPost2\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_token_2\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new token for the specified user\",\n \"tags\": [\n \"token\"\n ]\n },\n \"create_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Create a new user\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"create_user\",\n \"parameters\": [],\n \"path\": \"/user\",\n \"request_body\": {\n \"$ref\": \"#/definitions/UserCreate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"create_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Create a new user\",\n \"tags\": [\n \"user\"\n ]\n },\n \"delete_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified alias\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_alias\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alias\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/{alias}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"delete_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified alternative (for domain)\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_alternative\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alt\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alternative/{alt}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified alternative (for domain)\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"delete_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified domain\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"delete_manager\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified manager of the specified domain\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_manager\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_manager\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified manager of the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"delete_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified relay\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_relay\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"name\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/relay/{name}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"delete_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified token\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_token\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"token_id\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/token/{token_id}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"delete_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Delete the specified user\",\n \"destructive\": true,\n \"method\": \"DELETE\",\n \"operation_id\": \"delete_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/user/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"delete_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Delete the specified user\",\n \"tags\": [\n \"user\"\n ]\n },\n \"find_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified alias\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_alias\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alias\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/{alias}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"find_alias_filter_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the aliases of the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_alias_filter_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/destination/{domain}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_alias_filter_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the aliases of the specified domain\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"find_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified alternative (of domain)\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_alternative\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alt\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alternative/{alt}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified alternative (of domain)\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"find_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"find_manager\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Check if the specified user is a manager of the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_manager\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_manager\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Check if the specified user is a manager of the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"find_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified relay\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_relay\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"name\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/relay/{name}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"find_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Find the specified token\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_token\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"token_id\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/token/{token_id}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Find the specified token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"find_tokens_of_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up all the tokens of the specified user\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_tokens_of_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/tokenuser/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_tokens_of_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up all the tokens of the specified user\",\n \"tags\": [\n \"token\"\n ]\n },\n \"find_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Look up the specified user\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"find_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/user/{email}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"find_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Look up the specified user\",\n \"tags\": [\n \"user\"\n ]\n },\n \"generate_dkim\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Generate new DKIM/DMARC keys for the specified domain\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"generate_dkim\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/dkim\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"generate_dkim\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Generate new DKIM/DMARC keys for the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"list_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all aliases\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_alias\",\n \"parameters\": [],\n \"path\": \"/alias\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all aliases\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"list_alternative\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all alternatives\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_alternative\",\n \"parameters\": [],\n \"path\": \"/alternative\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_alternative\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all alternatives\",\n \"tags\": [\n \"alternative\"\n ]\n },\n \"list_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all domains\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_domain\",\n \"parameters\": [],\n \"path\": \"/domain\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all domains\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"list_managers\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all managers of the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_managers\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/manager\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_managers\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all managers of the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"list_relays\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all relays\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_relays\",\n \"parameters\": [],\n \"path\": \"/relay\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_relays\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all relays\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"list_tokens\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all tokens\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_tokens\",\n \"parameters\": [],\n \"path\": \"/token\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_tokens\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all tokens\",\n \"tags\": [\n \"token\"\n ]\n },\n \"list_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all users\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_user\",\n \"parameters\": [],\n \"path\": \"/user\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all users\",\n \"tags\": [\n \"user\"\n ]\n },\n \"list_user_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"List all the users from the specified domain\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"list_user_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}/users\",\n \"request_body\": null,\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"list_user_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"List all the users from the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"update_alias\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specfied alias\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_alias\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"alias\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/alias/{alias}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/AliasUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_alias\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specfied alias\",\n \"tags\": [\n \"alias\"\n ]\n },\n \"update_domain\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified domain\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_domain\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"domain\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/domain/{domain}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/DomainUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_domain\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified domain\",\n \"tags\": [\n \"domain\"\n ]\n },\n \"update_relay\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified relay\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_relay\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"name\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/relay/{name}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/RelayUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_relay\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified relay\",\n \"tags\": [\n \"relay\"\n ]\n },\n \"update_token\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified token\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_token\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"token_id\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/token/{token_id}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/TokenPost2\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_token\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified token\",\n \"tags\": [\n \"token\"\n ]\n },\n \"update_user\": {\n \"base_url\": \"https://mail.a2acloud.io/api/v1\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Update the specified user\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"update_user\",\n \"parameters\": [\n {\n \"description\": \"\",\n \"in\": \"path\",\n \"name\": \"email\",\n \"required\": true,\n \"schema\": {\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/user/{email}\",\n \"request_body\": {\n \"$ref\": \"#/definitions/UserUpdate\"\n },\n \"security\": [\n {\n \"Bearer\": []\n }\n ],\n \"skill_name\": \"update_user\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://mail.a2acloud.io/api/v1/swagger.json\",\n \"source_title\": \"Mailu API\",\n \"summary\": \"Update the specified user\",\n \"tags\": [\n \"user\"\n ]\n }\n}")
|
|
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"{([^}/]+)}")
|
|
|
|
|
|
class OperationInput(BaseModel):
|
|
parameters: dict[str, Any] = Field(
|
|
default_factory=dict,
|
|
description="Path, query, header, and cookie parameters keyed by OpenAPI parameter name.",
|
|
)
|
|
body: Any | None = Field(default=None, description="JSON request body, when the operation accepts one.")
|
|
|
|
|
|
class MailuOpenapiAgent(A2AAgent):
|
|
name = "mailu-openapi-agent"
|
|
description = "Mailu administration agent generated from the live Mailu OpenAPI spec."
|
|
version = "1.0"
|
|
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'.", required=True),
|
|
)
|
|
llm_provisioning = LLMProvisioning.PLATFORM
|
|
pricing = Pricing(
|
|
price_per_call_usd=0.0,
|
|
caller_pays_llm=False,
|
|
notes="Uses a scoped platform LLM grant through ctx.llm.",
|
|
)
|
|
resources = Resources(cpu="200m", memory="512Mi")
|
|
egress = EgressPolicy(
|
|
allow_hosts=('mail.a2acloud.io',),
|
|
deny_internet_by_default=True,
|
|
)
|
|
tools_used = ("openapi", "deepagents")
|
|
capabilities = {
|
|
"openapi_auto_agent": {
|
|
"operation_count": len(OPERATIONS),
|
|
"default_base_url": DEFAULT_BASE_URL,
|
|
"source_openapi_url": 'https://mail.a2acloud.io/api/v1/swagger.json',
|
|
"source_openapi_urls": ['https://mail.a2acloud.io/api/v1/swagger.json'],
|
|
"server_urls": ['https://mail.a2acloud.io/api/v1'],
|
|
"regenerable": True,
|
|
"security_schemes": list(SECURITY_SCHEMES),
|
|
}
|
|
}
|
|
|
|
@skill(
|
|
name="auto",
|
|
description="Use the OpenAPI service to complete a natural-language goal.",
|
|
tags=("openapi", "auto"),
|
|
timeout_seconds=180,
|
|
)
|
|
async def auto(self, ctx: RunContext, goal: str) -> dict[str, Any]:
|
|
creds = ctx.llm
|
|
if not creds.api_key:
|
|
return {
|
|
"error": "llm_credentials_missing",
|
|
"final": (
|
|
"LLM credentials were not available for this run. Hosted "
|
|
"generated OpenAPI agents should receive a platform LLM grant."
|
|
),
|
|
"messages": [],
|
|
}
|
|
model_kwargs: dict[str, Any] = {
|
|
"model": creds.model,
|
|
"base_url": creds.base_url,
|
|
"api_key": creds.api_key,
|
|
}
|
|
if creds.temperature_mode != "omit" and creds.temperature is not None:
|
|
model_kwargs["temperature"] = creds.temperature
|
|
if creds.extra_body:
|
|
model_kwargs["extra_body"] = dict(creds.extra_body)
|
|
model = ChatOpenAI(**model_kwargs)
|
|
graph = create_deep_agent(
|
|
model=model,
|
|
tools=self._operation_tools(ctx),
|
|
system_prompt=self._system_prompt(),
|
|
)
|
|
result = await graph.ainvoke({"messages": [{"role": "user", "content": goal}]})
|
|
messages = result.get("messages", []) if isinstance(result, dict) else []
|
|
final = _message_text(messages[-1]) if messages else result
|
|
return {
|
|
"final": final,
|
|
"messages": [_message_to_dict(message) for message in messages[-8:]],
|
|
}
|
|
|
|
@skill(
|
|
name="create_alias",
|
|
description="POST /alias - Create a new alias",
|
|
tags=('alias',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_alias(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_alias",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_alias",
|
|
description="GET /alias - List all aliases",
|
|
tags=('alias',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_alias(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_alias",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_alias_filter_domain",
|
|
description="GET /alias/destination/{domain} - Look up the aliases of the specified domain",
|
|
tags=('alias',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_alias_filter_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_alias_filter_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="update_alias",
|
|
description="PATCH /alias/{alias} - Update the specfied alias",
|
|
tags=('alias',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def update_alias(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"update_alias",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_alias",
|
|
description="DELETE /alias/{alias} - Delete the specified alias",
|
|
tags=('alias',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_alias(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_alias",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_alias",
|
|
description="GET /alias/{alias} - Look up the specified alias",
|
|
tags=('alias',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_alias(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_alias",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_alternative",
|
|
description="POST /alternative - Create a new alternative (for domain)",
|
|
tags=('alternative',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_alternative(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_alternative",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_alternative",
|
|
description="GET /alternative - List all alternatives",
|
|
tags=('alternative',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_alternative(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_alternative",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_alternative",
|
|
description="DELETE /alternative/{alt} - Delete the specified alternative (for domain)",
|
|
tags=('alternative',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_alternative(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_alternative",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_alternative",
|
|
description="GET /alternative/{alt} - Look up the specified alternative (of domain)",
|
|
tags=('alternative',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_alternative(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_alternative",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_domain",
|
|
description="POST /domain - Create a new domain",
|
|
tags=('domain',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_domain",
|
|
description="GET /domain - List all domains",
|
|
tags=('domain',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="update_domain",
|
|
description="PATCH /domain/{domain} - Update the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def update_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"update_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_domain",
|
|
description="DELETE /domain/{domain} - Delete the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_domain",
|
|
description="GET /domain/{domain} - Look up the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="generate_dkim",
|
|
description="POST /domain/{domain}/dkim - Generate new DKIM/DMARC keys for the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def generate_dkim(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"generate_dkim",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_manager",
|
|
description="POST /domain/{domain}/manager - Create a new domain manager for the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_manager(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_manager",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_managers",
|
|
description="GET /domain/{domain}/manager - List all managers of the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_managers(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_managers",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_manager",
|
|
description="DELETE /domain/{domain}/manager/{email} - Delete the specified manager of the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_manager(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_manager",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_manager",
|
|
description="GET /domain/{domain}/manager/{email} - Check if the specified user is a manager of the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_manager(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_manager",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_user_domain",
|
|
description="GET /domain/{domain}/users - List all the users from the specified domain",
|
|
tags=('domain',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_user_domain(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_user_domain",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_relay",
|
|
description="POST /relay - Create a new relay",
|
|
tags=('relay',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_relay(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_relay",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_relays",
|
|
description="GET /relay - List all relays",
|
|
tags=('relay',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_relays(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_relays",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="update_relay",
|
|
description="PATCH /relay/{name} - Update the specified relay",
|
|
tags=('relay',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def update_relay(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"update_relay",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_relay",
|
|
description="DELETE /relay/{name} - Delete the specified relay",
|
|
tags=('relay',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_relay(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_relay",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_relay",
|
|
description="GET /relay/{name} - Look up the specified relay",
|
|
tags=('relay',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_relay(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_relay",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_token",
|
|
description="POST /token - Create a new token",
|
|
tags=('token',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_token(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_token",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_tokens",
|
|
description="GET /token - List all tokens",
|
|
tags=('token',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_tokens(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_tokens",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="update_token",
|
|
description="PATCH /token/{token_id} - Update the specified token",
|
|
tags=('token',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def update_token(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"update_token",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_token",
|
|
description="DELETE /token/{token_id} - Delete the specified token",
|
|
tags=('token',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_token(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_token",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_token",
|
|
description="GET /token/{token_id} - Find the specified token",
|
|
tags=('token',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_token(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_token",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_token_2",
|
|
description="POST /tokenuser/{email} - Create a new token for the specified user",
|
|
tags=('token',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_token_2(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_token_2",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_tokens_of_user",
|
|
description="GET /tokenuser/{email} - Look up all the tokens of the specified user",
|
|
tags=('token',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_tokens_of_user(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_tokens_of_user",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="create_user",
|
|
description="POST /user - Create a new user",
|
|
tags=('user',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def create_user(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"create_user",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="list_user",
|
|
description="GET /user - List all users",
|
|
tags=('user',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def list_user(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"list_user",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="update_user",
|
|
description="PATCH /user/{email} - Update the specified user",
|
|
tags=('user',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def update_user(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"update_user",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="delete_user",
|
|
description="DELETE /user/{email} - Delete the specified user",
|
|
tags=('user',),
|
|
timeout_seconds=120,
|
|
)
|
|
async def delete_user(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"delete_user",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
@skill(
|
|
name="find_user",
|
|
description="GET /user/{email} - Look up the specified user",
|
|
tags=('user',),
|
|
timeout_seconds=60,
|
|
)
|
|
async def find_user(
|
|
self,
|
|
ctx: RunContext,
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
"find_user",
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
def _operation_tools(self, ctx: RunContext) -> list[StructuredTool]:
|
|
tools: list[StructuredTool] = []
|
|
for operation_id, operation in OPERATIONS.items():
|
|
async def call(
|
|
parameters: dict[str, Any] | None = None,
|
|
body: Any | None = None,
|
|
*,
|
|
_operation_id: str = operation_id,
|
|
) -> dict[str, Any]:
|
|
return await self._request(
|
|
ctx,
|
|
_operation_id,
|
|
parameters=parameters or {},
|
|
body=body,
|
|
)
|
|
|
|
tools.append(
|
|
StructuredTool.from_function(
|
|
coroutine=call,
|
|
name=operation["skill_name"],
|
|
description=self._tool_description(operation),
|
|
args_schema=OperationInput,
|
|
)
|
|
)
|
|
return tools
|
|
|
|
def _system_prompt(self) -> str:
|
|
lines = [
|
|
"You operate an API through generated OpenAPI tools.",
|
|
"Call tools to get real results. Do not invent API responses.",
|
|
"For write, update, or delete operations, explain the intended action before calling the tool.",
|
|
"If an operation reports missing consumer setup, tell the user which setup field is required.",
|
|
"If an operation returns 404, 405, 410, or a schema/validation error that suggests the live API no longer matches these tools, tell the user this generated agent may need to be refreshed from the latest OpenAPI spec and ask whether they want to refresh it.",
|
|
"",
|
|
"Available operations:",
|
|
]
|
|
for operation in OPERATIONS.values():
|
|
marker = "WRITE" if operation.get("destructive") else "READ"
|
|
lines.append(
|
|
f"- {operation['skill_name']}: {marker} {operation['method']} {operation['path']} — {operation['summary']}"
|
|
)
|
|
return "\n".join(lines)
|
|
|
|
def _tool_description(self, operation: dict[str, Any]) -> str:
|
|
parts = [
|
|
f"{operation['method']} {operation['path']}",
|
|
str(operation.get("description") or operation.get("summary") or ""),
|
|
]
|
|
if operation.get("parameters"):
|
|
parts.append(
|
|
"Parameters: "
|
|
+ ", ".join(
|
|
f"{p['name']} in {p['in']}{' required' if p.get('required') else ''}"
|
|
for p in operation["parameters"]
|
|
)
|
|
)
|
|
if operation.get("request_body") is not None:
|
|
parts.append("Accepts a JSON request body.")
|
|
return "\n".join(part for part in parts if part)
|
|
|
|
async def _request(
|
|
self,
|
|
ctx: RunContext,
|
|
operation_id: str,
|
|
*,
|
|
parameters: dict[str, Any],
|
|
body: Any | None,
|
|
) -> dict[str, Any]:
|
|
operation = OPERATIONS[operation_id]
|
|
default_base_url = str(operation.get("base_url") or DEFAULT_BASE_URL).rstrip("/")
|
|
base_url_field = str(operation.get("base_url_field") or "OPENAPI_BASE_URL")
|
|
base_url = str(ctx.consumer_config(base_url_field, default_base_url) or default_base_url).rstrip("/")
|
|
url, query, headers = self._request_parts(ctx, operation, parameters)
|
|
request_kwargs: dict[str, Any] = {
|
|
"params": query,
|
|
"headers": headers,
|
|
}
|
|
if body is not None:
|
|
if isinstance(body, (bytes, bytearray)):
|
|
body = body.decode("utf-8", errors="replace")
|
|
if isinstance(body, str):
|
|
stripped = body.strip()
|
|
if stripped and stripped[0] in "{[":
|
|
try:
|
|
body = json.loads(stripped)
|
|
except ValueError:
|
|
pass
|
|
request_kwargs["json"] = body
|
|
async with httpx.AsyncClient(timeout=60.0, follow_redirects=True) as client:
|
|
response = await client.request(
|
|
operation["method"],
|
|
f"{base_url}{url}",
|
|
**request_kwargs,
|
|
)
|
|
content_type = response.headers.get("content-type", "")
|
|
try:
|
|
payload: Any = response.json() if "json" in content_type else response.text
|
|
except ValueError:
|
|
payload = response.text
|
|
if response.status_code >= 400:
|
|
out = {
|
|
"ok": False,
|
|
"status_code": response.status_code,
|
|
"operation_id": operation_id,
|
|
"error": payload,
|
|
}
|
|
if response.status_code in {404, 405, 410, 422}:
|
|
out["stale_openapi_hint"] = (
|
|
"This generated OpenAPI agent may be stale. Ask the user "
|
|
"whether they want to refresh it from the latest OpenAPI spec."
|
|
)
|
|
return out
|
|
return {
|
|
"ok": True,
|
|
"status_code": response.status_code,
|
|
"operation_id": operation_id,
|
|
"result": payload,
|
|
}
|
|
|
|
def _request_parts(
|
|
self,
|
|
ctx: RunContext,
|
|
operation: dict[str, Any],
|
|
parameters: dict[str, Any],
|
|
) -> tuple[str, dict[str, Any], dict[str, str]]:
|
|
path = operation["path"]
|
|
query: dict[str, Any] = {}
|
|
headers: dict[str, str] = {}
|
|
cookies: dict[str, Any] = {}
|
|
for param in operation.get("parameters") or []:
|
|
name = param["name"]
|
|
if name not in parameters:
|
|
if param.get("required"):
|
|
raise ValueError(f"missing required parameter {name!r}")
|
|
continue
|
|
value = parameters[name]
|
|
location = param["in"]
|
|
if location == "path":
|
|
path = path.replace("{" + name + "}", str(value))
|
|
elif location == "query":
|
|
query[name] = value
|
|
elif location == "header":
|
|
headers[name] = str(value)
|
|
elif location == "cookie":
|
|
cookies[name] = value
|
|
unresolved = PATH_PARAMETER_RE.findall(path)
|
|
if unresolved:
|
|
missing = ", ".join(sorted(set(unresolved)))
|
|
raise ValueError("missing required path parameter(s): " + missing)
|
|
if cookies:
|
|
headers["cookie"] = "; ".join(f"{key}={value}" for key, value in cookies.items())
|
|
auth_headers, auth_query = self._auth_for_operation(ctx, operation)
|
|
headers.update(auth_headers)
|
|
query.update(auth_query)
|
|
return path, query, headers
|
|
|
|
def _auth_for_operation(self, ctx: RunContext, operation: dict[str, Any]) -> tuple[dict[str, str], dict[str, Any]]:
|
|
requirements = operation.get("security")
|
|
if requirements is None:
|
|
requirements = ROOT_SECURITY
|
|
if requirements == []:
|
|
return {}, {}
|
|
missing: list[str] = []
|
|
for requirement in requirements or []:
|
|
if not isinstance(requirement, dict) or not requirement:
|
|
return {}, {}
|
|
headers: dict[str, str] = {}
|
|
query: dict[str, Any] = {}
|
|
ok = True
|
|
for scheme_name in requirement:
|
|
mapping = SECURITY_FIELDS.get(scheme_name) or {}
|
|
secret_name = mapping.get("field")
|
|
value = _consumer_secret_optional(ctx, secret_name) if secret_name else ""
|
|
if not value:
|
|
ok = False
|
|
if secret_name:
|
|
missing.append(secret_name)
|
|
continue
|
|
kind = mapping.get("kind")
|
|
if kind == "apiKey":
|
|
prefix = mapping.get("prefix") or ""
|
|
sent_value = f"{prefix}{value}"
|
|
if mapping.get("location") == "query":
|
|
query[mapping.get("name") or scheme_name] = sent_value
|
|
else:
|
|
headers[mapping.get("name") or scheme_name] = sent_value
|
|
elif kind in {"http", "oauth2"}:
|
|
scheme = mapping.get("scheme") or "Bearer"
|
|
if scheme.lower() == "basic":
|
|
token = base64.b64encode(value.encode("utf-8")).decode("ascii")
|
|
headers["authorization"] = f"Basic {token}"
|
|
else:
|
|
headers["authorization"] = f"{scheme} {value}"
|
|
else:
|
|
ok = False
|
|
if ok:
|
|
return headers, query
|
|
if missing:
|
|
raise ConsumerSetupMissing(
|
|
"operation requires consumer setup secret(s): "
|
|
+ ", ".join(sorted(set(missing)))
|
|
)
|
|
return {}, {}
|
|
|
|
|
|
def _consumer_secret_optional(ctx: RunContext, name: str | None) -> str:
|
|
if not name:
|
|
return ""
|
|
try:
|
|
return ctx.consumer_secret(name)
|
|
except ConsumerSetupMissing:
|
|
return ""
|
|
|
|
|
|
def _message_text(message: Any) -> Any:
|
|
content = getattr(message, "content", message)
|
|
return content
|
|
|
|
|
|
def _message_to_dict(message: Any) -> dict[str, Any]:
|
|
if isinstance(message, BaseMessage):
|
|
return message.model_dump(mode="json")
|
|
if hasattr(message, "model_dump"):
|
|
return message.model_dump(mode="json")
|
|
if isinstance(message, dict):
|
|
return message
|
|
return {"content": str(message)}
|
|
|
|
|
|
agent = MailuOpenapiAgent()
|