uagents-core 0.2.2__tar.gz → 0.3.0__tar.gz

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.
Files changed (22) hide show
  1. {uagents_core-0.2.2 → uagents_core-0.3.0}/PKG-INFO +1 -1
  2. {uagents_core-0.2.2 → uagents_core-0.3.0}/pyproject.toml +1 -1
  3. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/config.py +5 -0
  4. uagents_core-0.3.0/uagents_core/storage.py +135 -0
  5. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/utils/messages.py +1 -1
  6. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/utils/registration.py +6 -7
  7. {uagents_core-0.2.2 → uagents_core-0.3.0}/README.md +0 -0
  8. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/__init__.py +0 -0
  9. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/contrib/__init__.py +0 -0
  10. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/contrib/protocols/__init__.py +0 -0
  11. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/contrib/protocols/chat/__init__.py +0 -0
  12. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/contrib/protocols/subscriptions/__init__.py +0 -0
  13. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/envelope.py +0 -0
  14. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/helpers.py +0 -0
  15. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/identity.py +0 -0
  16. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/logger.py +0 -0
  17. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/models.py +0 -0
  18. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/protocol.py +0 -0
  19. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/registration.py +0 -0
  20. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/types.py +0 -0
  21. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/utils/__init__.py +0 -0
  22. {uagents_core-0.2.2 → uagents_core-0.3.0}/uagents_core/utils/resolver.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: uagents-core
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Core components for agent based systems
5
5
  License: Apache 2.0
6
6
  Author: Ed FitzGerald
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "uagents-core"
3
- version = "0.2.2"
3
+ version = "0.3.0"
4
4
  description = "Core components for agent based systems"
