uagents-core 0.3.3__py3-none-any.whl → 0.3.5__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.
- uagents_core/config.py +5 -0
- uagents_core/storage.py +47 -16
- uagents_core/types.py +29 -0
- uagents_core/utils/messages.py +9 -5
- uagents_core/utils/resolver.py +18 -0
- uagents_core/utils/subscriptions.py +82 -0
- {uagents_core-0.3.3.dist-info → uagents_core-0.3.5.dist-info}/METADATA +1 -1
- {uagents_core-0.3.3.dist-info → uagents_core-0.3.5.dist-info}/RECORD +9 -8
- {uagents_core-0.3.3.dist-info → uagents_core-0.3.5.dist-info}/WHEEL +0 -0
uagents_core/config.py
CHANGED
@@ -7,6 +7,7 @@ DEFAULT_CHALLENGE_PATH = "/v1/auth/challenge"
|
|
7
7
|
DEFAULT_MAILBOX_PATH = "/v1/submit"
|
8
8
|
DEFAULT_PROXY_PATH = "/v1/proxy/submit"
|
9
9
|
DEFAULT_STORAGE_PATH = "/v1/storage"
|
10
|
+
DEFAULT_PAYMENTS_PATH = "/v1/payments"
|
10
11
|
|
11
12
|
DEFAULT_MAX_ENDPOINTS = 10
|
12
13
|
|
@@ -35,3 +36,7 @@ class AgentverseConfig(BaseModel):
|
|
35
36
|
@property
|
36
37
|
def storage_endpoint(self) -> str:
|
37
38
|
return f"{self.url}{DEFAULT_STORAGE_PATH}"
|
39
|
+
|
40
|
+
@property
|
41
|
+
def payments_endpoint(self) -> str:
|
42
|
+
return f"{self.url}{DEFAULT_PAYMENTS_PATH}"
|
uagents_core/storage.py
CHANGED
@@ -2,7 +2,6 @@ import base64
|
|
2
2
|
import struct
|
3
3
|
from datetime import datetime
|
4
4
|
from secrets import token_bytes
|
5
|
-
from typing import Optional
|
6
5
|
|
7
6
|
import requests
|
8
7
|
|
@@ -34,38 +33,57 @@ def compute_attestation(
|
|
34
33
|
class ExternalStorage:
|
35
34
|
def __init__(
|
36
35
|
self,
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
*,
|
37
|
+
identity: Identity | None = None,
|
38
|
+
storage_url: str | None = None,
|
39
|
+
api_token: str | None = None,
|
40
40
|
):
|
41
41
|
self.identity = identity
|
42
42
|
self.api_token = api_token
|
43
|
+
if not (identity or api_token):
|
44
|
+
raise ValueError(
|
45
|
+
"Either an identity or an API token must be provided for authentication"
|
46
|
+
)
|
43
47
|
self.storage_url = storage_url or AgentverseConfig().storage_endpoint
|
44
48
|
|
45
49
|
def _make_attestation(self) -> str:
|
46
50
|
nonce = token_bytes(32)
|
47
51
|
now = datetime.now()
|
48
|
-
|
52
|
+
if not self.identity:
|
53
|
+
raise RuntimeError("No identity available to create attestation")
|
54
|
+
return compute_attestation(
|
55
|
+
identity=self.identity,
|
56
|
+
validity_start=now,
|
57
|
+
validity_secs=3600,
|
58
|
+
nonce=nonce,
|
59
|
+
)
|
49
60
|
|
50
61
|
def _get_auth_header(self) -> dict:
|
51
62
|
if self.api_token:
|
52
63
|
return {"Authorization": f"Bearer {self.api_token}"}
|
53
|
-
|
64
|
+
if self.identity:
|
54
65
|
return {"Authorization": f"Agent {self._make_attestation()}"}
|
55
|
-
|
56
|
-
raise RuntimeError("No identity or API token available for authentication")
|
66
|
+
raise RuntimeError("No identity or API token available for authentication")
|
57
67
|
|
58
68
|
def upload(
|
59
|
-
self,
|
69
|
+
self,
|
70
|
+
asset_id: str,
|
71
|
+
asset_content: bytes,
|
72
|
+
mime_type: str = "text/plain",
|
60
73
|
) -> dict:
|
61
74
|
url = f"{self.storage_url}/assets/{asset_id}/contents/"
|
62
75
|
headers = self._get_auth_header()
|
63
76
|
headers["Content-Type"] = "application/json"
|
64
77
|
payload = {
|
65
|
-
"contents": base64.b64encode(
|
78
|
+
"contents": base64.b64encode(asset_content).decode(),
|
66
79
|
"mime_type": mime_type,
|
67
80
|
}
|
68
|
-
response = requests.put(
|
81
|
+
response = requests.put(
|
82
|
+
url=url,
|
83
|
+
json=payload,
|
84
|
+
headers=headers,
|
85
|
+
timeout=10,
|
86
|
+
)
|
69
87
|
if response.status_code != 200:
|
70
88
|
raise RuntimeError(
|
71
89
|
f"Upload failed: {response.status_code}, {response.text}"
|
@@ -76,8 +94,11 @@ class ExternalStorage:
|
|
76
94
|
def download(self, asset_id: str) -> dict:
|
77
95
|
url = f"{self.storage_url}/assets/{asset_id}/contents/"
|
78
96
|
headers = self._get_auth_header()
|
79
|
-
|
80
|
-
|
97
|
+
response = requests.get(
|
98
|
+
url=url,
|
99
|
+
headers=headers,
|
100
|
+
timeout=10,
|
101
|
+
)
|
81
102
|
if response.status_code != 200:
|
82
103
|
raise RuntimeError(
|
83
104
|
f"Download failed: {response.status_code}, {response.text}"
|
@@ -104,7 +125,12 @@ class ExternalStorage:
|
|
104
125
|
"lifetime_hours": lifetime_hours,
|
105
126
|
}
|
106
127
|
|
107
|
-
response = requests.post(
|
128
|
+
response = requests.post(
|
129
|
+
url=url,
|
130
|
+
json=payload,
|
131
|
+
headers=headers,
|
132
|
+
timeout=10,
|
133
|
+
)
|
108
134
|
if response.status_code != 201:
|
109
135
|
raise RuntimeError(
|
110
136
|
f"Asset creation failed: {response.status_code}, {response.text}"
|
@@ -114,7 +140,7 @@ class ExternalStorage:
|
|
114
140
|
|
115
141
|
def set_permissions(
|
116
142
|
self, asset_id: str, agent_address: str, read: bool = True, write: bool = True
|
117
|
-
):
|
143
|
+
) -> dict:
|
118
144
|
if not self.api_token:
|
119
145
|
raise RuntimeError("API token required to set permissions")
|
120
146
|
url = f"{self.storage_url}/assets/{asset_id}/permissions/"
|
@@ -126,7 +152,12 @@ class ExternalStorage:
|
|
126
152
|
"write": write,
|
127
153
|
}
|
128
154
|
|
129
|
-
response = requests.put(
|
155
|
+
response = requests.put(
|
156
|
+
url=url,
|
157
|
+
json=payload,
|
158
|
+
headers=headers,
|
159
|
+
timeout=10,
|
160
|
+
)
|
130
161
|
if response.status_code != 200:
|
131
162
|
raise RuntimeError(
|
132
163
|
f"Set permissions failed: {response.status_code}, {response.text}"
|
uagents_core/types.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import uuid
|
2
|
+
from abc import ABC, abstractmethod
|
2
3
|
from enum import Enum
|
3
4
|
from typing import Any, Literal
|
4
5
|
|
@@ -48,3 +49,31 @@ class MsgStatus(BaseModel):
|
|
48
49
|
destination: str
|
49
50
|
endpoint: str
|
50
51
|
session: uuid.UUID | None = None
|
52
|
+
|
53
|
+
|
54
|
+
class Resolver(ABC):
|
55
|
+
@abstractmethod
|
56
|
+
async def resolve(self, destination: str) -> tuple[str | None, list[str]]:
|
57
|
+
"""
|
58
|
+
Resolve the destination to an address and endpoint.
|
59
|
+
|
60
|
+
Args:
|
61
|
+
destination (str): The destination name or address to resolve.
|
62
|
+
|
63
|
+
Returns:
|
64
|
+
tuple[str | None, list[str]]: The address (if available) and resolved endpoints.
|
65
|
+
"""
|
66
|
+
raise NotImplementedError
|
67
|
+
|
68
|
+
@abstractmethod
|
69
|
+
def sync_resolve(self, destination: str) -> list[str]:
|
70
|
+
"""
|
71
|
+
Resolve the destination to a list of endpoints.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
destination (str): The destination name or address to resolve.
|
75
|
+
|
76
|
+
Returns:
|
77
|
+
list[str]: The resolved endpoints.
|
78
|
+
"""
|
79
|
+
raise NotImplementedError
|
uagents_core/utils/messages.py
CHANGED
@@ -14,8 +14,8 @@ from uagents_core.helpers import weighted_random_sample
|
|
14
14
|
from uagents_core.identity import Identity
|
15
15
|
from uagents_core.logger import get_logger
|
16
16
|
from uagents_core.models import Model
|
17
|
-
from uagents_core.types import DeliveryStatus, MsgStatus
|
18
|
-
from uagents_core.utils.resolver import
|
17
|
+
from uagents_core.types import DeliveryStatus, MsgStatus, Resolver
|
18
|
+
from uagents_core.utils.resolver import AlmanacResolver
|
19
19
|
|
20
20
|
logger = get_logger("uagents_core.utils.messages")
|
21
21
|
|
@@ -89,6 +89,7 @@ def send_message_to_agent(
|
|
89
89
|
session_id: UUID | None = None,
|
90
90
|
strategy: Literal["first", "random", "all"] = "first",
|
91
91
|
agentverse_config: AgentverseConfig | None = None,
|
92
|
+
resolver: Resolver | None = None,
|
92
93
|
) -> list[MsgStatus]:
|
93
94
|
"""
|
94
95
|
Send a message to an agent with default settings.
|
@@ -103,9 +104,12 @@ def send_message_to_agent(
|
|
103
104
|
agentverse_config (AgentverseConfig, optional): The configuration for the agentverse.
|
104
105
|
"""
|
105
106
|
agentverse_config = agentverse_config or AgentverseConfig()
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
|
108
|
+
if not resolver:
|
109
|
+
resolver = AlmanacResolver(
|
110
|
+
agentverse_config=agentverse_config,
|
111
|
+
)
|
112
|
+
endpoints = resolver.sync_resolve(destination)
|
109
113
|
if not endpoints:
|
110
114
|
logger.error("No endpoints found for agent", extra={"destination": destination})
|
111
115
|
return []
|
uagents_core/utils/resolver.py
CHANGED
@@ -14,6 +14,7 @@ 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
18
|
|
18
19
|
logger = get_logger("uagents_core.utils.resolver")
|
19
20
|
|
@@ -71,3 +72,20 @@ def lookup_endpoint_for_agent(
|
|
71
72
|
)
|
72
73
|
|
73
74
|
return []
|
75
|
+
|
76
|
+
|
77
|
+
class AlmanacResolver(Resolver):
|
78
|
+
def __init__(self, agentverse_config: AgentverseConfig | None = None):
|
79
|
+
self.agentverse_config = agentverse_config or AgentverseConfig()
|
80
|
+
|
81
|
+
async def resolve(self, destination: str) -> tuple[str | None, list[str]]:
|
82
|
+
endpoints = lookup_endpoint_for_agent(
|
83
|
+
agent_identifier=destination, agentverse_config=self.agentverse_config
|
84
|
+
)
|
85
|
+
return None, endpoints
|
86
|
+
|
87
|
+
def sync_resolve(self, destination: str) -> list[str]:
|
88
|
+
endpoints = lookup_endpoint_for_agent(
|
89
|
+
agent_identifier=destination, agentverse_config=self.agentverse_config
|
90
|
+
)
|
91
|
+
return endpoints
|
@@ -0,0 +1,82 @@
|
|
1
|
+
"""
|
2
|
+
This module provides methods related to agent bases subscriptions.
|
3
|
+
|
4
|
+
Example usage:
|
5
|
+
```
|
6
|
+
from uagents_core.contrib.protocols.subscriptions import TierType
|
7
|
+
|
8
|
+
@protocol.on_message(ChatMessage)
|
9
|
+
async def handle_message(ctx: Context, sender: str, msg: ChatMessage):
|
10
|
+
subscription_tier = get_subscription_tier(
|
11
|
+
identity=ctx.agent.identity,
|
12
|
+
requester_address=sender
|
13
|
+
)
|
14
|
+
|
15
|
+
if subscription_tier == TierType.PLUS:
|
16
|
+
...
|
17
|
+
|
18
|
+
if subscription_tier == TierType.PRO:
|
19
|
+
...
|
20
|
+
|
21
|
+
...
|
22
|
+
```
|
23
|
+
|
24
|
+
"""
|
25
|
+
|
26
|
+
from datetime import datetime
|
27
|
+
from secrets import token_bytes
|
28
|
+
|
29
|
+
import requests
|
30
|
+
|
31
|
+
from uagents_core.config import AgentverseConfig
|
32
|
+
from uagents_core.contrib.protocols.subscriptions import TierType
|
33
|
+
from uagents_core.identity import Identity
|
34
|
+
from uagents_core.logger import get_logger
|
35
|
+
from uagents_core.storage import compute_attestation
|
36
|
+
|
37
|
+
logger = get_logger("uagents_core.utils.subscriptions")
|
38
|
+
|
39
|
+
|
40
|
+
def get_subscription_tier(
|
41
|
+
identity: Identity,
|
42
|
+
requester_address: str,
|
43
|
+
agentverse_config: AgentverseConfig | None = None,
|
44
|
+
) -> TierType:
|
45
|
+
"""
|
46
|
+
Get the subscription tier of the requester for a specific agent.
|
47
|
+
|
48
|
+
This function is used to verify the type of subscription before processing
|
49
|
+
an incoming message.
|
50
|
+
|
51
|
+
Args:
|
52
|
+
identity (Identity): The identity of the agent that is requested.
|
53
|
+
requester_address (str): The address of the requester to check.
|
54
|
+
agentverse_config (AgentverseConfig | None): The configuration for the Agentverse.
|
55
|
+
If not provided, defaults to a new instance of AgentverseConfig.
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
TierType: The subscription tier type of the requester.
|
59
|
+
"""
|
60
|
+
if not agentverse_config:
|
61
|
+
agentverse_config = AgentverseConfig()
|
62
|
+
attestation: str = compute_attestation(
|
63
|
+
identity=identity,
|
64
|
+
validity_start=datetime.now(),
|
65
|
+
validity_secs=60,
|
66
|
+
nonce=token_bytes(nbytes=32),
|
67
|
+
)
|
68
|
+
url: str = (
|
69
|
+
f"{agentverse_config.payments_endpoint}/subscriptions"
|
70
|
+
+ f"/{identity.address}/{requester_address}"
|
71
|
+
)
|
72
|
+
headers: dict[str, str] = {"Authorization": f"Agent {attestation}"}
|
73
|
+
|
74
|
+
try:
|
75
|
+
response = requests.get(url=url, headers=headers, timeout=10)
|
76
|
+
response.raise_for_status()
|
77
|
+
except requests.RequestException as e:
|
78
|
+
logger.error(f"Failed to get subscription tier: {e}")
|
79
|
+
return TierType.FREE
|
80
|
+
|
81
|
+
data: dict = response.json()
|
82
|
+
return data.get("tier_type", TierType.FREE)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
uagents_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
uagents_core/config.py,sha256=
|
2
|
+
uagents_core/config.py,sha256=dvecXVs_Ej2UqjgrL96QwsEoUpkwJX5rQ09hikCRbDQ,1086
|
3
3
|
uagents_core/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
uagents_core/contrib/protocols/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
uagents_core/contrib/protocols/chat/__init__.py,sha256=EAt2uQeaZtrSEdt2SiFyqFcC4nxKtyoJRaHhjKtwngw,3032
|
@@ -11,12 +11,13 @@ 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
13
|
uagents_core/registration.py,sha256=lmDXnsAs2CvpgE1Ik0qja_0RAY7zJruK11_jUrlqlWs,3226
|
14
|
-
uagents_core/storage.py,sha256=
|
15
|
-
uagents_core/types.py,sha256=
|
14
|
+
uagents_core/storage.py,sha256=VLqMSFXOspzrlbUNvaqS95ht9oJze1c-p4CD6Ul80h4,5150
|
15
|
+
uagents_core/types.py,sha256=nPK2ebRL6S-vLZx2AR3OO2RTEb_N2Yno6PqL8KHkeo4,1993
|
16
16
|
uagents_core/utils/__init__.py,sha256=v0MaxDYCTtQlwbblEHCfLtbeTnA2hCmKKJk7mlcE20U,135
|
17
|
-
uagents_core/utils/messages.py,sha256=
|
17
|
+
uagents_core/utils/messages.py,sha256=x_99pLzMjKKuWrpSAAS9mtVUAjFEZco64mMmHJdtxEI,5149
|
18
18
|
uagents_core/utils/registration.py,sha256=Lp17tn4knw4qpKx_mZO5gDHD6pGssUhux6SGDhhb7rc,14607
|
19
|
-
uagents_core/utils/resolver.py,sha256=
|
20
|
-
uagents_core
|
21
|
-
uagents_core-0.3.
|
22
|
-
uagents_core-0.3.
|
19
|
+
uagents_core/utils/resolver.py,sha256=zcHKkTWxuIHq1kR89RDocruGs3i1S2baQpg4TK4j8QY,2868
|
20
|
+
uagents_core/utils/subscriptions.py,sha256=l29elYqKycgU3s2SN_oh-svfhDkqZce0-EIiv4jjxJw,2450
|
21
|
+
uagents_core-0.3.5.dist-info/METADATA,sha256=UbvgotVaLQXTyBJUWwW3dmjs6inQO2J7iPz2ybU2Amw,1009
|
22
|
+
uagents_core-0.3.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
23
|
+
uagents_core-0.3.5.dist-info/RECORD,,
|
File without changes
|