uagents-core 0.3.7__tar.gz → 0.3.8__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.
- {uagents_core-0.3.7 → uagents_core-0.3.8}/PKG-INFO +1 -1
- {uagents_core-0.3.7 → uagents_core-0.3.8}/pyproject.toml +1 -1
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/types.py +27 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/utils/registration.py +336 -98
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/utils/resolver.py +64 -3
- {uagents_core-0.3.7 → uagents_core-0.3.8}/README.md +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/__init__.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/config.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/contrib/__init__.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/contrib/protocols/__init__.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/contrib/protocols/chat/__init__.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/contrib/protocols/subscriptions/__init__.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/envelope.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/helpers.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/identity.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/logger.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/models.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/protocol.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/registration.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/storage.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/utils/__init__.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/utils/messages.py +0 -0
- {uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/utils/subscriptions.py +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import uuid
|
2
2
|
from abc import ABC, abstractmethod
|
3
|
+
from datetime import datetime
|
3
4
|
from enum import Enum
|
4
5
|
from typing import Any, Literal
|
5
6
|
|
@@ -26,6 +27,32 @@ class AgentInfo(BaseModel):
|
|
26
27
|
port: int | None = None
|
27
28
|
|
28
29
|
|
30
|
+
class AgentRecord(BaseModel):
|
31
|
+
address: str
|
32
|
+
weight: float
|
33
|
+
|
34
|
+
|
35
|
+
class DomainRecord(BaseModel):
|
36
|
+
name: str
|
37
|
+
agents: list[AgentRecord]
|
38
|
+
|
39
|
+
|
40
|
+
class DomainStatus(Enum):
|
41
|
+
Registered = "Registered"
|
42
|
+
Pending = "Pending"
|
43
|
+
Checking = "Checking"
|
44
|
+
Updating = "Updating"
|
45
|
+
Deleting = "Deleting"
|
46
|
+
Failed = "Failed"
|
47
|
+
|
48
|
+
|
49
|
+
class Domain(BaseModel):
|
50
|
+
name: str
|
51
|
+
status: DomainStatus
|
52
|
+
expiry: datetime | None = None
|
53
|
+
assigned_agents: list[AgentRecord]
|
54
|
+
|
55
|
+
|
29
56
|
class DeliveryStatus(str, Enum):
|
30
57
|
"""Delivery status of a message."""
|
31
58
|
|
@@ -2,10 +2,12 @@
|
|
2
2
|
This module provides methods to register your identity with the Fetch.ai services.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from typing import Literal
|
5
6
|
import urllib.parse
|
6
7
|
|
7
8
|
import requests
|
8
|
-
from pydantic import BaseModel
|
9
|
+
from pydantic import BaseModel, Field, model_validator
|
10
|
+
from json import JSONDecodeError
|
9
11
|
|
10
12
|
from uagents_core.config import (
|
11
13
|
DEFAULT_ALMANAC_API_PATH,
|
@@ -14,9 +16,10 @@ from uagents_core.config import (
|
|
14
16
|
DEFAULT_REQUEST_TIMEOUT,
|
15
17
|
AgentverseConfig,
|
16
18
|
)
|
19
|
+
from uagents_core.contrib.protocols.chat import chat_protocol_spec
|
17
20
|
from uagents_core.identity import Identity
|
18
21
|
from uagents_core.logger import get_logger
|
19
|
-
from uagents_core.protocol import is_valid_protocol_digest
|
22
|
+
from uagents_core.protocol import ProtocolSpecification, is_valid_protocol_digest
|
20
23
|
from uagents_core.registration import (
|
21
24
|
AgentRegistrationAttestation,
|
22
25
|
AgentRegistrationAttestationBatch,
|
@@ -26,9 +29,8 @@ from uagents_core.registration import (
|
|
26
29
|
ChallengeRequest,
|
27
30
|
ChallengeResponse,
|
28
31
|
RegistrationRequest,
|
29
|
-
RegistrationResponse,
|
30
32
|
)
|
31
|
-
from uagents_core.types import AddressPrefix, AgentEndpoint
|
33
|
+
from uagents_core.types import AddressPrefix, AgentEndpoint, AgentType
|
32
34
|
|
33
35
|
logger = get_logger("uagents_core.utils.registration")
|
34
36
|
|
@@ -55,35 +57,125 @@ class AgentRegistrationInput:
|
|
55
57
|
self.metadata = metadata
|
56
58
|
|
57
59
|
|
58
|
-
|
60
|
+
class AgentverseRegistrationRequest(BaseModel):
|
61
|
+
"""
|
62
|
+
A model containing all information for a user to register
|
63
|
+
their pre-existing agent to Agentverse.
|
64
|
+
"""
|
65
|
+
|
66
|
+
name: str = Field(description="Agent name in Agentverse")
|
67
|
+
endpoint: str = Field(
|
68
|
+
description="Endpoint where the existing agent is accessible at."
|
69
|
+
)
|
70
|
+
protocols: list[str] = Field(
|
71
|
+
description="List of protocols supported by the agent."
|
72
|
+
)
|
73
|
+
type: AgentType = Field(
|
74
|
+
default="custom", description="Agentverse registration type"
|
75
|
+
)
|
76
|
+
description: str | None = Field(
|
77
|
+
default=None,
|
78
|
+
description="Agent short description, shown on its Agentverse profile.",
|
79
|
+
)
|
80
|
+
readme: str | None = Field(default=None, description="Agent skills description.")
|
81
|
+
avatar_url: str | None = Field(
|
82
|
+
default=None,
|
83
|
+
description="Agent avatar url to be shown on its Agentverse profile.",
|
84
|
+
)
|
85
|
+
active: bool = Field(default=True, description="Set agent as active immediatly")
|
86
|
+
|
87
|
+
@model_validator(mode="after")
|
88
|
+
def check_request(self) -> "AgentverseRegistrationRequest":
|
89
|
+
# check endpoint
|
90
|
+
result = urllib.parse.urlparse(self.endpoint)
|
91
|
+
if not all([result.scheme, result.netloc]):
|
92
|
+
raise ValueError(f"Invalid endpoint provided: {self.endpoint}")
|
93
|
+
|
94
|
+
# check protocol digests
|
95
|
+
for proto_digest in self.protocols:
|
96
|
+
if not is_valid_protocol_digest(proto_digest):
|
97
|
+
raise ValueError(
|
98
|
+
f"Invalid protocol digest provided: {proto_digest}",
|
99
|
+
)
|
100
|
+
return self
|
101
|
+
|
102
|
+
|
103
|
+
class RegistrationRequestCredentials(BaseModel):
|
104
|
+
agentverse_api_key: str = Field(
|
105
|
+
description="Agentverse API key generated by the owner of the agent"
|
106
|
+
)
|
107
|
+
agent_seed_phrase: str = Field(
|
108
|
+
description="The secret seed phrase used to create the agent identity"
|
109
|
+
)
|
110
|
+
|
111
|
+
|
112
|
+
class AgentverseRequestException(Exception):
|
113
|
+
def __init__(self, *args, from_exc: Exception):
|
114
|
+
self.from_exc = from_exc
|
115
|
+
super().__init__(*args)
|
116
|
+
|
117
|
+
|
118
|
+
def _send_http_request_agentverse(
|
119
|
+
request_type: Literal["post", "put"],
|
59
120
|
url: str,
|
60
121
|
data: BaseModel,
|
61
122
|
*,
|
62
123
|
headers: dict[str, str] | None = None,
|
63
124
|
timeout: int = DEFAULT_REQUEST_TIMEOUT,
|
64
|
-
) ->
|
125
|
+
) -> requests.Response:
|
65
126
|
final_headers: dict[str, str] = {"content-type": "application/json"}
|
66
127
|
if headers:
|
67
128
|
final_headers.update(headers)
|
129
|
+
|
130
|
+
send_request = requests.post if request_type == "post" else requests.put
|
131
|
+
|
68
132
|
try:
|
69
|
-
response: requests.Response =
|
133
|
+
response: requests.Response = send_request(
|
70
134
|
url=url,
|
71
135
|
headers=final_headers,
|
72
136
|
data=data.model_dump_json(),
|
73
137
|
timeout=timeout,
|
74
138
|
)
|
75
139
|
response.raise_for_status()
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
if
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
140
|
+
except Exception as e:
|
141
|
+
err_msg = ""
|
142
|
+
|
143
|
+
if isinstance(e, requests.ConnectionError):
|
144
|
+
err_msg += f"Connection error {e.strerror}."
|
145
|
+
elif isinstance(e, requests.Timeout):
|
146
|
+
err_msg += "Operation timed out."
|
147
|
+
elif isinstance(e, requests.HTTPError):
|
148
|
+
code = e.response.status_code
|
149
|
+
try:
|
150
|
+
content = e.response.json()["detail"]
|
151
|
+
except (JSONDecodeError, KeyError):
|
152
|
+
content = e.response.content.decode()
|
153
|
+
if code in [401, 406, 409]:
|
154
|
+
err_msg += content
|
155
|
+
elif code == 500:
|
156
|
+
err_msg += "Unexpected server error."
|
157
|
+
else:
|
158
|
+
err_msg += f"HTTP error: {code} {content}"
|
159
|
+
elif isinstance(e, requests.RequestException):
|
160
|
+
err_msg += f"Unexpected request error: {e}."
|
161
|
+
else:
|
162
|
+
err_msg += f"Unexpected error: {e}."
|
163
|
+
|
164
|
+
raise AgentverseRequestException(err_msg, from_exc=e)
|
165
|
+
|
166
|
+
return response
|
167
|
+
|
168
|
+
|
169
|
+
def _send_post_request_agentverse(
|
170
|
+
url: str,
|
171
|
+
data: BaseModel,
|
172
|
+
*,
|
173
|
+
headers: dict[str, str] | None = None,
|
174
|
+
timeout: int = DEFAULT_REQUEST_TIMEOUT,
|
175
|
+
) -> requests.Response:
|
176
|
+
return _send_http_request_agentverse(
|
177
|
+
"post", url, data, headers=headers, timeout=timeout
|
178
|
+
)
|
87
179
|
|
88
180
|
|
89
181
|
def _build_signed_attestation(
|
@@ -94,9 +186,11 @@ def _build_signed_attestation(
|
|
94
186
|
]
|
95
187
|
|
96
188
|
attestation = AgentRegistrationAttestation(
|
97
|
-
agent_identifier=
|
98
|
-
|
99
|
-
|
189
|
+
agent_identifier=(
|
190
|
+
f"{item.prefix}://{item.identity.address}"
|
191
|
+
if item.prefix
|
192
|
+
else item.identity.address
|
193
|
+
),
|
100
194
|
protocols=item.protocol_digests,
|
101
195
|
endpoints=agent_endpoints,
|
102
196
|
metadata=item.metadata,
|
@@ -106,6 +200,49 @@ def _build_signed_attestation(
|
|
106
200
|
return attestation
|
107
201
|
|
108
202
|
|
203
|
+
def _register_in_almanac(
|
204
|
+
identity: Identity,
|
205
|
+
endpoints: list[str],
|
206
|
+
protocol_digests: list[str],
|
207
|
+
metadata: dict[str, str | list[str] | dict[str, str]] | None = None,
|
208
|
+
prefix: AddressPrefix | None = None,
|
209
|
+
*,
|
210
|
+
agentverse_config: AgentverseConfig | None = None,
|
211
|
+
timeout: int = DEFAULT_REQUEST_TIMEOUT,
|
212
|
+
) -> requests.Response:
|
213
|
+
"""
|
214
|
+
Register the identity with the Almanac API to make it discoverable by other agents.
|
215
|
+
|
216
|
+
Args:
|
217
|
+
identity (Identity): The identity of the agent.
|
218
|
+
prefix (AddressPrefix | None): The prefix for the agent identifier.
|
219
|
+
endpoints (list[str]): The endpoints that the agent can be reached at.
|
220
|
+
protocol_digests (list[str]): The digests of the protocol that the agent supports
|
221
|
+
agentverse_config (AgentverseConfig): The configuration for the agentverse API
|
222
|
+
timeout (int): The timeout for the request
|
223
|
+
"""
|
224
|
+
# get the almanac API endpoint
|
225
|
+
agentverse_config = agentverse_config or AgentverseConfig()
|
226
|
+
almanac_api = urllib.parse.urljoin(agentverse_config.url, DEFAULT_ALMANAC_API_PATH)
|
227
|
+
|
228
|
+
# create the attestation
|
229
|
+
item = AgentRegistrationInput(
|
230
|
+
identity=identity,
|
231
|
+
prefix=prefix,
|
232
|
+
endpoints=endpoints,
|
233
|
+
protocol_digests=protocol_digests,
|
234
|
+
metadata=metadata,
|
235
|
+
)
|
236
|
+
attestation = _build_signed_attestation(item)
|
237
|
+
|
238
|
+
logger.info(msg="Registering with Almanac API", extra=attestation.model_dump())
|
239
|
+
|
240
|
+
# submit the attestation to the API
|
241
|
+
return _send_post_request_agentverse(
|
242
|
+
url=f"{almanac_api}/agents", data=attestation, timeout=timeout
|
243
|
+
)
|
244
|
+
|
245
|
+
|
109
246
|
def register_in_almanac(
|
110
247
|
identity: Identity,
|
111
248
|
endpoints: list[str],
|
@@ -149,27 +286,21 @@ def register_in_almanac(
|
|
149
286
|
)
|
150
287
|
return False
|
151
288
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
logger.info(msg="Registering with Almanac API", extra=attestation.model_dump())
|
289
|
+
try:
|
290
|
+
_register_in_almanac(
|
291
|
+
identity,
|
292
|
+
endpoints,
|
293
|
+
protocol_digests,
|
294
|
+
metadata,
|
295
|
+
prefix,
|
296
|
+
agentverse_config=agentverse_config,
|
297
|
+
timeout=timeout,
|
298
|
+
)
|
299
|
+
return True
|
300
|
+
except AgentverseRequestException as e:
|
301
|
+
logger.error(msg=str(e), exc_info=e.from_exc)
|
167
302
|
|
168
|
-
|
169
|
-
status, _ = _send_post_request(
|
170
|
-
url=f"{almanac_api}/agents", data=attestation, timeout=timeout
|
171
|
-
)
|
172
|
-
return status
|
303
|
+
return False
|
173
304
|
|
174
305
|
|
175
306
|
def register_batch_in_almanac(
|
@@ -252,23 +383,27 @@ def register_batch_in_almanac(
|
|
252
383
|
)
|
253
384
|
|
254
385
|
# submit the attestation to the API
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
386
|
+
try:
|
387
|
+
_send_post_request_agentverse(
|
388
|
+
url=f"{almanac_api}/agents/batch",
|
389
|
+
data=attestation_batch,
|
390
|
+
timeout=timeout,
|
391
|
+
)
|
392
|
+
return True, invalid_identities
|
393
|
+
except AgentverseRequestException as e:
|
394
|
+
logger.error(msg=str(e), exc_info=e.from_exc)
|
261
395
|
|
396
|
+
return False, invalid_identities
|
262
397
|
|
263
|
-
|
264
|
-
def
|
398
|
+
|
399
|
+
def _register_in_agentverse(
|
265
400
|
request: AgentverseConnectRequest,
|
266
401
|
identity: Identity,
|
267
402
|
*,
|
268
403
|
agent_details: AgentUpdates | None = None,
|
269
404
|
agentverse_config: AgentverseConfig | None = None,
|
270
405
|
timeout: int = DEFAULT_REQUEST_TIMEOUT,
|
271
|
-
)
|
406
|
+
):
|
272
407
|
"""
|
273
408
|
Register an agent in Agentverse and update its details if provided.
|
274
409
|
|
@@ -317,18 +452,18 @@ def register_in_agentverse(
|
|
317
452
|
logger.debug(
|
318
453
|
msg="Requesting mailbox access challenge", extra=registration_metadata
|
319
454
|
)
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
455
|
+
try:
|
456
|
+
response = _send_post_request_agentverse(
|
457
|
+
url=challenge_api,
|
458
|
+
data=challenge_request,
|
459
|
+
headers={"authorization": f"Bearer {request.user_token}"},
|
460
|
+
timeout=timeout,
|
461
|
+
)
|
462
|
+
except AgentverseRequestException as e:
|
463
|
+
raise AgentverseRequestException(
|
464
|
+
f"failed to request proof-of-ownership challenge. {str(e)}",
|
465
|
+
from_exc=e.from_exc,
|
330
466
|
)
|
331
|
-
return False
|
332
467
|
|
333
468
|
challenge = ChallengeResponse.model_validate_json(response.text)
|
334
469
|
registration_payload = RegistrationRequest(
|
@@ -338,34 +473,20 @@ def register_in_agentverse(
|
|
338
473
|
endpoint=request.endpoint,
|
339
474
|
agent_type=request.agent_type,
|
340
475
|
)
|
341
|
-
|
476
|
+
|
477
|
+
response = _send_post_request_agentverse(
|
342
478
|
url=registration_api,
|
343
479
|
data=registration_payload,
|
344
480
|
headers={"authorization": f"Bearer {request.user_token}"},
|
345
481
|
timeout=timeout,
|
346
482
|
)
|
347
|
-
if not status or not response:
|
348
|
-
logger.error(
|
349
|
-
msg="Error registering agent with Agentverse",
|
350
|
-
extra=registration_metadata,
|
351
|
-
)
|
352
|
-
return False
|
353
|
-
else:
|
354
|
-
registration_response = RegistrationResponse.model_validate_json(
|
355
|
-
response.text
|
356
|
-
)
|
357
|
-
if registration_response.success:
|
358
|
-
logger.info(
|
359
|
-
msg=f"Successfully registered as {request.agent_type} agent in Agentverse",
|
360
|
-
extra=registration_metadata,
|
361
|
-
)
|
362
483
|
|
363
484
|
if not agent_details:
|
364
485
|
logger.debug(
|
365
486
|
msg="No agent details provided; skipping agent update",
|
366
487
|
extra=registration_metadata,
|
367
488
|
)
|
368
|
-
return
|
489
|
+
return
|
369
490
|
|
370
491
|
# update the readme and the name of the agent to make it easier to find
|
371
492
|
logger.debug(
|
@@ -373,31 +494,55 @@ def register_in_agentverse(
|
|
373
494
|
extra=registration_metadata,
|
374
495
|
)
|
375
496
|
try:
|
376
|
-
response =
|
497
|
+
response = _send_http_request_agentverse(
|
498
|
+
request_type="put",
|
377
499
|
url=f"{registration_api}/{agent_address}",
|
378
500
|
headers={
|
379
501
|
"content-type": "application/json",
|
380
502
|
"authorization": f"Bearer {request.user_token}",
|
381
503
|
},
|
382
|
-
data=agent_details
|
504
|
+
data=agent_details,
|
383
505
|
timeout=timeout,
|
384
506
|
)
|
385
|
-
|
386
|
-
logger.
|
387
|
-
|
388
|
-
|
507
|
+
except AgentverseRequestException as e:
|
508
|
+
logger.warning(f"failed to upload agent details. {str(e)}")
|
509
|
+
|
510
|
+
|
511
|
+
# associate user account with your agent
|
512
|
+
def register_in_agentverse(
|
513
|
+
request: AgentverseConnectRequest,
|
514
|
+
identity: Identity,
|
515
|
+
*,
|
516
|
+
agent_details: AgentUpdates | None = None,
|
517
|
+
agentverse_config: AgentverseConfig | None = None,
|
518
|
+
timeout: int = DEFAULT_REQUEST_TIMEOUT,
|
519
|
+
) -> bool:
|
520
|
+
"""
|
521
|
+
Register an agent in Agentverse and update its details if provided.
|
522
|
+
|
523
|
+
Args:
|
524
|
+
request (AgentverseConnectRequest): The request containing the agent details.
|
525
|
+
identity (Identity): The identity of the agent.
|
526
|
+
agent_details (AgentUpdates | None): The agent details to update.
|
527
|
+
agentverse_config (AgentverseConfig | None): The configuration for the agentverse API
|
528
|
+
timeout (int): The timeout for the requests
|
529
|
+
"""
|
530
|
+
try:
|
531
|
+
_register_in_agentverse(
|
532
|
+
request,
|
533
|
+
identity,
|
534
|
+
agent_details=agent_details,
|
535
|
+
agentverse_config=agentverse_config,
|
536
|
+
timeout=timeout,
|
389
537
|
)
|
390
538
|
return True
|
391
|
-
except
|
392
|
-
logger.error(
|
393
|
-
|
394
|
-
|
395
|
-
exc_info=e,
|
396
|
-
)
|
397
|
-
return False
|
539
|
+
except AgentverseRequestException as e:
|
540
|
+
logger.error(msg=str(e), exc_info=e.from_exc)
|
541
|
+
|
542
|
+
return False
|
398
543
|
|
399
544
|
|
400
|
-
def
|
545
|
+
def _update_agent_status(active: bool, identity: Identity):
|
401
546
|
"""
|
402
547
|
Update the agent's active/inactive status in the Almanac API.
|
403
548
|
|
@@ -417,15 +562,108 @@ def update_agent_status(active: bool, identity: Identity):
|
|
417
562
|
extra=status_update.model_dump(),
|
418
563
|
)
|
419
564
|
|
420
|
-
|
565
|
+
_send_post_request_agentverse(
|
421
566
|
url=f"{almanac_api}/agents/{identity.address}/status",
|
422
567
|
data=status_update,
|
423
568
|
)
|
424
569
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
570
|
+
|
571
|
+
def update_agent_status(active: bool, identity: Identity) -> bool:
|
572
|
+
"""
|
573
|
+
Update the agent's active/inactive status in the Almanac API.
|
574
|
+
|
575
|
+
Args:
|
576
|
+
active (bool): The status of the agent.
|
577
|
+
identity (Identity): The identity of the agent.
|
578
|
+
"""
|
579
|
+
try:
|
580
|
+
_update_agent_status(active, identity)
|
581
|
+
return True
|
582
|
+
except AgentverseRequestException as e:
|
583
|
+
logger.error(msg=str(e), exc_info=e.from_exc)
|
584
|
+
|
585
|
+
return False
|
586
|
+
|
587
|
+
|
588
|
+
def register_agent(
|
589
|
+
agent_registration: AgentverseRegistrationRequest,
|
590
|
+
agentverse_config: AgentverseConfig,
|
591
|
+
credentials: RegistrationRequestCredentials,
|
592
|
+
):
|
593
|
+
identity = Identity.from_seed(credentials.agent_seed_phrase, 0)
|
594
|
+
endpoints = [agent_registration.endpoint]
|
595
|
+
protos = agent_registration.protocols
|
596
|
+
|
597
|
+
connect_request = AgentverseConnectRequest(
|
598
|
+
user_token=credentials.agentverse_api_key,
|
599
|
+
agent_type=agent_registration.type,
|
600
|
+
endpoint=endpoints[0],
|
601
|
+
)
|
602
|
+
|
603
|
+
agent_details = AgentUpdates(
|
604
|
+
name=agent_registration.name,
|
605
|
+
readme=agent_registration.readme,
|
606
|
+
avatar_url=agent_registration.avatar_url,
|
607
|
+
short_description=agent_registration.description,
|
608
|
+
agent_type=agent_registration.type,
|
609
|
+
)
|
610
|
+
|
611
|
+
# register the agent to almanac
|
612
|
+
try:
|
613
|
+
logger.info("registering to Almanac...")
|
614
|
+
_register_in_almanac(
|
615
|
+
identity, endpoints, protos, agentverse_config=agentverse_config
|
429
616
|
)
|
617
|
+
logger.info("successfully registered to Almanac.")
|
618
|
+
except AgentverseRequestException as e:
|
619
|
+
logger.error(f"failed to register to Almanac. {str(e)}")
|
620
|
+
return
|
621
|
+
|
622
|
+
# register the agent to agentverse
|
623
|
+
try:
|
624
|
+
logger.info("registering to Agentverse...")
|
625
|
+
_register_in_agentverse(
|
626
|
+
connect_request,
|
627
|
+
identity,
|
628
|
+
agent_details=agent_details,
|
629
|
+
agentverse_config=agentverse_config,
|
630
|
+
)
|
631
|
+
logger.info("successfully registered to Agentverse.")
|
632
|
+
except AgentverseRequestException as e:
|
633
|
+
logger.error(f"failed to register to Agentverse. {str(e)}")
|
634
|
+
return
|
635
|
+
|
636
|
+
# set agent as active
|
637
|
+
if agent_registration.active:
|
638
|
+
try:
|
639
|
+
logger.info("setting agent as active...")
|
640
|
+
_update_agent_status(True, identity)
|
641
|
+
logger.info("successfully set agent to active.")
|
642
|
+
except AgentverseRequestException as e:
|
643
|
+
logger.warning(f"failed to set agent as active. {str(e)}")
|
644
|
+
|
645
|
+
|
646
|
+
def register_chat_agent(
|
647
|
+
name: str,
|
648
|
+
endpoint: str,
|
649
|
+
active: bool,
|
650
|
+
credentials: RegistrationRequestCredentials,
|
651
|
+
description: str | None = None,
|
652
|
+
readme: str | None = None,
|
653
|
+
avatar_url: str | None = None,
|
654
|
+
):
|
655
|
+
chat_protocol = [
|
656
|
+
ProtocolSpecification.compute_digest(chat_protocol_spec.manifest())
|
657
|
+
]
|
658
|
+
request = AgentverseRegistrationRequest(
|
659
|
+
name=name,
|
660
|
+
endpoint=endpoint,
|
661
|
+
protocols=chat_protocol,
|
662
|
+
active=active,
|
663
|
+
description=description,
|
664
|
+
readme=readme,
|
665
|
+
avatar_url=avatar_url,
|
666
|
+
)
|
667
|
+
config = AgentverseConfig()
|
430
668
|
|
431
|
-
|
669
|
+
register_agent(request, config, credentials)
|
@@ -14,11 +14,55 @@ from uagents_core.config import (
|
|
14
14
|
from uagents_core.helpers import weighted_random_sample
|
15
15
|
from uagents_core.identity import parse_identifier
|
16
16
|
from uagents_core.logger import get_logger
|
17
|
-
from uagents_core.types import Resolver
|
17
|
+
from uagents_core.types import Domain, Resolver
|
18
18
|
|
19
19
|
logger = get_logger("uagents_core.utils.resolver")
|
20
20
|
|
21
21
|
|
22
|
+
def lookup_address_for_domain(
|
23
|
+
agent_identifier: str,
|
24
|
+
*,
|
25
|
+
agentverse_config: AgentverseConfig | None = None,
|
26
|
+
) -> str | None:
|
27
|
+
agentverse_config = agentverse_config or AgentverseConfig()
|
28
|
+
almanac_api = urllib.parse.urljoin(agentverse_config.url, DEFAULT_ALMANAC_API_PATH)
|
29
|
+
|
30
|
+
prefix, domain, _ = parse_identifier(agent_identifier)
|
31
|
+
if not domain:
|
32
|
+
logger.error(
|
33
|
+
"No domain provided in agent identifier",
|
34
|
+
extra={"identifier": agent_identifier},
|
35
|
+
)
|
36
|
+
return None
|
37
|
+
|
38
|
+
params = {"prefix": prefix} if prefix else None
|
39
|
+
try:
|
40
|
+
response = requests.get(
|
41
|
+
url=f"{almanac_api}/domains/{domain}",
|
42
|
+
timeout=DEFAULT_REQUEST_TIMEOUT,
|
43
|
+
params=params,
|
44
|
+
)
|
45
|
+
response.raise_for_status()
|
46
|
+
except requests.RequestException as e:
|
47
|
+
logger.error(
|
48
|
+
msg="Error looking up domain",
|
49
|
+
extra={"domain": domain, "exception": str(e)},
|
50
|
+
)
|
51
|
+
return None
|
52
|
+
|
53
|
+
domain_record = Domain.model_validate(response.json())
|
54
|
+
|
55
|
+
agent_records = domain_record.assigned_agents
|
56
|
+
if len(agent_records) == 0:
|
57
|
+
return None
|
58
|
+
elif len(agent_records) == 1:
|
59
|
+
return agent_records[0].address
|
60
|
+
else:
|
61
|
+
addresses = [val.address for val in agent_records]
|
62
|
+
weights = [val.weight for val in agent_records]
|
63
|
+
return weighted_random_sample(addresses, weights=weights, k=1)[0]
|
64
|
+
|
65
|
+
|
22
66
|
def lookup_endpoint_for_agent(
|
23
67
|
agent_identifier: str,
|
24
68
|
*,
|
@@ -34,19 +78,36 @@ def lookup_endpoint_for_agent(
|
|
34
78
|
Returns:
|
35
79
|
List[str]: The endpoint(s) for the agent.
|
36
80
|
"""
|
37
|
-
_, _, agent_address = parse_identifier(agent_identifier)
|
38
81
|
|
39
82
|
agentverse_config = agentverse_config or AgentverseConfig()
|
40
83
|
almanac_api = urllib.parse.urljoin(agentverse_config.url, DEFAULT_ALMANAC_API_PATH)
|
41
84
|
|
85
|
+
prefix, domain, agent_address = parse_identifier(agent_identifier)
|
86
|
+
|
87
|
+
if not agent_address:
|
88
|
+
if domain:
|
89
|
+
agent_address = lookup_address_for_domain(
|
90
|
+
agent_identifier=agent_identifier,
|
91
|
+
agentverse_config=agentverse_config,
|
92
|
+
)
|
93
|
+
else:
|
94
|
+
logger.error(
|
95
|
+
"No address or domain provided in identifier",
|
96
|
+
extra={"identifier": agent_identifier},
|
97
|
+
)
|
98
|
+
return []
|
99
|
+
|
42
100
|
request_meta: dict[str, Any] = {
|
43
101
|
"agent_address": agent_address,
|
44
102
|
"lookup_url": almanac_api,
|
45
103
|
}
|
46
104
|
logger.debug(msg="looking up endpoint for agent", extra=request_meta)
|
47
105
|
try:
|
106
|
+
params = {"prefix": prefix} if prefix else None
|
48
107
|
response = requests.get(
|
49
|
-
url=f"{almanac_api}/agents/{agent_address}",
|
108
|
+
url=f"{almanac_api}/agents/{agent_address}",
|
109
|
+
params=params,
|
110
|
+
timeout=DEFAULT_REQUEST_TIMEOUT,
|
50
111
|
)
|
51
112
|
response.raise_for_status()
|
52
113
|
except requests.RequestException as e:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{uagents_core-0.3.7 → uagents_core-0.3.8}/uagents_core/contrib/protocols/subscriptions/__init__.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|