zyndai-agent 0.1.4__py3-none-any.whl → 0.2.1__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.
zyndai_agent/__init__.py CHANGED
@@ -0,0 +1,20 @@
1
+ from zyndai_agent.agent import ZyndAIAgent, AgentConfig
2
+ from zyndai_agent.communication import AgentCommunicationManager, MQTTMessage
3
+ from zyndai_agent.webhook_communication import WebhookCommunicationManager
4
+ from zyndai_agent.message import AgentMessage
5
+ from zyndai_agent.search import SearchAndDiscoveryManager, AgentSearchResponse
6
+ from zyndai_agent.identity import IdentityManager
7
+ from zyndai_agent.payment import X402PaymentProcessor
8
+
9
+ __all__ = [
10
+ "ZyndAIAgent",
11
+ "AgentConfig",
12
+ "AgentCommunicationManager",
13
+ "WebhookCommunicationManager",
14
+ "MQTTMessage",
15
+ "AgentMessage",
16
+ "SearchAndDiscoveryManager",
17
+ "AgentSearchResponse",
18
+ "IdentityManager",
19
+ "X402PaymentProcessor",
20
+ ]
zyndai_agent/agent.py CHANGED
@@ -3,6 +3,7 @@ import requests
3
3
  from zyndai_agent.search import SearchAndDiscoveryManager
4
4
  from zyndai_agent.identity import IdentityManager
5
5
  from zyndai_agent.communication import AgentCommunicationManager
6
+ from zyndai_agent.webhook_communication import WebhookCommunicationManager
6
7
  from zyndai_agent.payment import X402PaymentProcessor
7
8
  from pydantic import BaseModel
8
9
  from typing import Optional
@@ -13,46 +14,84 @@ class AgentConfig(BaseModel):
13
14
  auto_reconnect: bool = True
14
15
  message_history_limit: int = 100
15
16
  registry_url: str = "http://localhost:3002"
16
- mqtt_broker_url: str
17
+
18
+ # Webhook configuration (new)
19
+ webhook_host: Optional[str] = "0.0.0.0"
20
+ webhook_port: Optional[int] = 5000
21
+ webhook_url: Optional[str] = None # Public URL if behind NAT
22
+ api_key: Optional[str] = None # API key for webhook registration
23
+
24
+ # MQTT configuration (deprecated, kept for backward compatibility)
25
+ mqtt_broker_url: Optional[str] = None
26
+ default_outbox_topic: Optional[str] = None
27
+
28
+ # Common configuration
17
29
  identity_credential_path: str
18
30
  identity_credential: Optional[dict] = None
19
- default_outbox_topic: Optional[str] = None
20
31
  secret_seed: str = None
32
+ agent_id: str = None
21
33
 
22
- class ZyndAIAgent(SearchAndDiscoveryManager, IdentityManager, AgentCommunicationManager, X402PaymentProcessor):
34
+ price: Optional[str] = None
35
+ pay_to_address: Optional[str] = None
23
36
 
24
- def __init__(self, agent_config: AgentConfig):
37
+ class ZyndAIAgent(SearchAndDiscoveryManager, IdentityManager, X402PaymentProcessor, WebhookCommunicationManager):
38
+
39
+ def __init__(self, agent_config: AgentConfig):
25
40
 
26
41
  self.agent_executor: CompiledStateGraph = None
27
- self.agent_config = agent_config
42
+ self.agent_config = agent_config
28
43
  self.x402_processor = X402PaymentProcessor(agent_config.secret_seed)
44
+ self.communication_mode = None # Track which mode is active
29
45
 
30
46
  try:
31
47
  with open(agent_config.identity_credential_path, "r") as f:
32
48
  self.identity_credential = json.load(f)
33
49
  except FileNotFoundError:
34
50
  raise FileNotFoundError(f"Identity credential file not found at {agent_config.identity_credential_path}")
35
-
36
- IdentityManager.__init__(self,agent_config.registry_url)
51
+
52
+ IdentityManager.__init__(self, agent_config.registry_url)
37
53
 
38
54
  SearchAndDiscoveryManager.__init__(
39
55
  self,
40
56
  registry_url=agent_config.registry_url
41
57
  )
42
-
43
- AgentCommunicationManager.__init__(
44
- self,
45
- self.identity_credential["issuer"],
46
- default_inbox_topic=f"{self.identity_credential['issuer']}/inbox",
47
- default_outbox_topic=agent_config.default_outbox_topic,
48
- auto_reconnect=True,
49
- message_history_limit=agent_config.message_history_limit,
50
- identity_credential=self.identity_credential,
51
- secret_seed=agent_config.secret_seed,
52
- mqtt_broker_url=agent_config.mqtt_broker_url
53
- )
54
58
 
55
- self.update_agent_mqtt_info()
59
+ # Determine communication mode: webhook or MQTT
60
+ # Prefer webhook if webhook_port is configured
61
+ if agent_config.webhook_port is not None and agent_config.mqtt_broker_url is None:
62
+ # Use webhook mode
63
+ self.communication_mode = "webhook"
64
+ WebhookCommunicationManager.__init__(
65
+ self,
66
+ agent_id=self.identity_credential["issuer"],
67
+ webhook_host=agent_config.webhook_host,
68
+ webhook_port=agent_config.webhook_port,
69
+ webhook_url=agent_config.webhook_url,
70
+ auto_restart=agent_config.auto_reconnect,
71
+ message_history_limit=agent_config.message_history_limit,
72
+ identity_credential=self.identity_credential,
73
+ price=agent_config.price,
74
+ pay_to_address=agent_config.pay_to_address
75
+ )
76
+ self.update_agent_webhook_info()
77
+
78
+ elif agent_config.mqtt_broker_url is not None:
79
+ # Use MQTT mode (backward compatibility)
80
+ self.communication_mode = "mqtt"
81
+ AgentCommunicationManager.__init__(
82
+ self,
83
+ self.identity_credential["issuer"],
84
+ default_inbox_topic=f"{self.identity_credential['issuer']}/inbox",
85
+ default_outbox_topic=agent_config.default_outbox_topic,
86
+ auto_reconnect=True,
87
+ message_history_limit=agent_config.message_history_limit,
88
+ identity_credential=self.identity_credential,
89
+ secret_seed=agent_config.secret_seed,
90
+ mqtt_broker_url=agent_config.mqtt_broker_url
91
+ )
92
+ self.update_agent_mqtt_info()
93
+ else:
94
+ raise ValueError("Either webhook_port or mqtt_broker_url must be configured")
56
95
 
57
96
 
58
97
 
