traia-iatp 0.1.29__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of traia-iatp might be problematic. Click here for more details.

Files changed (107) hide show
  1. traia_iatp/README.md +368 -0
  2. traia_iatp/__init__.py +54 -0
  3. traia_iatp/cli/__init__.py +5 -0
  4. traia_iatp/cli/main.py +483 -0
  5. traia_iatp/client/__init__.py +10 -0
  6. traia_iatp/client/a2a_client.py +274 -0
  7. traia_iatp/client/crewai_a2a_tools.py +335 -0
  8. traia_iatp/client/d402_a2a_client.py +293 -0
  9. traia_iatp/client/grpc_a2a_tools.py +349 -0
  10. traia_iatp/client/root_path_a2a_client.py +1 -0
  11. traia_iatp/contracts/__init__.py +12 -0
  12. traia_iatp/contracts/iatp_contracts_config.py +263 -0
  13. traia_iatp/contracts/wallet_creator.py +255 -0
  14. traia_iatp/core/__init__.py +43 -0
  15. traia_iatp/core/models.py +172 -0
  16. traia_iatp/d402/__init__.py +55 -0
  17. traia_iatp/d402/chains.py +102 -0
  18. traia_iatp/d402/client.py +150 -0
  19. traia_iatp/d402/clients/__init__.py +7 -0
  20. traia_iatp/d402/clients/base.py +218 -0
  21. traia_iatp/d402/clients/httpx.py +219 -0
  22. traia_iatp/d402/common.py +114 -0
  23. traia_iatp/d402/encoding.py +28 -0
  24. traia_iatp/d402/examples/client_example.py +197 -0
  25. traia_iatp/d402/examples/server_example.py +171 -0
  26. traia_iatp/d402/facilitator.py +453 -0
  27. traia_iatp/d402/fastapi_middleware/__init__.py +6 -0
  28. traia_iatp/d402/fastapi_middleware/middleware.py +225 -0
  29. traia_iatp/d402/fastmcp_middleware.py +147 -0
  30. traia_iatp/d402/mcp_middleware.py +434 -0
  31. traia_iatp/d402/middleware.py +193 -0
  32. traia_iatp/d402/models.py +116 -0
  33. traia_iatp/d402/networks.py +98 -0
  34. traia_iatp/d402/path.py +43 -0
  35. traia_iatp/d402/payment_introspection.py +104 -0
  36. traia_iatp/d402/payment_signing.py +178 -0
  37. traia_iatp/d402/paywall.py +119 -0
  38. traia_iatp/d402/starlette_middleware.py +326 -0
  39. traia_iatp/d402/template.py +1 -0
  40. traia_iatp/d402/types.py +300 -0
  41. traia_iatp/mcp/__init__.py +18 -0
  42. traia_iatp/mcp/client.py +201 -0
  43. traia_iatp/mcp/d402_mcp_tool_adapter.py +361 -0
  44. traia_iatp/mcp/mcp_agent_template.py +481 -0
  45. traia_iatp/mcp/templates/Dockerfile.j2 +80 -0
  46. traia_iatp/mcp/templates/README.md.j2 +310 -0
  47. traia_iatp/mcp/templates/cursor-rules.md.j2 +520 -0
  48. traia_iatp/mcp/templates/deployment_params.json.j2 +20 -0
  49. traia_iatp/mcp/templates/docker-compose.yml.j2 +32 -0
  50. traia_iatp/mcp/templates/dockerignore.j2 +47 -0
  51. traia_iatp/mcp/templates/env.example.j2 +57 -0
  52. traia_iatp/mcp/templates/gitignore.j2 +77 -0
  53. traia_iatp/mcp/templates/mcp_health_check.py.j2 +150 -0
  54. traia_iatp/mcp/templates/pyproject.toml.j2 +32 -0
  55. traia_iatp/mcp/templates/pyrightconfig.json.j2 +22 -0
  56. traia_iatp/mcp/templates/run_local_docker.sh.j2 +390 -0
  57. traia_iatp/mcp/templates/server.py.j2 +175 -0
  58. traia_iatp/mcp/traia_mcp_adapter.py +543 -0
  59. traia_iatp/preview_diagrams.html +181 -0
  60. traia_iatp/registry/__init__.py +26 -0
  61. traia_iatp/registry/atlas_search_indexes.json +280 -0
  62. traia_iatp/registry/embeddings.py +298 -0
  63. traia_iatp/registry/iatp_search_api.py +846 -0
  64. traia_iatp/registry/mongodb_registry.py +771 -0
  65. traia_iatp/registry/readmes/ATLAS_SEARCH_INDEXES.md +252 -0
  66. traia_iatp/registry/readmes/ATLAS_SEARCH_SETUP.md +134 -0
  67. traia_iatp/registry/readmes/AUTHENTICATION_UPDATE.md +124 -0
  68. traia_iatp/registry/readmes/EMBEDDINGS_SETUP.md +172 -0
  69. traia_iatp/registry/readmes/IATP_SEARCH_API_GUIDE.md +257 -0
  70. traia_iatp/registry/readmes/MONGODB_X509_AUTH.md +208 -0
  71. traia_iatp/registry/readmes/README.md +251 -0
  72. traia_iatp/registry/readmes/REFACTORING_SUMMARY.md +191 -0
  73. traia_iatp/scripts/__init__.py +2 -0
  74. traia_iatp/scripts/create_wallet.py +244 -0
  75. traia_iatp/server/__init__.py +15 -0
  76. traia_iatp/server/a2a_server.py +219 -0
  77. traia_iatp/server/example_template_usage.py +72 -0
  78. traia_iatp/server/iatp_server_agent_generator.py +237 -0
  79. traia_iatp/server/iatp_server_template_generator.py +235 -0
  80. traia_iatp/server/templates/.dockerignore.j2 +48 -0
  81. traia_iatp/server/templates/Dockerfile.j2 +49 -0
  82. traia_iatp/server/templates/README.md +137 -0
  83. traia_iatp/server/templates/README.md.j2 +425 -0
  84. traia_iatp/server/templates/__init__.py +1 -0
  85. traia_iatp/server/templates/__main__.py.j2 +565 -0
  86. traia_iatp/server/templates/agent.py.j2 +94 -0
  87. traia_iatp/server/templates/agent_config.json.j2 +22 -0
  88. traia_iatp/server/templates/agent_executor.py.j2 +279 -0
  89. traia_iatp/server/templates/docker-compose.yml.j2 +23 -0
  90. traia_iatp/server/templates/env.example.j2 +84 -0
  91. traia_iatp/server/templates/gitignore.j2 +78 -0
  92. traia_iatp/server/templates/grpc_server.py.j2 +218 -0
  93. traia_iatp/server/templates/pyproject.toml.j2 +78 -0
  94. traia_iatp/server/templates/run_local_docker.sh.j2 +103 -0
  95. traia_iatp/server/templates/server.py.j2 +243 -0
  96. traia_iatp/special_agencies/__init__.py +4 -0
  97. traia_iatp/special_agencies/registry_search_agency.py +392 -0
  98. traia_iatp/utils/__init__.py +10 -0
  99. traia_iatp/utils/docker_utils.py +251 -0
  100. traia_iatp/utils/general.py +64 -0
  101. traia_iatp/utils/iatp_utils.py +126 -0
  102. traia_iatp-0.1.29.dist-info/METADATA +423 -0
  103. traia_iatp-0.1.29.dist-info/RECORD +107 -0
  104. traia_iatp-0.1.29.dist-info/WHEEL +5 -0
  105. traia_iatp-0.1.29.dist-info/entry_points.txt +2 -0
  106. traia_iatp-0.1.29.dist-info/licenses/LICENSE +21 -0
  107. traia_iatp-0.1.29.dist-info/top_level.txt +1 -0
