Files
paypal-checkout-orders-open…/agent.py
a2a-platform 858df123a8 deploy
2026-06-27 01:02:00 +00:00

600 lines
57 KiB
Python

from __future__ import annotations
import base64
import json
import re
from pathlib import Path
from typing import Any
import httpx
from a2a_pack.deepagents import create_a2a_deep_agent
from langchain_core.messages import BaseMessage
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
from a2a_pack import (
A2AAgent,
ConsumerSetup,
ConsumerSetupField,
ConsumerSetupMissing,
EgressPolicy,
LLMProvisioning,
Pricing,
Resources,
RunContext,
skill,
)
DEFAULT_BASE_URL = "https://api-m.paypal.com"
OPERATIONS = json.loads("{\n \"orders_authorize\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Authorizes payment for an order. To successfully authorize payment for an order, the buyer must first approve the order or a valid payment_source must be provided in the request. A buyer can approve the order upon being redirected to the rel:approve URL that was returned in the HATEOAS links in the create order response.<blockquote><strong>Note:</strong> For error handling and troubleshooting, see <a href=\\\"https://developer.paypal.com/api/rest/reference/orders/v2/errors/#authorize-order\\\">Orders v2 errors</a>.</blockquote>\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"orders_authorize\",\n \"parameters\": [\n {\n \"description\": \"The server stores keys for 6 hours. The API callers can request the times to up to 72 hours by speaking to their Account Manager. It is mandatory for all single-step create order calls (E.g. Create Order Request with payment source information like Card, PayPal.vault_id, PayPal.billing_agreement_id, etc).\",\n \"in\": \"header\",\n \"name\": \"PayPal-Request-Id\",\n \"required\": false,\n \"schema\": {\n \"maxLength\": 108,\n \"minLength\": 1,\n \"pattern\": \"^[\\\\S\\\\s]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"The preferred server response upon successful completion of the request. Value is:<ul><li><code>return=minimal</code>. The server returns a minimal response to optimize communication between the API caller and the server. A minimal response includes the <code>id</code>, <code>status</code> and HATEOAS links.</li><li><code>return=representation</code>. The server returns a complete resource representation, including the current state of the resource.</li></ul>\",\n \"in\": \"header\",\n \"name\": \"Prefer\",\n \"required\": false,\n \"schema\": {\n \"default\": \"return=minimal\",\n \"maxLength\": 25,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z=,-]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"A GUID value originating from Fraudnet and Dyson passed from external API clients via HTTP header. The value is used by Risk decisions to correlate calls which, in turn, might result in lower decline rates..\",\n \"in\": \"header\",\n \"name\": \"PayPal-Client-Metadata-Id\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/guid\"\n }\n },\n {\n \"description\": \"The ID of the order for which to authorize.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}/authorize\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/order_authorize_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\",\n \"https://uri.paypal.com/services/payments/orders/deprecating-jssdk-migration-for-limited-merchants\"\n ]\n }\n ],\n \"skill_name\": \"orders_authorize\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Authorize payment for order\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_capture\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Captures payment for an order. To successfully capture payment for an order, the buyer must first approve the order or a valid payment_source must be provided in the request. A buyer can approve the order upon being redirected to the rel:approve URL that was returned in the HATEOAS links in the create order response.<blockquote><strong>Note:</strong> For error handling and troubleshooting, see <a href=\\\"https://developer.paypal.com/api/rest/reference/orders/v2/errors/#capture-order\\\">Orders v2 errors</a>.</blockquote>\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"orders_capture\",\n \"parameters\": [\n {\n \"description\": \"The server stores keys for 6 hours. The API callers can request the times to up to 72 hours by speaking to their Account Manager. It is mandatory for all single-step create order calls (E.g. Create Order Request with payment source information like Card, PayPal.vault_id, PayPal.billing_agreement_id, etc).\",\n \"in\": \"header\",\n \"name\": \"PayPal-Request-Id\",\n \"required\": false,\n \"schema\": {\n \"maxLength\": 108,\n \"minLength\": 1,\n \"pattern\": \"^[\\\\S\\\\s]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"The preferred server response upon successful completion of the request. Value is:<ul><li><code>return=minimal</code>. The server returns a minimal response to optimize communication between the API caller and the server. A minimal response includes the <code>id</code>, <code>status</code> and HATEOAS links.</li><li><code>return=representation</code>. The server returns a complete resource representation, including the current state of the resource.</li></ul>\",\n \"in\": \"header\",\n \"name\": \"Prefer\",\n \"required\": false,\n \"schema\": {\n \"default\": \"return=minimal\",\n \"maxLength\": 25,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z=,-]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"A GUID value originating from Fraudnet and Dyson passed from external API clients via HTTP header. The value is used by Risk decisions to correlate calls which, in turn, might result in lower decline rates..\",\n \"in\": \"header\",\n \"name\": \"PayPal-Client-Metadata-Id\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/guid\"\n }\n },\n {\n \"description\": \"The ID of the order for which to capture a payment.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}/capture\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/order_capture_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\",\n \"https://uri.paypal.com/services/payments/orders/deprecating-jssdk-migration-for-limited-merchants\"\n ]\n }\n ],\n \"skill_name\": \"orders_capture\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Capture payment for order\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_confirm\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Payer confirms their intent to pay for the the Order with the given payment source.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"orders_confirm\",\n \"parameters\": [\n {\n \"description\": \"A GUID value originating from Fraudnet and Dyson passed from external API clients via HTTP header. The value is used by Risk decisions to correlate calls which, in turn, might result in lower decline rates..\",\n \"in\": \"header\",\n \"name\": \"PayPal-Client-Metadata-Id\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/guid\"\n }\n },\n {\n \"description\": \"The ID of the order for which the payer confirms their intent to pay.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"The preferred server response upon successful completion of the request. Value is:<ul><li><code>return=minimal</code>. The server returns a minimal response to optimize communication between the API caller and the server. A minimal response includes the <code>id</code>, <code>status</code> and HATEOAS links.</li><li><code>return=representation</code>. The server returns a complete resource representation, including the current state of the resource.</li></ul>\",\n \"in\": \"header\",\n \"name\": \"Prefer\",\n \"required\": false,\n \"schema\": {\n \"default\": \"return=minimal\",\n \"maxLength\": 25,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z=]*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}/confirm-payment-source\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/confirm_order_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\",\n \"https://uri.paypal.com/services/payments/initiatepayment\",\n \"https://uri.paypal.com/services/payments/orders/client_sdk_orders_api\"\n ]\n }\n ],\n \"skill_name\": \"orders_confirm\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Confirm the Order\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_create\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Creates an order. Merchants and partners can add Level 2 and 3 data to payments to reduce risk and payment processing costs. For more information about processing payments, see <a href=\\\"https://developer.paypal.com/docs/checkout/advanced/processing/\\\">checkout</a> or <a href=\\\"https://developer.paypal.com/docs/multiparty/checkout/advanced/processing/\\\">multiparty checkout</a>.<blockquote><strong>Note:</strong> For error handling and troubleshooting, see <a href=\\\"https://developer.paypal.com/api/rest/reference/orders/v2/errors/#create-order\\\">Orders v2 errors</a>.</blockquote>\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"orders_create\",\n \"parameters\": [\n {\n \"description\": \"The server stores keys for 6 hours. The API callers can request the times to up to 72 hours by speaking to their Account Manager. It is mandatory for all single-step create order calls (E.g. Create Order Request with payment source information like Card, PayPal.vault_id, PayPal.billing_agreement_id, etc).\",\n \"in\": \"header\",\n \"name\": \"PayPal-Request-Id\",\n \"required\": false,\n \"schema\": {\n \"maxLength\": 108,\n \"minLength\": 1,\n \"pattern\": \"^[\\\\S\\\\s]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"PayPal Partner can send a PayPal-Partner-Attribution-Id request header with the value that they have been assigned by the PayPal Partner program. This value is known as a BN Code. In order to reward such partners (through partner programs), all the activities (including API calls) that they are doing on behalf of the merchants need to be tracked.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Partner-Attribution-Id\",\n \"required\": false,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[\\\\S\\\\s]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"A GUID value originating from Fraudnet and Dyson passed from external API clients via HTTP header. The value is used by Risk decisions to correlate calls which, in turn, might result in lower decline rates..\",\n \"in\": \"header\",\n \"name\": \"PayPal-Client-Metadata-Id\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/guid\"\n }\n },\n {\n \"description\": \"The preferred server response upon successful completion of the request. Value is:<ul><li><code>return=minimal</code>. The server returns a minimal response to optimize communication between the API caller and the server. A minimal response includes the <code>id</code>, <code>status</code> and HATEOAS links.</li><li><code>return=representation</code>. The server returns a complete resource representation, including the current state of the resource.</li></ul>\",\n \"in\": \"header\",\n \"name\": \"Prefer\",\n \"required\": false,\n \"schema\": {\n \"default\": \"return=minimal\",\n \"maxLength\": 25,\n \"minLength\": 1,\n \"pattern\": \"^[a-zA-Z=,-]*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/order_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\",\n \"https://uri.paypal.com/services/payments/orders/deprecating-jssdk-migration-for-limited-merchants\"\n ]\n }\n ],\n \"skill_name\": \"orders_create\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Create order\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_get\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Shows details for an order, by ID.<blockquote><strong>Note:</strong> For error handling and troubleshooting, see <a href=\\\"https://developer.paypal.com/api/rest/reference/orders/v2/errors/#get-order\\\">Orders v2 errors</a>.</blockquote>\",\n \"destructive\": false,\n \"method\": \"GET\",\n \"operation_id\": \"orders_get\",\n \"parameters\": [\n {\n \"description\": \"The ID of the order for which to show details.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"A comma-separated list of fields that should be returned for the order. Valid filter field is `payment_source`.\",\n \"in\": \"query\",\n \"name\": \"fields\",\n \"required\": false,\n \"schema\": {\n \"maxLength\": 2147483647,\n \"minLength\": 0,\n \"pattern\": \"^[a-z_]*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}\",\n \"request_body\": null,\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\",\n \"https://uri.paypal.com/services/payments/orders/deprecating-jssdk-migration-for-limited-merchants\"\n ]\n }\n ],\n \"skill_name\": \"orders_get\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Show order details\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_patch\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Updates an order with a `CREATED` or `APPROVED` status. You cannot update an order with the `COMPLETED` status.<br/><br/>To make an update, you must provide a `reference_id`. If you omit this value with an order that contains only one purchase unit, PayPal sets the value to `default` which enables you to use the path: <code>\\\\\\\"/purchase_units/@reference_id=='default'/{attribute-or-object}\\\\\\\"</code>. Merchants and partners can add Level 2 and 3 data to payments to reduce risk and payment processing costs. For more information about processing payments, see <a href=\\\"https://developer.paypal.com/docs/checkout/advanced/processing/\\\">checkout</a> or <a href=\\\"https://developer.paypal.com/docs/multiparty/checkout/advanced/processing/\\\">multiparty checkout</a>.<blockquote><strong>Note:</strong> For error handling and troubleshooting, see <a href=\\\"https://developer.paypal.com/api/rest/reference/orders/v2/errors/#patch-order\\\">Orders v2 errors</a>.</blockquote>Patchable attributes or objects:<br/><br/><table><thead><th>Attribute</th><th>Op</th><th>Notes</th></thead><tbody><tr><td><code>intent</code></td><td>replace</td><td></td></tr><tr><td><code>payer</code></td><td>replace, add</td><td>Using replace op for <code>payer</code> will replace the whole <code>payer</code> object with the value sent in request.</td></tr><tr><td><code>purchase_units</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].custom_id</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].description</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].payee.email</code></td><td>replace</td><td></td></tr><tr><td><code>purchase_units[].shipping.name</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].shipping.email_address</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].shipping.phone_number</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].shipping.options</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].shipping.address</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].shipping.type</code></td><td>replace, add</td><td></td></tr><tr><td><code>purchase_units[].soft_descriptor</code></td><td>replace, remove</td><td></td></tr><tr><td><code>purchase_units[].amount</code></td><td>replace</td><td></td></tr><tr><td><code>purchase_units[].items</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].invoice_id</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].payment_instruction</code></td><td>replace</td><td></td></tr><tr><td><code>purchase_units[].payment_instruction.disbursement_mode</code></td><td>replace</td><td>By default, <code>disbursement_mode</code> is <code>INSTANT</code>.</td></tr><tr><td><code>purchase_units[].payment_instruction.payee_receivable_fx_rate_id</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].payment_instruction.platform_fees</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].supplementary_data.airline</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>purchase_units[].supplementary_data.card</code></td><td>replace, add, remove</td><td></td></tr><tr><td><code>application_context.client_configuration</code></td><td>replace, add</td><td></td></tr></tbody></table>\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"orders_patch\",\n \"parameters\": [\n {\n \"description\": \"The ID of the order to update.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/patch_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\",\n \"https://uri.paypal.com/services/payments/orders/deprecating-jssdk-migration-for-limited-merchants\"\n ]\n }\n ],\n \"skill_name\": \"orders_patch\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Update order\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_track_create\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Adds tracking information for an Order.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"orders_track_create\",\n \"parameters\": [\n {\n \"description\": \"The ID of the order that the tracking information is associated with.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}/track\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/order_tracker_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\"\n ]\n }\n ],\n \"skill_name\": \"orders_track_create\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Add tracking information for an Order.\",\n \"tags\": [\n \"orders\"\n ]\n },\n \"orders_trackers_patch\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"Updates or cancels the tracking information for a PayPal order, by ID. Updatable attributes or objects:<br/><br/><table><thead><th>Attribute</th><th>Op</th><th>Notes</th></thead><tbody></tr><tr><td><code>items</code></td><td>replace</td><td>Using replace op for <code>items</code> will replace the entire <code>items</code> object with the value sent in request.</td></tr><tr><td><code>notify_payer</code></td><td>replace, add</td><td></td></tr><tr><td><code>status</code></td><td>replace</td><td>Only patching status to CANCELLED is currently supported.</td></tr></tbody></table>\",\n \"destructive\": true,\n \"method\": \"PATCH\",\n \"operation_id\": \"orders_trackers_patch\",\n \"parameters\": [\n {\n \"description\": \"The ID of the order that the tracking information is associated with.\",\n \"in\": \"path\",\n \"name\": \"id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"The order tracking ID.\",\n \"in\": \"path\",\n \"name\": \"tracker_id\",\n \"required\": true,\n \"schema\": {\n \"maxLength\": 36,\n \"minLength\": 1,\n \"pattern\": \"^[A-Z0-9]+$\",\n \"type\": \"string\"\n }\n },\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n },\n {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"in\": \"header\",\n \"name\": \"PayPal-Auth-Assertion\",\n \"required\": false,\n \"schema\": {\n \"description\": \"Header for an API client-provided JWT assertion that identifies the merchant. Establishing the consent to act-on-behalf of a merchant is a prerequisite for using this header.\",\n \"maxLength\": 10000,\n \"minLength\": 1,\n \"pattern\": \"^.*$\",\n \"type\": \"string\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/{id}/trackers/{tracker_id}\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/patch_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\"\n ]\n }\n ],\n \"skill_name\": \"orders_trackers_patch\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Update or cancel tracking information for an order\",\n \"tags\": [\n \"trackers\"\n ]\n },\n \"server_callback\": {\n \"base_url\": \"https://api-m.paypal.com\",\n \"base_url_field\": \"OPENAPI_BASE_URL\",\n \"description\": \"The documentation for this 'endpoint' is different from the other endpoints under v2 Orders. For this endpoint the role of client and server is reversed. The client sending the request is PayPal, and the server sending the response is the merchant. In the request, PayPal will send the buyer's redacted shipping address and selected shipping option to the callback URL defined the create order request. The response from the merchant will update the Orders resource.\",\n \"destructive\": true,\n \"method\": \"POST\",\n \"operation_id\": \"server_callback\",\n \"parameters\": [\n {\n \"description\": \"Holds authorization information for external API calls.\",\n \"in\": \"header\",\n \"name\": \"Authorization\",\n \"required\": false,\n \"schema\": {\n \"$ref\": \"#/components/schemas/standard_header_schema\"\n }\n }\n ],\n \"path\": \"/v2/checkout/orders/order-update-callback\",\n \"request_body\": {\n \"$ref\": \"#/components/schemas/order_update_callback_request\"\n },\n \"security\": [\n {\n \"Oauth2\": [\n \"https://uri.paypal.com/services/payments/payment\"\n ]\n }\n ],\n \"skill_name\": \"server_callback\",\n \"source_index\": 1,\n \"source_openapi_url\": \"https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json\",\n \"source_title\": \"Orders\",\n \"summary\": \"Receive updated order information via callback URL\",\n \"tags\": [\n \"externalcallback\"\n ]\n }\n}")
OPERATION_GROUPS = json.loads("[]")
ROOT_SECURITY = json.loads("[]")
SECURITY_SCHEMES = json.loads("{\n \"Oauth2\": {\n \"description\": \"Oauth 2.0 authentication\",\n \"flows\": {\n \"clientCredentials\": {\n \"scopes\": {\n \"https://uri.paypal.com/services/payments/initiatepayment\": \"Initiates payments and checkout workflows.\",\n \"https://uri.paypal.com/services/payments/orders/client_sdk_orders_api\": \"Enables secure client-side integration on confirm payment source endpoint\",\n \"https://uri.paypal.com/services/payments/orders/deprecating-jssdk-migration-for-limited-merchants\": \"Allows client-side integration on Create, Get, Patch, Authorize & Capture Order endpoints.\",\n \"https://uri.paypal.com/services/payments/orders/provision-card\": \"Manage card provisioning merchants.\",\n \"https://uri.paypal.com/services/payments/payment\": \"Manage payments and checkout workflow.\",\n \"https://uri.paypal.com/services/payments/payment/reference-transaction\": \"Permission to initiate reference transaction\"\n },\n \"tokenUrl\": \"https://api-m.paypal.com/v1/oauth2/token\"\n }\n },\n \"type\": \"oauth2\"\n }\n}")
SECURITY_FIELDS = json.loads("{\n \"Oauth2\": {\n \"field\": \"OAUTH2\",\n \"kind\": \"oauth2\",\n \"scheme\": \"Bearer\"\n }\n}")
PATH_PARAMETER_RE = re.compile(r"{([^}/]+)}")
SOURCE_ROOT = Path(globals().get("__file__", "agent.py")).resolve().parent
SOURCE_SKILLS_DIR = SOURCE_ROOT / "skills"
RUNTIME_SKILLS_DIR = ".deepagents/openapi-skills/"
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 PaypalCheckoutOrdersOpenapiAgent(A2AAgent):
name = "paypal-checkout-orders-openapi-agent"
description = "Merchant checkout and order-management API for PayPal commerce flows."
version = "2.32"
consumer_setup = ConsumerSetup.from_fields(
ConsumerSetupField.config("OPENAPI_BASE_URL", label="API base URL", description="Override the default API server (https://api-m.paypal.com).", required=False, input_type="url"),
ConsumerSetupField.secret("OAUTH2", label="Oauth2 access token", description="Oauth 2.0 authentication", required=True),
)
llm_provisioning = LLMProvisioning.PLATFORM
pricing = Pricing(
price_per_call_usd=0.0,
caller_pays_llm=True,
notes="Uses the caller's saved LLM credential through ctx.llm.",
)
resources = Resources(cpu="200m", memory="512Mi")
egress = EgressPolicy(
allow_hosts=('api-m.paypal.com',),
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://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json',
"source_openapi_urls": ['https://raw.githubusercontent.com/paypal/paypal-rest-api-specifications/main/openapi/checkout_orders_v2.json'],
"server_urls": ['https://api-m.paypal.com'],
"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=900,
)
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 key required. Add an LLM credential in Settings > "
"LLM credentials before running this agent."
),
"messages": [],
}
graph = create_a2a_deep_agent(
ctx,
creds=creds,
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="orders_create",
description="POST /v2/checkout/orders - Create order",
tags=('orders',),
timeout_seconds=120,
)
async def orders_create(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_create",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_get",
description="GET /v2/checkout/orders/{id} - Show order details",
tags=('orders',),
timeout_seconds=60,
)
async def orders_get(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_get",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_patch",
description="PATCH /v2/checkout/orders/{id} - Update order",
tags=('orders',),
timeout_seconds=120,
)
async def orders_patch(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_patch",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_confirm",
description="POST /v2/checkout/orders/{id}/confirm-payment-source - Confirm the Order",
tags=('orders',),
timeout_seconds=120,
)
async def orders_confirm(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_confirm",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_authorize",
description="POST /v2/checkout/orders/{id}/authorize - Authorize payment for order",
tags=('orders',),
timeout_seconds=120,
)
async def orders_authorize(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_authorize",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_capture",
description="POST /v2/checkout/orders/{id}/capture - Capture payment for order",
tags=('orders',),
timeout_seconds=120,
)
async def orders_capture(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_capture",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_track_create",
description="POST /v2/checkout/orders/{id}/track - Add tracking information for an Order.",
tags=('orders',),
timeout_seconds=120,
)
async def orders_track_create(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_track_create",
parameters=parameters or {},
body=body,
)
@skill(
name="orders_trackers_patch",
description="PATCH /v2/checkout/orders/{id}/trackers/{tracker_id} - Update or cancel tracking information for an order",
tags=('trackers',),
timeout_seconds=120,
)
async def orders_trackers_patch(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"orders_trackers_patch",
parameters=parameters or {},
body=body,
)
@skill(
name="server_callback",
description="POST /v2/checkout/orders/order-update-callback - Receive updated order information via callback URL",
tags=('externalcallback',),
timeout_seconds=120,
)
async def server_callback(
self,
ctx: RunContext,
parameters: dict[str, Any] | None = None,
body: Any | None = None,
) -> dict[str, Any]:
return await self._request(
ctx,
"server_callback",
parameters=parameters or {},
body=body,
)
def _operation_subagents(self, ctx: RunContext, skills_root: str) -> list[dict[str, Any]]:
subagents: list[dict[str, Any]] = []
for group in OPERATION_GROUPS:
skill_path = (
f"{skills_root}{group['skill_dir']}/"
if skills_root
else ""
)
subagent = {
"name": group["subagent_name"],
"description": group["description"],
"system_prompt": self._subagent_prompt(group),
"tools": self._operation_tools(ctx, set(group["operation_ids"])),
}
if skill_path:
subagent["skills"] = [skill_path]
subagents.append(subagent)
return subagents
def _operation_tools(
self,
ctx: RunContext,
operation_ids: set[str] | None = None,
) -> list[StructuredTool]:
tools: list[StructuredTool] = []
for operation_id, operation in OPERATIONS.items():
if operation_ids is not None and operation_id not in operation_ids:
continue
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:
if OPERATION_GROUPS:
lines = [
"You coordinate route-specific OpenAPI subagents.",
"Delegate API work to the matching subagent with the task tool.",
"Do not invent API responses; ask subagents to call their tools for real results.",
"If a subagent reports missing consumer setup, tell the user which setup field is required.",
"If a subagent reports that the generated OpenAPI agent may be stale, ask whether the user wants to refresh it from the latest OpenAPI spec.",
"",
"Available route groups:",
]
for group in OPERATION_GROUPS:
lines.append(
f"- {group['subagent_name']}: {group['description']}"
)
return "\n".join(lines)
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 _subagent_prompt(self, group: dict[str, Any]) -> str:
lines = [
"You operate one route group from a generated OpenAPI API.",
"Use the available operation tools to make real API calls. Do not invent API responses.",
"Read the route group's SKILL.md when it applies; it lists every route you cover.",
"For write, update, or delete operations, explain the intended action before calling the tool.",
"If an operation reports missing consumer setup, return the exact setup field name.",
"If an operation returns 404, 405, 410, or a schema/validation error that suggests the live API no longer matches these tools, say the generated agent may need to be refreshed from the latest OpenAPI spec.",
"",
f"Route group: {group['label']}",
f"Common prefix: {group.get('common_prefix') or '/'}",
"Available operation tools:",
]
for operation_id in group["operation_ids"]:
operation = OPERATIONS[operation_id]
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 _runtime_skills_root(ctx: RunContext) -> str:
workspace = getattr(ctx, "_workspace", None)
prefixes = tuple(getattr(workspace, "write_prefixes", ()) or ())
if not prefixes:
outputs_prefix = getattr(workspace, "outputs_prefix", None)
prefixes = (outputs_prefix or "outputs/",)
prefix = str(prefixes[0]).strip("/")
return f"/{prefix}/{RUNTIME_SKILLS_DIR}" if prefix else f"/{RUNTIME_SKILLS_DIR}"
def _seed_runtime_skills(backend: Any, ctx: RunContext) -> str:
if not SOURCE_SKILLS_DIR.exists():
return ""
runtime_root = _runtime_skills_root(ctx)
uploads: list[tuple[str, bytes]] = []
for path in SOURCE_SKILLS_DIR.rglob("*"):
if not path.is_file():
continue
rel = path.relative_to(SOURCE_SKILLS_DIR).as_posix()
uploads.append((runtime_root + rel, path.read_bytes()))
if not uploads:
return ""
backend.upload_files(uploads)
return runtime_root
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 = PaypalCheckoutOrdersOpenapiAgent()