uagents-core 0.3.1__py3-none-any.whl → 0.3.3__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.
@@ -70,7 +70,11 @@ class VerifiableModel(BaseModel):
70
70
  class AgentRegistrationAttestation(VerifiableModel):
71
71
  protocols: list[str]
72
72
  endpoints: list[AgentEndpoint]
73
- metadata: dict[str, str | dict[str, str]] | None = None
73
+ metadata: dict[str, str | list[str] | dict[str, str]] | None = None
74
+
75
+
76
+ class AgentRegistrationAttestationBatch(BaseModel):
77
+ attestations: list[AgentRegistrationAttestation]
74
78
 
75
79
 
76
80
  # Agentverse related models
@@ -107,3 +111,9 @@ class AgentUpdates(BaseModel):
107
111
  avatar_url: str | None = Field(default=None, max_length=4000)
108
112
  short_description: str | None = Field(default=None, max_length=300)
109
113
  agent_type: AgentType | None = "custom"
114
+
115
+
116
+ class AgentStatusUpdate(VerifiableModel):
117
+ is_active: bool = Field(
118
+ ..., description="Indicates whether the agent is currently active"
119
+ )
uagents_core/storage.py CHANGED
@@ -56,7 +56,7 @@ class ExternalStorage:
56
56
  raise RuntimeError("No identity or API token available for authentication")
57
57
 
58
58
  def upload(
59
- self, asset_id: str, asset_content: bytes, mime_type: str = "text/plain"
59
+ self, asset_id: str, content: bytes, mime_type: str = "text/plain"
60
60
  ) -> dict:
61
61
  url = f"{self.storage_url}/assets/{asset_id}/contents/"
62
62
  headers = self._get_auth_header()
@@ -19,6 +19,8 @@ from uagents_core.logger import get_logger
19
19
  from uagents_core.protocol import is_valid_protocol_digest