@@ -0,0 +1,172 @@
1
+ """Core data models for IATP."""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional, Dict, Any, List
5
+ from pydantic import BaseModel, Field, HttpUrl
6
+ from enum import Enum
7
+
8
+
9
+ class MCPServerType(str, Enum):
10
+ """Types of MCP servers."""
11
+ STREAMABLE_HTTP = "streamable-http"
12
+
13
+
14
+ class MCPServer(BaseModel):
15
+ """MCP Server specification."""
16
+ id: Optional[str] = Field(default=None, description="Unique identifier")
17
+ name: str = Field(..., description="Name of the MCP server")
18
+ url: str = Field(..., description="URL or path to connect to the MCP server")
19
+ server_type: MCPServerType = Field(default=MCPServerType.STREAMABLE_HTTP, description="Type of MCP server connection")
20
+ description: str = Field(..., description="Description of what the server enables")
21
+ capabilities: List[str] = Field(default_factory=list, description="List of capabilities/APIs")
22
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
23
+
24
+ class ConfigDict:
25
+ json_encoders = {HttpUrl: str}
26
+
27
+
28
+ class AgentSkill(BaseModel):
29
+ """IATP Agent skill definition."""
30
+ id: str = Field(..., description="Unique skill identifier")
31
+ name: str = Field(..., description="Human-readable skill name")
32
+ description: str = Field(..., description="Detailed skill description")
33
+ examples: List[str] = Field(default_factory=list, description="Example usage patterns")
34
+ input_modes: List[str] = Field(default_factory=list, description="Supported input modes")
35
+ output_modes: List[str] = Field(default_factory=list, description="Supported output modes")
36
+ tags: List[str] = Field(default_factory=list, description="Tags for categorization")
37
+
38
+
39
+ class AgentCapabilities(BaseModel):
40
+ """IATP Agent capabilities."""
41
+ streaming: bool = Field(default=False, description="Supports SSE streaming")
42
+ push_notifications: bool = Field(default=False, description="Supports push notifications")
43
+ state_transition_history: bool = Field(default=False, description="Maintains state history")
44
+ custom_features: Dict[str, Any] = Field(default_factory=dict, description="Custom capabilities")
45
+
46
+
47
+ class AgentCard(BaseModel):
48
+ """IATP Agent card for discovery and initialization."""
49
+ name: str = Field(..., description="Agent name")
50
+ description: str = Field(..., description="Agent description")
51
+ version: str = Field(..., description="Agent version")
52
+ skills: List[AgentSkill] = Field(default_factory=list, description="Available skills")
53
+ capabilities: AgentCapabilities = Field(default_factory=AgentCapabilities, description="Agent capabilities")
54
+ default_input_modes: List[str] = Field(default_factory=list, description="Default input modes")
55
+ default_output_modes: List[str] = Field(default_factory=list, description="Default output modes")
56
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
57
+
58
+
59
+ class IATPEndpoints(BaseModel):
60
+ """IATP server endpoints configuration.
61
+
62
+ Note: The A2A protocol only defines a minimal set of endpoints:
63
+ - Root path (/) for JSON-RPC
64
+ - /.well-known/agent.json for agent card
65
+ - /a2a/tasks/* for SSE subscriptions (if streaming is supported)
66
+
67
+ Health and info endpoints are NOT part of the A2A protocol standard.
68
+ """
69
+ base_url: str = Field(..., description="Base URL of the IATP server")
70
+ iatp_endpoint: str = Field(..., description="Main IATP JSON-RPC endpoint (usually at root path)")
71
+ streaming_endpoint: Optional[str] = Field(None, description="SSE streaming endpoint (same as iatp_endpoint when supported)")
72
+ health_endpoint: Optional[str] = Field(None, description="Health check endpoint (not part of A2A protocol)")
73
+ info_endpoint: Optional[str] = Field(None, description="Agent info endpoint (not part of A2A protocol)")
74
+ agent_card_endpoint: str = Field(..., description="Agent card endpoint (.well-known/agent.json)")
75
+ subscribe_endpoint: Optional[str] = Field(None, description="SSE subscription endpoint (/a2a/tasks/subscribe)")
76
+ resubscribe_endpoint: Optional[str] = Field(None, description="SSE resubscription endpoint (/a2a/tasks/resubscribe)")
77
+
78
+
79
+ class UtilityAgentStatus(str, Enum):
80
+ """Status of a utility agent."""
81
+ GENERATED = "generated"
82
+ DEPLOYING = "deploying"
83
+ DEPLOYED = "deployed"
84
+ RUNNING = "running"
85
+ STOPPED = "stopped"
86
+ ERROR = "error"
87
+
88
+
89
+ #this is the model for internal management of utility agents by their creator and traia protocol (should be persisted into db)
90
+ class UtilityAgent(BaseModel):
91
+ """Utility Agent configuration and metadata."""
92
+ id: str = Field(..., description="Unique identifier for the agent")
93
+ name: str = Field(..., description="Name of the utility agent")
94
+ description: str = Field(..., description="Description of the agent's purpose")
95
+ mcp_server_id: str = Field(..., description="ID of the associated MCP server")
96
+
97
+ # IATP specific fields
98
+ agent_card: Optional[AgentCard] = Field(None, description="IATP agent card for discovery")
99
+ endpoints: Optional[IATPEndpoints] = Field(None, description="IATP endpoints configuration")
100
+
101
+ capabilities: List[str] = Field(default_factory=list, description="List of exposed capabilities")
102
+ status: UtilityAgentStatus = Field(default=UtilityAgentStatus.GENERATED)
103
+ code_path: Optional[str] = Field(None, description="Path to generated code")
104
+ docker_image: Optional[str] = Field(None, description="Docker image name when built")
105
+ github_repo: Optional[str] = Field(None, description="GitHub repository URL")
106
+ cloud_run_url: Optional[str] = Field(None, description="Cloud Run deployment URL")
107
+
108
+ # X402 payment configuration
109
+ contract_address: Optional[str] = Field(None, description="On-chain utility agent contract address")
110
+ operator_address: Optional[str] = Field(None, description="Operator address for signing attestations")
111
+ d402_enabled: bool = Field(default=False, description="Whether d402 payments are enabled")
112
+ d402_config: Optional[Dict[str, Any]] = Field(None, description="X402 payment configuration")
113
+
114
+ # Search and discovery
115
+ search_text: Optional[str] = Field(None, description="Concatenated searchable text")
116
+ tags: List[str] = Field(default_factory=list, description="Tags for search and categorization")
117
+
118
+ created_at: datetime = Field(default_factory=datetime.utcnow)
119
+ updated_at: datetime = Field(default_factory=datetime.utcnow)
120
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
121
+
122
+ class ConfigDict:
123
+ json_encoders = {HttpUrl: str, datetime: lambda v: v.isoformat()}
124
+
125
+
126
+ #this is a model for registry discovery that will be used by the mongodb indexes
127
+ class UtilityAgentRegistryEntry(BaseModel):
128
+ """Registry entry for a deployed utility agent."""
129
+ agent_id: str = Field(..., description="ID of the utility agent")
130
+ name: str = Field(..., description="Name for discovery")
131
+ description: str = Field(..., description="Description for search")
132
+
133
+ # Base URL for the agent - all endpoints are derived from this
134
+ base_url: Optional[str] = Field(None, description="Base URL of the deployed agent")
135
+
136
+ # Enhanced IATP discovery fields
137
+ agent_card: Optional[AgentCard] = Field(None, description="IATP agent card")
138
+ endpoints: Optional[IATPEndpoints] = Field(None, description="IATP endpoints")
139
+
140
+ capabilities: List[str] = Field(..., description="List of capabilities")
141
+ skills: List[AgentSkill] = Field(default_factory=list, description="Detailed skills from agent card")
142
+ tags: List[str] = Field(default_factory=list, description="Tags for search")
143
+
144
+ # X402 payment information
145
+ contract_address: Optional[str] = Field(None, description="On-chain utility agent contract address")
146
+ d402_enabled: bool = Field(default=False, description="Whether d402 payments are enabled")
147
+ d402_payment_info: Optional[Dict[str, Any]] = Field(None, description="X402 payment information")
148
+
149
+ # Search optimization
150
+ search_text: Optional[str] = Field(None, description="Full text for search")
151
+
152
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
153
+ registered_at: datetime = Field(default_factory=datetime.utcnow)
154
+ last_health_check: Optional[datetime] = Field(None, description="Last successful health check")
155
+ is_active: bool = Field(default=True, description="Whether the agent is active")
156
+
157
+ class ConfigDict:
158
+ json_encoders = {HttpUrl: str, datetime: lambda v: v.isoformat()}
159
+
160
+
161
+ class IATPRequest(BaseModel):
162
+ """IATP protocol request."""
163
+ action: str = Field(..., description="Action to perform")
164
+ parameters: Dict[str, Any] = Field(default_factory=dict, description="Action parameters")
165
+ context: Dict[str, Any] = Field(default_factory=dict, description="Request context")
166
+
167
+
168
+ class IATPResponse(BaseModel):
169
+ """IATP protocol response."""
170
+ result: Any = Field(..., description="Result of the action")
171
+ status: str = Field(..., description="Status of the response")
172
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Response metadata")
@@ -0,0 +1,55 @@
1
+ """D402 payment integration for IATP protocol.
2
+
3
+ This module provides d402 (HTTP 402 Payment Required) payment capabilities
4
+ for the Inter-Agent Transfer Protocol (IATP). It enables utility agents to
5
+ accept payments and client agents to send payments for API access.
6
+
7
+ Components:
8
+ - middleware: FastAPI middleware for accepting d402 payments
9
+ - client: d402 client integration for sending payments
10
+ - facilitator: Custom facilitator that interfaces with IATPSettlementLayer
11
+ - models: Payment configuration models
12
+ """
13
+
14
+ from .models import (
15
+ D402Config,
16
+ D402PaymentInfo,
17
+ D402ServicePrice,
18
+ PaymentScheme,
19
+ )
20
+ from .middleware import D402IATPMiddleware, require_iatp_payment
21
+ from .client import D402IATPClient
22
+ from .facilitator import IATPSettlementFacilitator
23
+ from .fastmcp_middleware import (
24
+ D402MCPMiddleware,
25
+ create_d402_mcp_middleware,
26
+ )
27
+ from .mcp_middleware import (
28
+ EndpointPaymentInfo,
29
+ get_active_api_key,
30
+ require_payment_for_tool,
31
+ settle_payment
32
+ )
33
+ from .clients.httpx import d402_payment_hooks, d402HttpxClient
34
+ from .clients.base import decode_x_payment_response
35
+
36
+ __all__ = [
37
+ "D402Config",
38
+ "D402PaymentInfo",
39
+ "D402ServicePrice",
40
+ "PaymentScheme",
41
+ "D402IATPMiddleware",
42
+ "require_iatp_payment",
43
+ "D402IATPClient",
44
+ "IATPSettlementFacilitator",
45
+ "D402MCPMiddleware",
46
+ "create_d402_mcp_middleware",
47
+ "EndpointPaymentInfo",
48
+ "get_active_api_key",
49
+ "require_payment_for_tool",
50
+ "settle_payment",
51
+ "d402_payment_hooks",
52
+ "d402HttpxClient",
53
+ "decode_x_payment_response",
54
+ ]
55
+
@@ -0,0 +1,102 @@
1
+ NETWORK_TO_ID = {
2
+ "sepolia": "11155111", # Ethereum Sepolia testnet
3
+ "base-sepolia": "84532",
4
+ "base": "8453",
5
+ "avalanche-fuji": "43113",
6
+ "avalanche": "43114",
7
+ }
8
+
9
+
10
+ def get_chain_id(network: str) -> str:
11
+ """Get the chain ID for a given network
12
+ Supports string encoded chain IDs and human readable networks
13
+ """
14
+ try:
15
+ int(network)
16
+ return network
17
+ except ValueError:
18
+ pass
19
+ if network not in NETWORK_TO_ID:
20
+ raise ValueError(f"Unsupported network: {network}")
21
+ return NETWORK_TO_ID[network]
22
+
23
+
24
+ KNOWN_TOKENS = {
25
+ "11155111": [ # Sepolia testnet
26
+ {
27
+ "human_name": "usdc",
28
+ "address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
29
+ "name": "USD Coin",
30
+ "decimals": 6,
31
+ "version": "2",
32
+ }
33
+ ],
34
+ "84532": [
35
+ {
36
+ "human_name": "usdc",
37
+ "address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
38
+ "name": "USDC",
39
+ "decimals": 6,
40
+ "version": "2",
41
+ }
42
+ ],
43
+ "8453": [
44
+ {
45
+ "human_name": "usdc",
46
+ "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
47
+ "name": "USD Coin", # needs to be exactly what is returned by name() on contract
48
+ "decimals": 6,
49
+ "version": "2",
50
+ }
51
+ ],
52
+ "43113": [
53
+ {
54
+ "human_name": "usdc",
55
+ "address": "0x5425890298aed601595a70AB815c96711a31Bc65",
56
+ "name": "USD Coin",
57
+ "decimals": 6,
58
+ "version": "2",
59
+ }
60
+ ],
61
+ "43114": [
62
+ {
63
+ "human_name": "usdc",
64
+ "address": "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
65
+ "name": "USDC",
66
+ "decimals": 6,
67
+ "version": "2",
68
+ }
69
+ ],
70
+ }
71
+
72
+
73
+ def get_token_name(chain_id: str, address: str) -> str:
74
+ """Get the token name for a given chain and address"""
75
+ for token in KNOWN_TOKENS[chain_id]:
76
+ if token["address"] == address:
77
+ return token["name"]
78
+ raise ValueError(f"Token not found for chain {chain_id} and address {address}")
79
+
80
+
81
+ def get_token_version(chain_id: str, address: str) -> str:
82
+ """Get the token version for a given chain and address"""
83
+ for token in KNOWN_TOKENS[chain_id]:
84
+ if token["address"] == address:
85
+ return token["version"]
86
+ raise ValueError(f"Token not found for chain {chain_id} and address {address}")
87
+
88
+
89
+ def get_token_decimals(chain_id: str, address: str) -> int:
90
+ """Get the token decimals for a given chain and address"""
91
+ for token in KNOWN_TOKENS[chain_id]:
92
+ if token["address"] == address:
93
+ return token["decimals"]
94
+ raise ValueError(f"Token not found for chain {chain_id} and address {address}")
95
+
96
+
97
+ def get_default_token_address(chain_id: str, token_type: str = "usdc") -> str:
98
+ """Get the default token address for a given chain and token type"""
99
+ for token in KNOWN_TOKENS[chain_id]:
100
+ if token["human_name"] == token_type:
101
+ return token["address"]
102
+ raise ValueError(f"Token type '{token_type}' not found for chain {chain_id}")
@@ -0,0 +1,150 @@
1
+ """D402 client for IATP agent-to-agent payments."""
2
+
3
+ import logging
4
+ from typing import Optional, Dict, Any
5
+ from eth_account import Account
6
+ from .clients.base import d402Client
7
+ from .types import PaymentRequirements
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class D402IATPClient:
13
+ """Client for making d402 payments in IATP protocol.
14
+
15
+ This wraps the Coinbase d402 client and provides IATP-specific functionality,
16
+ including integration with utility agent smart contracts.
17
+ """
18
+
19
+ def __init__(
20
+ self,
21
+ account: Account,
22
+ max_value: Optional[int] = None,
23
+ agent_contract_address: Optional[str] = None,
24
+ operator_private_key: Optional[str] = None
25
+ ):
26
+ """Initialize the d402 IATP client.
27
+
28
+ Args:
29
+ account: eth_account.Account instance for signing payments
30
+ max_value: Optional maximum allowed payment amount in base units
31
+ agent_contract_address: Optional address of the client agent's smart contract
32
+ operator_private_key: Optional operator private key for signing service requests
33
+ """
34
+ self.account = account
35
+ self.max_value = max_value
36
+ self.agent_contract_address = agent_contract_address
37
+ self.operator_private_key = operator_private_key
38
+
39
+ # Initialize the underlying d402 client
40
+ self.d402_client = d402Client(
41
+ account=account,
42
+ max_value=max_value
43
+ )
44
+
45
+ def create_payment_header(
46
+ self,
47
+ payment_requirements: PaymentRequirements,
48
+ d402_version: int = 1
49
+ ) -> str:
50
+ """Create a payment header for the given requirements.
51
+
52
+ This creates an EIP-3009 signed payment authorization that the facilitator
53
+ can use to pull funds from the client agent's wallet.
54
+
55
+ Args:
56
+ payment_requirements: Selected payment requirements from server
57
+ d402_version: d402 protocol version
58
+
59
+ Returns:
60
+ Base64-encoded signed payment header for X-PAYMENT header
61
+ """
62
+ return self.d402_client.create_payment_header(
63
+ payment_requirements=payment_requirements,
64
+ d402_version=d402_version
65
+ )
66
+
67
+ def select_payment_requirements(
68
+ self,
69
+ accepts: list[PaymentRequirements],
70
+ network_filter: Optional[str] = None,
71
+ scheme_filter: Optional[str] = "exact"
72
+ ) -> PaymentRequirements:
73
+ """Select payment requirements from available options.
74
+
75
+ Args:
76
+ accepts: List of accepted payment requirements from server
77
+ network_filter: Optional network to filter by
78
+ scheme_filter: Optional scheme to filter by (default: "exact")
79
+
80
+ Returns:
81
+ Selected payment requirements
82
+
83
+ Raises:
84
+ UnsupportedSchemeException: If no supported scheme found
85
+ PaymentAmountExceededError: If amount exceeds max_value
86
+ """
87
+ return self.d402_client.select_payment_requirements(
88
+ accepts=accepts,
89
+ network_filter=network_filter,
90
+ scheme_filter=scheme_filter
91
+ )
92
+
93
+ def get_payment_info_for_agent_card(self, agent_card: dict) -> Optional[Dict[str, Any]]:
94
+ """Extract d402 payment information from an agent card.
95
+
96
+ Args:
97
+ agent_card: Agent card dictionary
98
+
99
+ Returns:
100
+ Payment information if available, None otherwise
101
+ """
102
+ metadata = agent_card.get("metadata", {})
103
+ return metadata.get("d402")
104
+
105
+
106
+ def create_iatp_payment_client(
107
+ private_key: str,
108
+ max_value_usd: Optional[float] = None,
109
+ agent_contract_address: Optional[str] = None
110
+ ) -> D402IATPClient:
111
+ """Convenience function to create an IATP payment client.
112
+
113
+ Args:
114
+ private_key: Hex-encoded private key (with or without 0x prefix)
115
+ max_value_usd: Optional maximum payment in USD
116
+ agent_contract_address: Optional agent contract address
117
+
118
+ Returns:
119
+ Configured D402IATPClient
120
+
121
+ Example:
122
+ client = create_iatp_payment_client(
123
+ private_key="0x...",
124
+ max_value_usd=10.0 # Max $10 per request
125
+ )
126
+
127
+ # Use with httpx
128
+ from .clients.httpx import Httpd402Client
129
+ http_client = Httpd402Client(client)
130
+ response = await http_client.get("https://agent.example.com/api")
131
+ """
132
+ # Remove 0x prefix if present
133
+ if private_key.startswith("0x"):
134
+ private_key = private_key[2:]
135
+
136
+ # Create eth_account.Account
137
+ account = Account.from_key(private_key)
138
+
139
+ # Convert USD to atomic units (assuming USDC with 6 decimals)
140
+ max_value = None
141
+ if max_value_usd is not None:
142
+ max_value = int(max_value_usd * 1_000_000) # USDC has 6 decimals
143
+
144
+ return D402IATPClient(
145
+ account=account,
146
+ max_value=max_value,
147
+ agent_contract_address=agent_contract_address,
148
+ operator_private_key=private_key
149
+ )
150
+
@@ -0,0 +1,7 @@
1
+ """D402 client implementations."""
2
+
3
+ from .base import d402Client, decode_x_payment_response
4
+ from .httpx import d402_payment_hooks, d402HttpxClient
5
+
6
+ __all__ = ["d402Client", "decode_x_payment_response", "d402_payment_hooks", "d402HttpxClient"]
7
+