5
5
  authors = [
6
6
  { name = "Ed FitzGerald", email = "edward.fitzgerald@fetch.ai" },
@@ -6,6 +6,7 @@ DEFAULT_REGISTRATION_PATH = "/v1/agents"
6
6
  DEFAULT_CHALLENGE_PATH = "/v1/auth/challenge"
7
7
  DEFAULT_MAILBOX_PATH = "/v1/submit"
8
8
  DEFAULT_PROXY_PATH = "/v1/proxy/submit"
9
+ DEFAULT_STORAGE_PATH = "/v1/storage"
9
10
 
10
11
  DEFAULT_MAX_ENDPOINTS = 10
11
12
 
@@ -30,3 +31,7 @@ class AgentverseConfig(BaseModel):
30
31
  @property
31
32
  def proxy_endpoint(self) -> str:
32
33
  return f"{self.url}{DEFAULT_PROXY_PATH}"
34
+
35
+ @property
36
+ def storage_endpoint(self) -> str:
37
+ return f"{self.url}{DEFAULT_STORAGE_PATH}"
@@ -0,0 +1,135 @@
1
+ import base64
2
+ import struct
3
+ from datetime import datetime
4
+ from secrets import token_bytes
5
+ from typing import Optional
6
+
7
+ import requests
8
+
9
+ from uagents_core.config import AgentverseConfig
10
+ from uagents_core.identity import Identity
11
+
12
+
13
+ def compute_attestation(
14
+ identity: Identity, validity_start: datetime, validity_secs: int, nonce: bytes
15
+ ) -> str:
16
+ """
17
+ Compute a valid agent attestation token for authentication.
18
+ """
19
+ assert len(nonce) == 32, "Nonce is of invalid length"
20
+
21
+ valid_from = int(validity_start.timestamp())
22
+ valid_to = valid_from + validity_secs
23
+
24
+ public_key = bytes.fromhex(identity.pub_key)
25
+
26
+ payload = public_key + struct.pack(">QQ", valid_from, valid_to) + nonce
27
+ assert len(payload) == 81, "attestation payload is incorrect"
28
+
29
+ signature = identity.sign(payload)
30
+ attestation = f"attr:{base64.b64encode(payload).decode()}:{signature}"
31
+ return attestation
32
+
33
+
34
+ class ExternalStorage:
35
+ def __init__(
36
+ self,
37
+ identity: Optional[Identity] = None,
38
+ storage_url: Optional[str] = None,
39
+ api_token: Optional[str] = None,
40
+ ):
41
+ self.identity = identity
42
+ self.api_token = api_token
43
+ self.storage_url = storage_url or AgentverseConfig().storage_endpoint
44
+
45
+ def _make_attestation(self) -> str:
46
+ nonce = token_bytes(32)
47
+ now = datetime.now()
48
+ return compute_attestation(self.identity, now, 3600, nonce)
49
+
50
+ def _get_auth_header(self) -> dict:
51
+ if self.api_token:
52
+ return {"Authorization": f"Bearer {self.api_token}"}
53
+ elif self.identity:
54
+ return {"Authorization": f"Agent {self._make_attestation()}"}
55
+ else:
56
+ raise RuntimeError("No identity or API token available for authentication")
57
+
58
+ def upload(
59
+ self, asset_id: str, asset_content: str, mime_type: str = "text/plain"
60
+ ) -> dict:
61
+ url = f"{self.storage_url}/assets/{asset_id}/contents/"
62
+ headers = self._get_auth_header()
63
+ headers["Content-Type"] = "application/json"
64
+ payload = {
65
+ "contents": base64.b64encode(asset_content.encode()).decode(),
66
+ "mime_type": mime_type,
67
+ }
68
+ response = requests.put(url, json=payload, headers=headers)
69
+ if response.status_code != 200:
70
+ raise RuntimeError(
71
+ f"Upload failed: {response.status_code}, {response.text}"
72
+ )
73
+
74
+ return response.json()
75
+
76
+ def download(self, asset_id: str) -> dict:
77
+ url = f"{self.storage_url}/assets/{asset_id}/contents/"
78
+ headers = self._get_auth_header()
79
+
80
+ response = requests.get(url, headers=headers)
81
+ if response.status_code != 200:
82
+ raise RuntimeError(
83
+ f"Download failed: {response.status_code}, {response.text}"
84
+ )
85
+
86
+ return response.json()
87
+
88
+ def create_asset(
89
+ self,
90
+ name: str,
91
+ content: str,
92
+ mime_type: str = "text/plain",
93
+ lifetime_hours: int = 24,
94
+ ) -> str:
95
+ if not self.api_token:
96
+ raise RuntimeError("API token required to create assets")
97
+ url = f"{self.storage_url}/assets/"
98
+ headers = self._get_auth_header()
99
+ headers["Content-Type"] = "application/json"
100
+ payload = {
101
+ "name": name,
102
+ "mime_type": mime_type,
103
+ "contents": base64.b64encode(content.encode()).decode(),
104
+ "lifetime_hours": lifetime_hours,
105
+ }
106
+
107
+ response = requests.post(url, json=payload, headers=headers)
108
+ if response.status_code != 201:
109
+ raise RuntimeError(
110
+ f"Asset creation failed: {response.status_code}, {response.text}"
111
+ )
112
+
113
+ return response.json()["asset_id"]
114
+
115
+ def set_permissions(
116
+ self, asset_id: str, agent_address: str, read: bool = True, write: bool = True
117
+ ):
118
+ if not self.api_token:
119
+ raise RuntimeError("API token required to set permissions")
120
+ url = f"{self.storage_url}/assets/{asset_id}/permissions/"
121
+ headers = self._get_auth_header()
122
+ headers["Content-Type"] = "application/json"
123
+ payload = {
124
+ "agent_address": agent_address,
125
+ "read": read,
126
+ "write": write,
127
+ }
128
+
129
+ response = requests.put(url, json=payload, headers=headers)
130
+ if response.status_code != 200:
131
+ raise RuntimeError(
132
+ f"Set permissions failed: {response.status_code}, {response.text}"
133
+ )
134
+
135
+ return response.json()
@@ -144,7 +144,7 @@ def send_message_to_agent(
144
144
  result.append(
145
145
  MsgStatus(
146
146
  status=DeliveryStatus.FAILED,
147
- detail=response.text,
147
+ detail=str(e),
148
148
  destination=destination,
149
149
  endpoint=endpoint,
150
150
  session=env.session,
@@ -51,8 +51,11 @@ def _send_post_request(
51
51
  response.raise_for_status()
52
52
  return True, response
53
53
  except requests.RequestException as e:
54
+ error_detail = getattr(e, "response", None)
55
+ if error_detail is not None:
56
+ error_detail = error_detail.text
54
57
  logger.error(
55
- msg="Error submitting request",
58
+ msg=f"Error submitting request: {error_detail}",
56
59
  extra={"url": url, "data": data.model_dump_json()},
57
60
  exc_info=e,
58
61
  )
@@ -63,6 +66,7 @@ def register_in_almanac(
63
66
  identity: Identity,
64
67
  endpoints: list[str],
65
68
  protocol_digests: list[str],
69
+ metadata: dict[str, str | dict[str, str]] | None = None,
66
70
  *,
67
71
  agentverse_config: AgentverseConfig | None = None,
68
72
  timeout: int = DEFAULT_REQUEST_TIMEOUT,
@@ -115,7 +119,7 @@ def register_in_almanac(
115
119
  agent_identifier=agent_address,
116
120
  protocols=protocol_digests,
117
121
  endpoints=agent_endpoints,
118
- metadata=None,
122
+ metadata=metadata,
119
123
  )
120
124
 
121
125
  logger.info(msg="Registering with Almanac API", extra=attestation.model_dump())
@@ -220,11 +224,6 @@ def register_in_agentverse(
220
224
  extra=registration_metadata,
221
225
  )
222
226
  return False
223
- if response.status_code == 409:
224
- logger.info(
225
- msg="Agent already registered with Agentverse",
226
- extra=registration_metadata,
227
- )
228
227
  else:
229
228
  registration_response = RegistrationResponse.model_validate_json(
230
229
  response.text
File without changes