20
20
  from uagents_core.registration import (
21
21
  AgentRegistrationAttestation,
22
+ AgentRegistrationAttestationBatch,
23
+ AgentStatusUpdate,
22
24
  AgentUpdates,
23
25
  AgentverseConnectRequest,
24
26
  ChallengeRequest,
@@ -26,11 +28,33 @@ from uagents_core.registration import (
26
28
  RegistrationRequest,
27
29
  RegistrationResponse,
28
30
  )
29
- from uagents_core.types import AgentEndpoint
31
+ from uagents_core.types import AddressPrefix, AgentEndpoint
30
32
 
31
33
  logger = get_logger("uagents_core.utils.registration")
32
34
 
33
35
 
36
+ class AgentRegistrationInput:
37
+ identity: Identity
38
+ prefix: str | None = None
39
+ endpoints: list[str]
40
+ protocol_digests: list[str]
41
+ metadata: dict[str, str | list[str] | dict[str, str]] | None = None
42
+
43
+ def __init__(
44
+ self,
45
+ identity: Identity,
46
+ endpoints: list[str],
47
+ protocol_digests: list[str],
48
+ prefix: AddressPrefix | None = None,
49
+ metadata: dict[str, str | list[str] | dict[str, str]] | None = None,
50
+ ):
51
+ self.identity = identity
52
+ self.prefix = prefix
53
+ self.endpoints = endpoints
54
+ self.protocol_digests = protocol_digests
55
+ self.metadata = metadata
56
+
57
+
34
58
  def _send_post_request(
35
59
  url: str,
36
60
  data: BaseModel,
@@ -62,11 +86,32 @@ def _send_post_request(
62
86
  return False, None
63
87
 
64
88
 
89
+ def _build_signed_attestation(
90
+ item: AgentRegistrationInput,
91
+ ) -> AgentRegistrationAttestation:
92
+ agent_endpoints: list[AgentEndpoint] = [
93
+ AgentEndpoint(url=endpoint, weight=1) for endpoint in item.endpoints
94
+ ]
95
+
96
+ attestation = AgentRegistrationAttestation(
97
+ agent_identifier=f"{item.prefix}://{item.identity.address}"
98
+ if item.prefix
99
+ else item.identity.address,
100
+ protocols=item.protocol_digests,
101
+ endpoints=agent_endpoints,
102
+ metadata=item.metadata,
103
+ )
104
+
105
+ attestation.sign(item.identity)
106
+ return attestation
107
+
108
+
65
109
  def register_in_almanac(
66
110
  identity: Identity,
67
111
  endpoints: list[str],
68
112
  protocol_digests: list[str],
69
- metadata: dict[str, str | dict[str, str]] | None = None,
113
+ metadata: dict[str, str | list[str] | dict[str, str]] | None = None,
114
+ prefix: AddressPrefix | None = None,
70
115
  *,
71
116
  agentverse_config: AgentverseConfig | None = None,
72
117
  timeout: int = DEFAULT_REQUEST_TIMEOUT,
@@ -76,6 +121,7 @@ def register_in_almanac(
76
121
 
77
122
  Args:
78
123
  identity (Identity): The identity of the agent.
124
+ prefix (AddressPrefix | None): The prefix for the agent identifier.
79
125
  endpoints (list[str]): The endpoints that the agent can be reached at.
80
126
  protocol_digests (list[str]): The digests of the protocol that the agent supports
81
127
  agentverse_config (AgentverseConfig): The configuration for the agentverse API
@@ -94,10 +140,6 @@ def register_in_almanac(
94
140
  )
95
141
  return False
96
142
 
97
- agent_endpoints: list[AgentEndpoint] = [
98
- AgentEndpoint(url=endpoint, weight=1) for endpoint in endpoints
99
- ]
100
-
101
143
  # check protocol digests
102
144
  for proto_digest in protocol_digests:
103
145
  if not is_valid_protocol_digest(proto_digest):
@@ -111,22 +153,18 @@ def register_in_almanac(
111
153
  agentverse_config = agentverse_config or AgentverseConfig()
112
154
  almanac_api = urllib.parse.urljoin(agentverse_config.url, DEFAULT_ALMANAC_API_PATH)
113
155
 
114
- # get the agent address
115
- agent_address = identity.address
116
-
117
156
  # create the attestation
118
- attestation = AgentRegistrationAttestation(
119
- agent_identifier=agent_address,
120
- protocols=protocol_digests,
121
- endpoints=agent_endpoints,
157
+ item = AgentRegistrationInput(
158
+ identity=identity,
159
+ prefix=prefix,
160
+ endpoints=endpoints,
161
+ protocol_digests=protocol_digests,
122
162
  metadata=metadata,
123
163
  )
164
+ attestation = _build_signed_attestation(item)
124
165
 
125
166
  logger.info(msg="Registering with Almanac API", extra=attestation.model_dump())
126
167
 
127
- # sign the attestation
128
- attestation.sign(identity)
129
-
130
168
  # submit the attestation to the API
131
169
  status, _ = _send_post_request(
132
170
  url=f"{almanac_api}/agents", data=attestation, timeout=timeout
@@ -134,6 +172,94 @@ def register_in_almanac(
134
172
  return status
135
173
 
136
174
 
175
+ def register_batch_in_almanac(
176
+ items: list[AgentRegistrationInput],
177
+ *,
178
+ agentverse_config: AgentverseConfig | None = None,
179
+ timeout: int = DEFAULT_REQUEST_TIMEOUT,
180
+ validate_all_before_registration: bool = False,
181
+ ) -> tuple[bool, list[str]]:
182
+ """
183
+ Register multiple identities with the Almanac API to make them discoverable by other agents.
184
+
185
+ The return value is a 2-tuple including:
186
+ * (bool) Whether the registration request was both attempted and successful.
187
+ * (list[str]) A list of addresses of identities that failed validation.
188
+
189
+ If `validate_all_before_registration` is `True`, no registration request will be sent
190
+ unless all identities pass validation.
191
+
192
+ Args:
193
+ items (list[AgentRegistrationInput]): The list of identities to register.
194
+ See `register_in_almanac` for details about attributes in `AgentRegistrationInput`.
195
+ agentverse_config (AgentverseConfig): The configuration for the agentverse API
196
+ timeout (int): The timeout for the request
197
+ """
198
+ invalid_identities: list[str] = []
199
+ attestations: list[AgentRegistrationAttestation] = []
200
+
201
+ for item in items:
202
+ # check endpoints
203
+ if not item.endpoints:
204
+ logger.warning(
205
+ f"No endpoints provided for {item.identity.address}; skipping registration",
206
+ )
207
+ invalid_identities.append(item.identity.address)
208
+ for endpoint in item.endpoints:
209
+ result = urllib.parse.urlparse(endpoint)
210
+ if not all([result.scheme, result.netloc]):
211
+ logger.error(
212
+ msg=f"Invalid endpoint provided for {item.identity.address}; "
213
+ + "skipping registration",
214
+ extra={"endpoint": endpoint},
215
+ )
216
+ invalid_identities.append(item.identity.address)
217
+
218
+ # check protocol digests
219
+ for proto_digest in item.protocol_digests:
220
+ if not is_valid_protocol_digest(proto_digest):
221
+ logger.error(
222
+ msg=f"Invalid protocol digest provided for {item.identity.address}; "
223
+ + "skipping registration",
224
+ extra={"protocol_digest": proto_digest},
225
+ )
226
+ invalid_identities.append(item.identity.address)
227
+
228
+ # Remove duplicates
229
+ invalid_identities = sorted(list(set(invalid_identities)))
230
+
231
+ for item in items:
232
+ if item.identity.address not in invalid_identities:
233
+ attestations.append(_build_signed_attestation(item))
234
+
235
+ if validate_all_before_registration and invalid_identities:
236
+ return False, invalid_identities
237
+
238
+ # get the almanac API endpoint
239
+ agentverse_config = agentverse_config or AgentverseConfig()
240
+ almanac_api = urllib.parse.urljoin(agentverse_config.url, DEFAULT_ALMANAC_API_PATH)
241
+
242
+ logger.info(
243
+ msg="Bulk registering with Almanac API",
244
+ extra={
245
+ "agent_addresses": [
246
+ attestation.agent_identifier for attestation in attestations
247
+ ]
248
+ },
249
+ )
250
+ attestation_batch = AgentRegistrationAttestationBatch(
251
+ attestations=attestations,
252
+ )
253
+
254
+ # submit the attestation to the API
255
+ status, _ = _send_post_request(
256
+ url=f"{almanac_api}/agents/batch",
257
+ data=attestation_batch,
258
+ timeout=timeout,
259
+ )
260
+ return status, invalid_identities
261
+
262
+
137
263
  # associate user account with your agent
138
264
  def register_in_agentverse(
139
265
  request: AgentverseConnectRequest,
@@ -269,3 +395,37 @@ def register_in_agentverse(
269
395
  exc_info=e,
270
396
  )
271
397
  return False
398
+
399
+
400
+ def update_agent_status(active: bool, identity: Identity):
401
+ """
402
+ Update the agent's active/inactive status in the Almanac API.
403
+
404
+ Args:
405
+ active (bool): The status of the agent.
406
+ identity (Identity): The identity of the agent.
407
+ """
408
+ almanac_api = AgentverseConfig().url + DEFAULT_ALMANAC_API_PATH
409
+
410
+ status_update = AgentStatusUpdate(
411
+ agent_identifier=identity.address, is_active=active
412
+ )
413
+ status_update.sign(identity)
414
+
415
+ logger.debug(
416
+ msg="Updating agent status in Almanac API",
417
+ extra=status_update.model_dump(),
418
+ )
419
+
420
+ status, _ = _send_post_request(
421
+ url=f"{almanac_api}/agents/{identity.address}/status",
422
+ data=status_update,
423
+ )
424
+
425
+ if status:
426
+ logger.info(
427
+ msg=f"Agent status updated to {'active' if active else 'inactive'}",
428
+ extra={"agent_address": identity.address},
429
+ )
430
+
431
+ return status
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: uagents-core
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Summary: Core components for agent based systems
5
5
  License: Apache 2.0
6
6
  Author: Ed FitzGerald
@@ -10,13 +10,13 @@ uagents_core/identity.py,sha256=YHI3rromJ27DGF3X102MGkuvBIZ6usAaMutlDLdVaL0,6185
10
10
  uagents_core/logger.py,sha256=5XLs3-XpKDeoSwtAE5pEM9h6x3pUPNbb0T0J7KvFmG0,877
11
11
  uagents_core/models.py,sha256=fxsFjRochkJUdgPltT1HWgBl-9K-HVQWl8sSMVgeJFY,1066
12
12
  uagents_core/protocol.py,sha256=T9jasOkltne33E16Y7VrqcB2moWVsv-Qh4XLZotyz8g,5428
13
- uagents_core/registration.py,sha256=h7ha58H7XbLQ__qOHMKVnHShzMlWfd2xrK9fRdgfb9w,2953
14
- uagents_core/storage.py,sha256=SKcK9_m7KNyIu7THFbK41DEoP2CmjTjKGKch_bUQ-MY,4486
13
+ uagents_core/registration.py,sha256=lmDXnsAs2CvpgE1Ik0qja_0RAY7zJruK11_jUrlqlWs,3226
14
+ uagents_core/storage.py,sha256=Nb_vvckp5FV48bcewFmYUoLFHAsWxwaiIv_-idFpGqQ,4480
15
15
  uagents_core/types.py,sha256=_W3EN1wEIRFxuhhBxyZxQH_dA_3AtoPPReIzrgcTUHc,1167
16
16
  uagents_core/utils/__init__.py,sha256=v0MaxDYCTtQlwbblEHCfLtbeTnA2hCmKKJk7mlcE20U,135
17
17
  uagents_core/utils/messages.py,sha256=5o3ahVaeGwzWMAxDs0fl2pC1duDq2VS_-czoVtZpkOc,5066
18
- uagents_core/utils/registration.py,sha256=HaA0J2yisx0bwVs1LbpJ4UP3-maeeAf37yziI-U07l8,9067
18
+ uagents_core/utils/registration.py,sha256=Lp17tn4knw4qpKx_mZO5gDHD6pGssUhux6SGDhhb7rc,14607
19
19
  uagents_core/utils/resolver.py,sha256=X18oe-WPU_6pfTc6x_Oa69kZQYCMidkaSeCs68Poaik,2169
20
- uagents_core-0.3.1.dist-info/METADATA,sha256=5ZoG4P9uYXSasuvixsWiUJGTMbHbSaRUQ3U_zUORSUc,1009
21
- uagents_core-0.3.1.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
22
- uagents_core-0.3.1.dist-info/RECORD,,
20
+ uagents_core-0.3.3.dist-info/METADATA,sha256=qXEPfhisZj17usBXwFkMcoFYtsd8l39XYGu5FHypvQc,1009
21
+ uagents_core-0.3.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
22
+ uagents_core-0.3.3.dist-info/RECORD,,