@@ -62,16 +101,53 @@ class ZyndAIAgent(SearchAndDiscoveryManager, IdentityManager, AgentCommunication
62
101
 
63
102
  def update_agent_mqtt_info(self):
64
103
  """Updates the mqtt connection info of the agent into the registry so other agents can find me"""
65
- print(self.agent_config.secret_seed, self.agent_config.mqtt_broker_url, f"{self.agent_config.registry_url}/agents/update-mqtt")
66
- updateResponse = requests.post(
67
- f"{self.agent_config.registry_url}/agents/update-mqtt",
104
+
105
+ updateResponse = requests.patch(
106
+ f"{self.agent_config.registry_url}/agents/update-mqtt",
68
107
  data={
69
108
  "seed": self.agent_config.secret_seed,
70
109
  "mqttUri": self.agent_config.mqtt_broker_url
71
110
  }
72
111
  )
73
- print(updateResponse.status_code,"====")
112
+
74
113
  if (updateResponse.status_code != 201):
75
114
  raise Exception("Failed to update agent connection info in p3 registry.")
76
115
 
77
- print("Synced with the registry...")
116
+ print("Synced with the registry...")
117
+
118
+ def update_agent_webhook_info(self):
119
+ """Updates the webhook URL of the agent into the registry so other agents can find me"""
120
+ if not self.agent_config.api_key:
121
+ raise ValueError("API key is required for webhook registration. Please provide api_key in AgentConfig.")
122
+
123
+ # Prepare headers with API key
124
+ headers = {
125
+ "X-API-KEY": self.agent_config.api_key,
126
+ "Content-Type": "application/json"
127
+ }
128
+
129
+ # Prepare request body
130
+ payload = {
131
+ "agentId": self.agent_config.agent_id,
132
+ "httpWebhookUrl": self.webhook_url
133
+ }
134
+
135
+ updateResponse = requests.patch(
136
+ f"{self.agent_config.registry_url}/agents/update-webhook",
137
+ json=payload,
138
+ headers=headers
139
+ )
140
+
141
+ if updateResponse.status_code != 200:
142
+ raise Exception(f"Failed to update agent webhook info in Zynd registry. Status: {updateResponse.status_code}, Response: {updateResponse.text}")
143
+
144
+ print("Synced webhook URL with the registry...")
145
+
146
+ def update_agent_connection_info(self):
147
+ """Updates the agent connection info (webhook or MQTT) in the registry based on communication mode"""
148
+ if self.communication_mode == "webhook":
149
+ self.update_agent_webhook_info()
150
+ elif self.communication_mode == "mqtt":
151
+ self.update_agent_mqtt_info()
152
+ else:
153
+ raise ValueError(f"Unknown communication mode: {self.communication_mode}")
@@ -0,0 +1,111 @@
1
+ import time
2
+ import json
3
+ import uuid
4
+ import logging
5
+ from typing import Optional, Dict, Any
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class AgentMessage:
10
+ """
11
+ Structured message format for agent communication.
12
+
13
+ This class provides a standardized way to format, serialize, and deserialize
14
+ messages exchanged between agents, with support for conversation threading,
15
+ message types, and metadata.
16
+
17
+ Protocol-agnostic: Can be used with MQTT, HTTP webhooks, or other transports.
18
+ """
19
+
20
+ def __init__(
21
+ self,
22
+ content: str,
23
+ sender_id: str,
24
+ sender_did: dict = None,
25
+ receiver_id: Optional[str] = None,
26
+ message_type: str = "query",
27
+ message_id: Optional[str] = None,
28
+ conversation_id: Optional[str] = None,
29
+ in_reply_to: Optional[str] = None,
30
+ metadata: Optional[Dict[str, Any]] = None,
31
+ ):
32
+ """
33
+ Initialize a new agent message.
34
+
35
+ Args:
36
+ content: The main message content
37
+ sender_id: Identifier for the message sender
38
+ sender_did: DID credential of the sender
39
+ receiver_id: Identifier for the intended recipient (None for broadcasts)
40
+ message_type: Type categorization ("query", "response", "broadcast", "system")
41
+ message_id: Unique identifier for this message (auto-generated if None)
42
+ conversation_id: ID grouping related messages (auto-generated if None)
43
+ in_reply_to: ID of the message this is responding to (None if not a reply)
44
+ metadata: Additional contextual information
45
+ """
46
+ self.content = content
47
+ self.sender_id = sender_id
48
+ self.receiver_id = receiver_id
49
+ self.sender_did = sender_did
50
+ self.message_type = message_type
51
+ self.message_id = message_id or str(uuid.uuid4())
52
+ self.conversation_id = conversation_id or str(uuid.uuid4())
53
+ self.in_reply_to = in_reply_to
54
+ self.metadata = metadata or {}
55
+ self.timestamp = time.time()
56
+
57
+ def to_dict(self) -> Dict[str, Any]:
58
+ """Convert message to dictionary format."""
59
+ return {
60
+ "content": self.content,
61
+ "sender_id": self.sender_id,
62
+ "sender_did": self.sender_did,
63
+ "receiver_id": self.receiver_id,
64
+ "message_type": self.message_type,
65
+ "message_id": self.message_id,
66
+ "conversation_id": self.conversation_id,
67
+ "in_reply_to": self.in_reply_to,
68
+ "metadata": self.metadata,
69
+ "timestamp": self.timestamp
70
+ }
71
+
72
+ def to_json(self) -> str:
73
+ """Convert message to JSON string for transmission."""
74
+ return json.dumps(self.to_dict())
75
+
76
+ @classmethod
77
+ def from_dict(cls, data: Dict[str, Any]) -> 'AgentMessage':
78
+ """Create message object from dictionary data."""
79
+ return cls(
80
+ content=data.get("content", ""),
81
+ sender_id=data.get("sender_id", "unknown"),
82
+ sender_did=data.get("sender_did", "unknown"),
83
+ receiver_id=data.get("receiver_id"),
84
+ message_type=data.get("message_type", "query"),
85
+ message_id=data.get("message_id"),
86
+ conversation_id=data.get("conversation_id"),
87
+ in_reply_to=data.get("in_reply_to"),
88
+ metadata=data.get("metadata", {})
89
+ )
90
+
91
+ @classmethod
92
+ def from_json(cls, json_str: str) -> 'AgentMessage':
93
+ """
94
+ Create message object from JSON string.
95
+
96
+ Handles both valid JSON and fallback for plain text messages.
97
+ """
98
+ try:
99
+ data = json.loads(json_str)
100
+ return cls.from_dict(data)
101
+ except json.JSONDecodeError as e:
102
+ logger.error(f"Failed to parse message as JSON: {e}")
103
+ return cls(
104
+ content=json_str,
105
+ sender_id="unknown",
106
+ message_type="raw"
107
+ )
108
+
109
+
110
+ # Backward compatibility alias
111
+ MQTTMessage = AgentMessage
zyndai_agent/search.py CHANGED
@@ -15,7 +15,8 @@ class AgentSearchResponse(TypedDict):
15
15
  id: str
16
16
  name: str
17
17
  description: str
18
- mqttUri: Optional[str]
18
+ mqttUri: Optional[str] # Deprecated, kept for backward compatibility
19
+ webhookUrl: Optional[str] # New field for webhook communication
19
20
  inboxTopic: Optional[str]
20
21
  matchScore: int
21
22
  didIdentifier: str
@@ -0,0 +1,466 @@
1
+ import time
2
+ import logging
3
+ import threading
4
+ import requests
5
+ from flask import Flask, request, jsonify
6
+ from typing import List, Callable, Optional, Dict, Any
7
+ from zyndai_agent.message import AgentMessage
8
+ from zyndai_agent.search import AgentSearchResponse
9
+ from x402.flask.middleware import PaymentMiddleware
10
+
11
+ # Configure logging with a more descriptive format
12
+ logging.basicConfig(
13
+ level=logging.INFO,
14
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
15
+ )
16
+ logger = logging.getLogger("WebhookAgentCommunication")
17
+
18
+
19
+ class WebhookCommunicationManager:
20
+ """
21
+ HTTP Webhook-based communication manager for LangChain agents.
22
+
23
+ This class provides tools for LangChain agents to communicate via HTTP webhooks,
24
+ enabling multi-agent collaboration through a request-response pattern.
25
+ Each agent runs an embedded Flask server to receive messages.
26
+ """
27
+
28
+ identity_credential: dict = None
29
+
30
+ def __init__(
31
+ self,
32
+ agent_id: str,
33
+ webhook_host: str = "0.0.0.0",
34
+ webhook_port: int = 5000,
35
+ webhook_url: Optional[str] = None,
36
+ auto_restart: bool = True,
37
+ message_history_limit: int = 100,
38
+ identity_credential: dict = None,
39
+ price: Optional[str] = "$0.01",
40
+ pay_to_address: Optional[str] = None
41
+ ):
42
+ """
43
+ Initialize the webhook agent communication manager.
44
+
45
+ Args:
46
+ agent_id: Unique identifier for this agent
47
+ webhook_host: Host address to bind the webhook server (default: 0.0.0.0)
48
+ webhook_port: Port number for the webhook server (default: 5000)
49
+ webhook_url: Public webhook URL (auto-generated if None)
50
+ auto_restart: Whether to attempt restart on failure
51
+ message_history_limit: Maximum number of messages to keep in history
52
+ identity_credential: DID credential for this agent
53
+ """
54
+
55
+ self.agent_id = agent_id
56
+ self.webhook_host = webhook_host
57
+ self.webhook_port = webhook_port
58
+ self.webhook_url = webhook_url
59
+ self.auto_restart = auto_restart
60
+ self.message_history_limit = message_history_limit
61
+
62
+ self.identity_credential = identity_credential
63
+
64
+ self.is_running = False
65
+ self.is_agent_connected = False
66
+ self.received_messages = []
67
+ self.message_history = []
68
+ self.message_handlers = []
69
+ self.target_webhook_url = None
70
+ self.pending_responses = {} # Store responses by message_id
71
+
72
+ # Thread safety
73
+ self._lock = threading.Lock()
74
+
75
+ # Create Flask app
76
+ self.flask_app = Flask(f"agent_{agent_id}")
77
+ self.flask_app.logger.setLevel(logging.ERROR) # Suppress Flask logging
78
+
79
+ if price != None and pay_to_address != None:
80
+ self.payment_middleware = PaymentMiddleware(self.flask_app)
81
+ self.payment_middleware.add(
82
+ price,
83
+ pay_to_address,
84
+ path="/webhook"
85
+ )
86
+ else:
87
+ print("Disabling x402, X402 payment config not provided")
88
+
89
+ self._setup_routes()
90
+
91
+ # Start webhook server
92
+ self.start_webhook_server()
93
+
94
+ print("Agent webhook server started")
95
+ print(f"Listening on {self.webhook_url}")
96
+
97
+ def _setup_routes(self):
98
+ """Setup Flask routes for webhook endpoints."""
99
+
100
+ @self.flask_app.route('/webhook', methods=['POST'])
101
+ def webhook_handler():
102
+ return self._handle_webhook_request(sync=False)
103
+
104
+ @self.flask_app.route('/webhook/sync', methods=['POST'])
105
+ def webhook_sync_handler():
106
+ return self._handle_webhook_request(sync=True)
107
+
108
+ @self.flask_app.route('/health', methods=['GET'])
109
+ def health_check():
110
+ return jsonify({
111
+ "status": "ok",
112
+ "agent_id": self.agent_id,
113
+ "timestamp": time.time()
114
+ }), 200
115
+
116
+ def _handle_webhook_request(self, sync=False):
117
+ """Handle incoming webhook POST requests."""
118
+ try:
119
+ # Verify request is JSON
120
+ if not request.is_json:
121
+ logger.error("Received non-JSON request")
122
+ return jsonify({"error": "Content-Type must be application/json"}), 400
123
+
124
+ payload = request.get_json()
125
+
126
+ # Parse message from dict (request.get_json() returns a dict, not a string)
127
+ message = AgentMessage.from_dict(payload)
128
+
129
+ logger.info(f"[{self.agent_id}] Received message from {message.sender_id}")
130
+
131
+ # Auto-connect to sender if not connected
132
+ if not self.is_agent_connected:
133
+ self.is_agent_connected = True
134
+
135
+ # Store in history
136
+ message_with_metadata = {
137
+ "message": message,
138
+ "received_at": time.time(),
139
+ "structured": True,
140
+ "source_ip": request.remote_addr
141
+ }
142
+
143
+ print("\nIncoming Message: ", message.content, "\n")
144
+
145
+ with self._lock:
146
+ self.received_messages.append(message_with_metadata)
147
+ self.message_history.append(message_with_metadata)
148
+
149
+ # Maintain history limit
150
+ if len(self.message_history) > self.message_history_limit:
151
+ self.message_history = self.message_history[-self.message_history_limit:]
152
+
153
+ # Check if synchronous response is requested
154
+ if sync:
155
+ # Wait for handler to process and store response
156
+ # Invoke message handlers synchronously
157
+ for handler in self.message_handlers:
158
+ try:
159
+ handler(message, None) # No topic in webhook context
160
+ except Exception as e:
161
+ logger.error(f"Error in message handler: {e}")
162
+
163
+ # Wait for response (with timeout)
164
+ timeout = 30 # 30 seconds
165
+ start_time = time.time()
166
+ while time.time() - start_time < timeout:
167
+ with self._lock:
168
+ if message.message_id in self.pending_responses:
169
+ response = self.pending_responses.pop(message.message_id)
170
+ return jsonify({
171
+ "status": "success",
172
+ "message_id": message.message_id,
173
+ "response": response,
174
+ "timestamp": time.time()
175
+ }), 200
176
+ time.sleep(0.1) # Small delay to avoid busy waiting
177
+
178
+ # Timeout - no response received
179
+ return jsonify({
180
+ "status": "timeout",
181
+ "message_id": message.message_id,
182
+ "error": "Agent did not respond within timeout period",
183
+ "timestamp": time.time()
184
+ }), 408
185
+ else:
186
+ # Async mode - invoke handlers and return immediately
187
+ for handler in self.message_handlers:
188
+ try:
189
+ handler(message, None) # No topic in webhook context
190
+ except Exception as e:
191
+ logger.error(f"Error in message handler: {e}")
192
+
193
+ # Return success
194
+ return jsonify({
195
+ "status": "received",
196
+ "message_id": message.message_id,
197
+ "timestamp": time.time()
198
+ }), 200
199
+
200
+ except Exception as e:
201
+ logger.error(f"Error handling webhook request: {e}")
202
+ return jsonify({"error": str(e)}), 500
203
+
204
+ def start_webhook_server(self):
205
+ """Start Flask webhook server in background thread."""
206
+ if self.is_running:
207
+ logger.warning("Webhook server already running")
208
+ return
209
+
210
+ # Try to bind to configured port, retry with different ports if needed
211
+ max_retries = 10
212
+ server_started = False
213
+
214
+ for attempt in range(max_retries):
215
+ try:
216
+ port = self.webhook_port + attempt
217
+
218
+ def run_flask():
219
+ self.flask_app.run(
220
+ host=self.webhook_host,
221
+ port=port,
222
+ debug=False,
223
+ use_reloader=False,
224
+ threaded=True
225
+ )
226
+
227
+ self.flask_thread = threading.Thread(
228
+ target=run_flask,
229
+ daemon=True,
230
+ name=f"WebhookServer-{self.agent_id}"
231
+ )
232
+ self.flask_thread.start()
233
+
234
+ # Update actual port used
235
+ self.webhook_port = port
236
+
237
+ # Update webhook URL if not manually configured
238
+ if self.webhook_url is None:
239
+ # Use localhost for local development, can be overridden
240
+ self.webhook_url = f"http://localhost:{port}/webhook"
241
+
242
+ self.is_running = True
243
+ server_started = True
244
+
245
+ # Wait for server to start
246
+ time.sleep(1.5)
247
+
248
+ logger.info(f"Webhook server started on {self.webhook_host}:{port}")
249
+ break
250
+
251
+ except OSError as e:
252
+ if "Address already in use" in str(e) and attempt < max_retries - 1:
253
+ logger.warning(f"Port {port} already in use, trying next port...")
254
+ continue
255
+ else:
256
+ logger.error(f"Failed to start webhook server: {e}")
257
+ raise
258
+
259
+ if not server_started:
260
+ raise RuntimeError("Failed to start webhook server after multiple attempts")
261
+
262
+ def stop_webhook_server(self):
263
+ """Stop the webhook server and cleanup resources."""
264
+ if not self.is_running:
265
+ logger.warning("Webhook server not running")
266
+ return
267
+
268
+ self.is_running = False
269
+ logger.info(f"[{self.agent_id}] Webhook server stopped")
270
+
271
+ def send_message(
272
+ self,
273
+ message_content: str,
274
+ message_type: str = "query",
275
+ receiver_id: Optional[str] = None
276
+ ) -> str:
277
+ """
278
+ Send a message to another agent via HTTP POST.
279
+
280
+ Args:
281
+ message_content: The main content of the message
282
+ message_type: The type of message being sent
283
+ receiver_id: Specific recipient ID
284
+
285
+ Returns:
286
+ Status message or error
287
+ """
288
+ if not self.is_running:
289
+ return "Webhook server not running. Cannot send messages."
290
+
291
+ if not self.target_webhook_url:
292
+ return "No target agent connected. Use connect_agent() first."
293
+
294
+ try:
295
+ # Create structured message
296
+ message = AgentMessage(
297
+ content=message_content,
298
+ sender_id=self.agent_id,
299
+ receiver_id=receiver_id,
300
+ message_type=message_type,
301
+ sender_did=self.identity_credential
302
+ )
303
+
304
+ # Convert to JSON and send directly
305
+ json_payload = message.to_json()
306
+
307
+ # Send HTTP POST request with plain JSON
308
+ response = requests.post(
309
+ self.target_webhook_url,
310
+ json=json_payload,
311
+ headers={"Content-Type": "application/json"},
312
+ timeout=30 # 30 second timeout
313
+ )
314
+
315
+ # Check response
316
+ if response.status_code == 200:
317
+ logger.info(f"Message sent successfully to {self.target_webhook_url}")
318
+
319
+ # Add to history
320
+ with self._lock:
321
+ self.message_history.append({
322
+ "message": message,
323
+ "sent_at": time.time(),
324
+ "direction": "outgoing",
325
+ "target_url": self.target_webhook_url
326
+ })
327
+
328
+ if len(self.message_history) > self.message_history_limit:
329
+ self.message_history = self.message_history[-self.message_history_limit:]
330
+
331
+ return f"Message sent successfully to topic '{self.target_webhook_url}'"
332
+ else:
333
+ error_msg = f"Failed to send message. HTTP {response.status_code}: {response.text}"
334
+ logger.error(error_msg)
335
+ return error_msg
336
+
337
+ except requests.exceptions.Timeout:
338
+ error_msg = "Error: Request timed out. Target agent may be offline."
339
+ logger.error(error_msg)
340
+ return error_msg
341
+ except requests.exceptions.ConnectionError:
342
+ error_msg = "Error: Could not connect to target agent. Agent may be offline."
343
+ logger.error(error_msg)
344
+ return error_msg
345
+ except Exception as e:
346
+ error_msg = f"Error sending message: {str(e)}"
347
+ logger.error(f"[{self.agent_id}] {error_msg}")
348
+ return error_msg
349
+
350
+ def read_messages(self) -> str:
351
+ """
352
+ Read and clear the current message queue.
353
+
354
+ Returns:
355
+ Formatted string of received messages
356
+ """
357
+ if not self.is_running:
358
+ return "Webhook server not running."
359
+
360
+ if not self.received_messages:
361
+ return "No new messages in the queue."
362
+
363
+ # Format messages for output
364
+ formatted_messages = []
365
+ for item in self.received_messages:
366
+ message = item["message"]
367
+
368
+ formatted_msg = (
369
+ f"From: {message.sender_id}\n"
370
+ f"Type: {message.message_type}\n"
371
+ f"Content: {message.content}\n"
372
+ )
373
+ formatted_messages.append(formatted_msg)
374
+
375
+ # Create a combined output
376
+ output = "Messages received:\n\n" + "\n---\n".join(formatted_messages)
377
+
378
+ # Clear the received messages queue but keep them in history
379
+ with self._lock:
380
+ self.received_messages.clear()
381
+
382
+ return output
383
+
384
+ def add_message_handler(self, handler_function: Callable) -> None:
385
+ """
386
+ Add a custom message handler function.
387
+
388
+ Args:
389
+ handler_function: Function to call when messages arrive
390
+ Should accept (message, topic) parameters
391
+ """
392
+ with self._lock:
393
+ self.message_handlers.append(handler_function)
394
+ logger.info(f"[{self.agent_id}] Added custom message handler")
395
+
396
+ def register_handler(self, handler_fn: Callable[[AgentMessage, str], None]):
397
+ """Alias for add_message_handler for backward compatibility."""
398
+ self.add_message_handler(handler_fn)
399
+
400
+ def set_response(self, message_id: str, response: str):
401
+ """
402
+ Set a response for a specific message ID (for synchronous responses).
403
+
404
+ Args:
405
+ message_id: The ID of the message being responded to
406
+ response: The response content
407
+ """
408
+ with self._lock:
409
+ self.pending_responses[message_id] = response
410
+ logger.info(f"[{self.agent_id}] Set response for message {message_id}")
411
+
412
+ def get_connection_status(self) -> Dict[str, Any]:
413
+ """
414
+ Get the current webhook server status and statistics.
415
+
416
+ Returns:
417
+ Dictionary with connection information
418
+ """
419
+ return {
420
+ "agent_id": self.agent_id,
421
+ "is_running": self.is_running,
422
+ "webhook_url": self.webhook_url,
423
+ "webhook_port": self.webhook_port,
424
+ "target_webhook_url": self.target_webhook_url,
425
+ "pending_messages": len(self.received_messages),
426
+ "message_history_count": len(self.message_history)
427
+ }
428
+
429
+ def get_message_history(
430
+ self,
431
+ limit: int = None,
432
+ filter_by_topic: str = None
433
+ ) -> List[Dict[str, Any]]:
434
+ """
435
+ Get the message history with optional filtering.
436
+
437
+ Args:
438
+ limit: Maximum number of messages to return (None for all)
439
+ filter_by_topic: Only return messages from this topic (not applicable for webhooks)
440
+
441
+ Returns:
442
+ List of message history entries
443
+ """
444
+ with self._lock:
445
+ history = self.message_history.copy()
446
+
447
+ # Apply limit if specified
448
+ if limit is not None:
449
+ history = history[-limit:]
450
+
451
+ return history
452
+
453
+ def connect_agent(self, agent: AgentSearchResponse):
454
+ """
455
+ Connect to another agent using their webhook URL.
456
+
457
+ Args:
458
+ agent: Agent search response containing webhookUrl
459
+ """
460
+ if "webhookUrl" not in agent:
461
+ raise ValueError("Agent does not have webhookUrl. Cannot connect via webhook.")
462
+
463
+ self.target_webhook_url = agent["webhookUrl"]
464
+ self.is_agent_connected = True
465
+
466
+ logger.info(f"Connected to agent {agent['didIdentifier']} at {self.target_webhook_url}")
@@ -1,18 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zyndai-agent
3
- Version: 0.1.4
4
- Summary: A Langchain and Autogen wrapper that enables agents to communicate and establish identity on the Zynd AI Network. This SDK provides three core capabilities: Identity Management, Agent Discovery & Search, and MQTT-based Communication.
3
+ Version: 0.2.1
4
+ Summary: A Langchain and Autogen wrapper that enables agents to communicate and establish identity on the Zynd AI Network. This SDK provides three core capabilities: Identity Management, Agent Discovery & Search, and HTTP Webhook or MQTT-based Communication.
5
5
  Author-email: Swapnil Shinde <swapnilshinde9382@gmail.com>
6
6
  Requires-Python: >=3.12
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: base58>=2.1.1
9
9
  Requires-Dist: cryptography>=46.0.3
10
10
  Requires-Dist: eth-account>=0.13.7
11
- Requires-Dist: langchain<1.0.4
12
- Requires-Dist: langchain-core>=1.0.3
13
- Requires-Dist: langchain-openai>=1.0.2
11
+ Requires-Dist: flask>=3.0.0
12
+ Requires-Dist: langchain>=1.1.0
14
13
  Requires-Dist: paho-mqtt>=2.1.0
15
- Requires-Dist: x402>=0.2.1
14
+ Requires-Dist: requests>=2.31.0
15
+ Requires-Dist: x402==1.0.0
16
16
 
17
17
  # ZyndAI Agent SDK
18
18
 
@@ -22,7 +22,7 @@ A powerful Python SDK that enables AI agents to communicate securely and discove
22
22
 
23
23
  - 🔐 **Secure Identity Management**: Verify and manage agent identities using Polygon ID credentials
24
24
  - 🔍 **Smart Agent Discovery**: Search and discover agents based on their capabilities with ML-powered semantic matching
25
- - 💬 **Encrypted MQTT Communication**: End-to-end encrypted real-time messaging between agents
25
+ - 💬 **Flexible Communication**: Choose between HTTP Webhooks or MQTT for encrypted real-time messaging between agents
26
26
  - 🤖 **LangChain Integration**: Seamlessly works with LangChain agents and any LLM
27
27
  - 💰 **x402 Micropayments**: Built-in support for pay-per-use API endpoints with automatic payment handling
28
28
  - 🌐 **Decentralized Network**: Connect to the global ZyndAI agent network
@@ -37,7 +37,7 @@ pip install zyndai-agent
37
37
 
38
38
  Or install from source:
39
39
  ```bash
40
- git clone https://github.com/P3-AI-Network/zyndai-agent.git
40
+ git clone https://github.com/Zynd-AI-Network/zyndai-agent.git
41
41
  cd zyndai-agent
42
42
  pip install -r requirements.txt
43
43
  ```
@@ -46,18 +46,46 @@ pip install -r requirements.txt
46
46
 
47
47
  ### 1. Get Your Credentials
48
48
 
49
- 1. Visit the [ZyndAI Dashboard](https://dashboard.zynd.ai) and create an agent
50
- 2. Download your `identity_credential.json` file
51
- 3. Copy your `secret_seed` from the dashboard
49
+ Follow these steps to set up your agent credentials from the ZyndAI Dashboard:
50
+
51
+ 1. **Visit the Dashboard**
52
+ - Go to [dashboard.zynd.ai](https://dashboard.zynd.ai)
53
+ - Click "Get Started"
54
+
55
+ 2. **Connect Your Wallet**
56
+ - Connect your MetaMask wallet
57
+ - Ensure you're on the correct network
58
+
59
+ 3. **Create Your Agent**
60
+ - Navigate to the "Agents" section
61
+ - Click "Create New Agent"
62
+ - Fill in your agent's details (name, description, capabilities)
63
+ - Submit to create your agent
64
+
65
+ 4. **Get Your Agent Seed**
66
+ - After creating the agent, view your agent's details
67
+ - Copy the **Agent Seed** (secret seed phrase)
68
+ - Save this securely - you'll need it for your `.env` file
69
+
70
+ 5. **Download DID Credential Document**
71
+ - In your agent's view, go to the **Credentials** tab
72
+ - Copy or download the **DID Document Credential**
73
+ - Save this as `identity_credential.json` in your project directory
52
74
 
53
75
  ### 2. Environment Setup
54
76
 
55
- Create a `.env` file:
77
+ Create a `.env` file in your project root:
56
78
  ```env
57
- AGENT_SEED=your_secret_seed_here
79
+ AGENT_SEED=your_agent_seed_from_dashboard
80
+ API_KEY=your_api_key_from_dashboard
58
81
  OPENAI_API_KEY=your_openai_api_key_here
59
82
  ```
60
83
 
84
+ **Important Notes:**
85
+ - Keep your `AGENT_SEED` and `identity_credential.json` secure and never commit them to version control
86
+ - The agent seed and DID credential must match - they are cryptographically linked
87
+ - Add both `.env` and `identity_credential.json` to your `.gitignore` file
88
+
61
89
  ### 3. Basic Agent Example
62
90
  ```python
63
91
  from zyndai_agent.agent import AgentConfig, ZyndAIAgent
@@ -206,7 +234,7 @@ Create LangChain tools that leverage x402-enabled paid APIs:
206
234
  ```python
207
235
  from langchain_core.tools import tool
208
236
  from langchain_openai import ChatOpenAI
209
- from langchain.agents import create_tool_calling_agent, AgentExecutor
237
+ from langchain_classic.agents import create_tool_calling_agent, AgentExecutor
210
238
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
211
239
  from zyndai_agent.agent import AgentConfig, ZyndAIAgent
212
240
  import os
@@ -318,12 +346,37 @@ for agent in agents:
318
346
 
319
347
  ### 💬 Secure Communication
320
348
 
321
- All messages are end-to-end encrypted using ECIES (Elliptic Curve Integrated Encryption Scheme):
349
+ The SDK supports two communication modes: **HTTP Webhooks** (recommended) and **MQTT** (legacy). Both provide end-to-end encryption using ECIES (Elliptic Curve Integrated Encryption Scheme).
350
+
351
+ #### HTTP Webhook Mode (Recommended)
352
+
353
+ Each agent runs an embedded Flask server to receive webhook requests. This mode is simpler, doesn't require external MQTT brokers, and works well for most use cases.
354
+
322
355
  ```python
356
+ from zyndai_agent.agent import AgentConfig, ZyndAIAgent
357
+ import os
358
+
359
+ # Configure with webhook mode
360
+ agent_config = AgentConfig(
361
+ webhook_host="0.0.0.0", # Listen on all interfaces
362
+ webhook_port=5000, # Port for webhook server
363
+ webhook_url=None, # Auto-generated or specify public URL
364
+ api_key=os.environ["API_KEY"], # API key for webhook registration
365
+ auto_reconnect=True,
366
+ message_history_limit=100,
367
+ registry_url="https://registry.zynd.ai",
368
+ identity_credential_path="./identity_credential.json",
369
+ secret_seed=os.environ["AGENT_SEED"]
370
+ )
371
+
372
+ # Agent automatically starts webhook server
373
+ zyndai_agent = ZyndAIAgent(agent_config=agent_config)
374
+ print(f"Webhook server running at: {zyndai_agent.webhook_url}")
375
+
323
376
  # Connect to a discovered agent
324
377
  zyndai_agent.connect_agent(selected_agent)
325
378
 
326
- # Send encrypted message
379
+ # Send encrypted message via HTTP POST
327
380
  result = zyndai_agent.send_message(
328
381
  message_content="Can you help me analyze this dataset?",
329
382
  message_type="query"
@@ -333,6 +386,44 @@ result = zyndai_agent.send_message(
333
386
  messages = zyndai_agent.read_messages()
334
387
  ```
335
388
 
389
+ **Webhook Mode Features:**
390
+ - ✅ No external broker required
391
+ - ✅ Standard HTTP/HTTPS communication
392
+ - ✅ Easy to deploy and debug
393
+ - ✅ Works behind firewalls with port forwarding
394
+ - ✅ Auto-retry on port conflicts (tries ports 5000-5010)
395
+ - ✅ Built-in health check endpoint (`/health`)
396
+
397
+ #### MQTT Mode (Legacy)
398
+
399
+ Traditional MQTT broker-based communication. Requires a running MQTT broker.
400
+
401
+ ```python
402
+ agent_config = AgentConfig(
403
+ mqtt_broker_url="mqtt://registry.zynd.ai:1883", # MQTT broker
404
+ default_outbox_topic=None,
405
+ auto_reconnect=True,
406
+ message_history_limit=100,
407
+ registry_url="https://registry.zynd.ai",
408
+ identity_credential_path="./identity_credential.json",
409
+ secret_seed=os.environ["AGENT_SEED"]
410
+ )
411
+
412
+ zyndai_agent = ZyndAIAgent(agent_config=agent_config)
413
+
414
+ # Connect to a discovered agent
415
+ zyndai_agent.connect_agent(selected_agent)
416
+
417
+ # Send encrypted message via MQTT
418
+ result = zyndai_agent.send_message(
419
+ message_content="Can you help me analyze this dataset?",
420
+ message_type="query"
421
+ )
422
+ ```
423
+
424
+ **Migration from MQTT to Webhooks:**
425
+ To migrate existing agents, simply change your configuration from `mqtt_broker_url` to `webhook_host` and `webhook_port`. All other code remains the same!
426
+
336
427
  ### 🔐 Identity Verification
337
428
 
338
429
  Verify other agents' identities before trusting them:
@@ -442,9 +533,9 @@ from zyndai_agent.agent import AgentConfig, ZyndAIAgent
442
533
  from zyndai_agent.communication import MQTTMessage
443
534
  from langchain_openai import ChatOpenAI
444
535
  from langchain_core.tools import tool
445
- from langchain.agents import create_tool_calling_agent, AgentExecutor
536
+ from langchain_classic.agents import create_tool_calling_agent, AgentExecutor
446
537
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
447
- from langchain.memory import ConversationBufferMemory
538
+ from langchain_core.chat_history import InMemoryChatMessageHistory
448
539
  import json
449
540
 
450
541
  @tool
@@ -496,10 +587,12 @@ zyndai_agent = ZyndAIAgent(agent_config=agent_config)
496
587
  # Create LangChain agent with custom tool
497
588
  llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
498
589
  tools = [compare_stocks]
499
- memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
590
+
591
+ # Create message history store
592
+ message_history = InMemoryChatMessageHistory()
500
593
 
501
594
  prompt = ChatPromptTemplate.from_messages([
502
- ("system", """You are a Stock Comparison Agent.
595
+ ("system", """You are a Stock Comparison Agent.
503
596
  Use the compare_stocks tool to analyze stock data.
504
597
  Capabilities: stock_comparison, financial_analysis, investment_advice"""),
505
598
  MessagesPlaceholder(variable_name="chat_history"),
@@ -508,14 +601,25 @@ prompt = ChatPromptTemplate.from_messages([
508
601
  ])
509
602
 
510
603
  agent = create_tool_calling_agent(llm, tools, prompt)
511
- agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)
604
+ agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
512
605
 
513
606
  zyndai_agent.set_agent_executor(agent_executor)
514
607
 
515
608
  # Message handler
516
609
  def message_handler(message: MQTTMessage, topic: str):
517
610
  print(f"Received: {message.content}")
518
- response = zyndai_agent.agent_executor.invoke({"input": message.content})
611
+
612
+ # Add user message to history
613
+ message_history.add_user_message(message.content)
614
+
615
+ response = zyndai_agent.agent_executor.invoke({
616
+ "input": message.content,
617
+ "chat_history": message_history.messages
618
+ })
619
+
620
+ # Add AI response to history
621
+ message_history.add_ai_message(response["output"])
622
+
519
623
  zyndai_agent.send_message(response["output"])
520
624
 
521
625
  zyndai_agent.add_message_handler(message_handler)
@@ -535,13 +639,21 @@ while True:
535
639
 
536
640
  | Parameter | Type | Default | Description |
537
641
  |-----------|------|---------|-------------|
538
- | `auto_reconnect` | `bool` | `True` | Auto-reconnect to MQTT broker on disconnect |
642
+ | `webhook_host` | `str` | `"0.0.0.0"` | **Webhook mode**: Host address to bind webhook server |
643
+ | `webhook_port` | `int` | `5000` | **Webhook mode**: Port number for webhook server |
644
+ | `webhook_url` | `str` | `None` | **Webhook mode**: Public URL (auto-generated if None) |
645
+ | `api_key` | `str` | `None` | **Webhook mode**: API key for webhook registration (required for webhook mode) |
646
+ | `mqtt_broker_url` | `str` | `None` | **MQTT mode**: MQTT broker connection URL |
647
+ | `default_outbox_topic` | `str` | `None` | **MQTT mode**: Default topic for outgoing messages |
648
+ | `auto_reconnect` | `bool` | `True` | Auto-reconnect/restart on disconnect |
539
649
  | `message_history_limit` | `int` | `100` | Maximum messages to keep in history |
540
650
  | `registry_url` | `str` | `"http://localhost:3002"` | ZyndAI registry service URL |
541
- | `mqtt_broker_url` | `str` | Required | MQTT broker connection URL |
542
- | `identity_credential_path` | `str` | Required | Path to your credential file |
651
+ | `identity_credential_path` | `str` | Required | Path to your DID credential file |
543
652
  | `secret_seed` | `str` | Required | Your agent's secret seed |
544
- | `default_outbox_topic` | `str` | `None` | Default topic for outgoing messages |
653
+
654
+ **Note**:
655
+ - Configure either `webhook_port` (recommended) OR `mqtt_broker_url`, not both.
656
+ - When using webhook mode, `api_key` is required for registering your webhook URL with the registry.
545
657
 
546
658
  ### Message Types
547
659
 
@@ -777,7 +889,7 @@ We welcome contributions! Here's how to get started:
777
889
 
778
890
  ### Development Setup
779
891
  ```bash
780
- git clone https://github.com/P3-AI-Network/zyndai-agent.git
892
+ git clone https://github.com/Zynd-AI-Network/zyndai-agent.git
781
893
  cd zyndai-agent
782
894
  python -m venv venv
783
895
  source venv/bin/activate # On Windows: venv\Scripts\activate
@@ -812,11 +924,92 @@ Create agents for real-time market data (x402), technical analysis by agents, se
812
924
  ### 6. Content Generation with Fact-Checking
813
925
  Orchestrate agents for research, writing, accessing paid fact-checking APIs via x402, and publishing verified content.
814
926
 
927
+ ## 🔧 Troubleshooting
928
+
929
+ ### Webhook Mode Issues
930
+
931
+ **Port Already in Use**
932
+ ```
933
+ The SDK automatically tries ports 5000-5010 if the configured port is busy.
934
+ Check the console output for the actual port being used.
935
+ ```
936
+
937
+ **Agent Behind NAT/Firewall**
938
+ ```python
939
+ # Specify your public webhook URL manually
940
+ agent_config = AgentConfig(
941
+ webhook_host="0.0.0.0",
942
+ webhook_port=5000,
943
+ webhook_url="https://my-public-domain.com/webhook", # Your public URL
944
+ ...
945
+ )
946
+ ```
947
+
948
+ **Running Multiple Agents Locally**
949
+ ```python
950
+ # Agent 1: Port 5000
951
+ agent1_config = AgentConfig(webhook_port=5000, ...)
952
+
953
+ # Agent 2: Port 5001
954
+ agent2_config = AgentConfig(webhook_port=5001, ...)
955
+
956
+ # Agent 3: Port 5002
957
+ agent3_config = AgentConfig(webhook_port=5002, ...)
958
+ ```
959
+
960
+ **Target Agent Offline**
961
+ ```
962
+ When sending messages, you'll receive clear error messages:
963
+ - "Error: Could not connect to target agent. Agent may be offline."
964
+ - "Error: Request timed out. Target agent may be offline."
965
+
966
+ The SDK does not automatically retry failed webhooks.
967
+ ```
968
+
969
+ **Health Check**
970
+ ```bash
971
+ # Check if webhook server is running
972
+ curl http://localhost:5000/health
973
+
974
+ # Response:
975
+ # {"status": "ok", "agent_id": "did:polygonid:...", "timestamp": 1234567890}
976
+ ```
977
+
978
+ ### MQTT Mode Issues
979
+
980
+ **Connection Refused**
981
+ ```
982
+ Ensure your MQTT broker URL is correct and the broker is running.
983
+ Default: mqtt://registry.zynd.ai:1883
984
+ ```
985
+
986
+ **Messages Not Being Received**
987
+ ```
988
+ Check that agents are subscribed to the correct topics.
989
+ Verify encryption credentials match between agents.
990
+ ```
991
+
992
+ ### General Issues
993
+
994
+ **Decryption Failures**
995
+ ```
996
+ Ensure both agents have the correct DID credentials.
997
+ Verify secret_seed matches the identity_credential_path.
998
+ Check that credentials haven't been regenerated.
999
+ ```
1000
+
1001
+ **Registry Connection Errors**
1002
+ ```
1003
+ Verify registry_url is correct.
1004
+ Check network connectivity to registry.
1005
+ Ensure webhook URL or MQTT broker info was successfully registered.
1006
+ ```
1007
+
815
1008
  ## 🆘 Support & Community
816
1009
 
817
- - **GitHub Issues**: [Report bugs or request features](https://github.com/P3-AI-Network/zyndai-agent/issues)
1010
+ - **GitHub Issues**: [Report bugs or request features](https://github.com/Zynd-AI-Network/zyndai-agent/issues)
818
1011
  - **Documentation**: [Full API Documentation](https://docs.zynd.ai)
819
- - **Email**: p3ainetwork@gmail.com
1012
+ - **Email**: zyndainetwork@gmail.com
820
1013
  - **Twitter**: [@ZyndAI](https://x.com/ZyndAI)
821
1014
 
822
1015
  ## 📄 License
@@ -838,6 +1031,8 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
838
1031
  - [x] End-to-end encryption
839
1032
  - [x] LangChain integration
840
1033
  - [x] x402 micropayment support
1034
+ - [x] HTTP Webhook communication mode
1035
+ - [ ] WebSocket support for real-time bidirectional communication
841
1036
  - [ ] Support for additional LLM providers (Anthropic, Cohere, etc.)
842
1037
  - [ ] Web dashboard for agent monitoring and payment tracking
843
1038
  - [ ] Advanced orchestration patterns (workflows, state machines)
@@ -846,6 +1041,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
846
1041
  - [ ] Enhanced security features (rate limiting, access control)
847
1042
  - [ ] Performance optimizations for high-throughput scenarios
848
1043
  - [ ] x402 payment analytics and budgeting tools
1044
+ - [ ] Webhook authentication and rate limiting
849
1045
 
850
1046
  ---
851
1047
 
@@ -0,0 +1,13 @@
1
+ zyndai_agent/__init__.py,sha256=HQIL1JElpM14oOWl_7LKQeSxbDSeFMnt7goBFpI7D7I,709
2
+ zyndai_agent/agent.py,sha256=bko2GuGJ4Cd8Ra9IaFncs5j2-OgEcvlR5KW-4sWk1rQ,6262
3
+ zyndai_agent/communication.py,sha256=kMHvlSoj5aL3pfVxfiQImQQDl5VFC1zXUxX-_PwcFrM,21118
4
+ zyndai_agent/identity.py,sha256=9W9iDcrAg07jxE4llrubW1poYBTVtONddyDULGUSnV8,3906
5
+ zyndai_agent/message.py,sha256=nXpKboqAyv-V2bDbgyZ84NerZhGLjYt-lAeM7ndFLTs,3974
6
+ zyndai_agent/payment.py,sha256=Yxnm8rbSB0B2t78jJwGobtcpRbQlM3lSLpUljohhDgc,6238
7
+ zyndai_agent/search.py,sha256=cSLoD4NCXYGo2YiGYE7xYrJr90c_6WjEjoWPQBgu78g,2129
8
+ zyndai_agent/utils.py,sha256=YN1EXGawaUPiPRyPszYvZ7lwTgimmca2DQeW_8nFjRo,16634
9
+ zyndai_agent/webhook_communication.py,sha256=UgyXlNYqooGVoeWqn3uLke8G9vqKBqE1fcFpe1C_mpw,16912
10
+ zyndai_agent-0.2.1.dist-info/METADATA,sha256=FDNvaFcQSzma2Vm8dbWr7d7ERrXacOViaI_d8d7HHMc,37007
11
+ zyndai_agent-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ zyndai_agent-0.2.1.dist-info/top_level.txt,sha256=6jE9hyvpa18fstxa4omi9X2c97rawKydn6NwMwVSql4,13
13
+ zyndai_agent-0.2.1.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- zyndai_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- zyndai_agent/agent.py,sha256=j2ZKpZAmZ23g5GHa1l61CDpOXrG-w0KWIqH9Bgvkytg,3102
3
- zyndai_agent/communication.py,sha256=kMHvlSoj5aL3pfVxfiQImQQDl5VFC1zXUxX-_PwcFrM,21118
4
- zyndai_agent/identity.py,sha256=9W9iDcrAg07jxE4llrubW1poYBTVtONddyDULGUSnV8,3906
5
- zyndai_agent/payment.py,sha256=Yxnm8rbSB0B2t78jJwGobtcpRbQlM3lSLpUljohhDgc,6238
6
- zyndai_agent/search.py,sha256=rYIs39ZM8iXFrv6Zb548UMeWQYjiTO7Ou_2vhvscLPM,2013
7
- zyndai_agent/utils.py,sha256=YN1EXGawaUPiPRyPszYvZ7lwTgimmca2DQeW_8nFjRo,16634
8
- zyndai_agent-0.1.4.dist-info/METADATA,sha256=MLKGZCW72HucmuQ1xzbqFE4fIYHPXqTLxVJgEzGVnQA,30709
9
- zyndai_agent-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- zyndai_agent-0.1.4.dist-info/top_level.txt,sha256=6jE9hyvpa18fstxa4omi9X2c97rawKydn6NwMwVSql4,13
11
- zyndai_agent-0.1.4.dist-info/RECORD,,