nest-core 0.1.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.
- nest_core-0.1.0/.gitignore +10 -0
- nest_core-0.1.0/PKG-INFO +32 -0
- nest_core-0.1.0/README.md +13 -0
- nest_core-0.1.0/nest_core/__init__.py +4 -0
- nest_core-0.1.0/nest_core/inspect.py +136 -0
- nest_core-0.1.0/nest_core/layers/__init__.py +35 -0
- nest_core-0.1.0/nest_core/layers/auth.py +53 -0
- nest_core-0.1.0/nest_core/layers/comms.py +71 -0
- nest_core-0.1.0/nest_core/layers/coordination.py +62 -0
- nest_core-0.1.0/nest_core/layers/datafacts.py +64 -0
- nest_core-0.1.0/nest_core/layers/identity.py +53 -0
- nest_core-0.1.0/nest_core/layers/memory.py +62 -0
- nest_core-0.1.0/nest_core/layers/negotiation.py +68 -0
- nest_core-0.1.0/nest_core/layers/payments.py +63 -0
- nest_core-0.1.0/nest_core/layers/privacy.py +62 -0
- nest_core-0.1.0/nest_core/layers/registry.py +64 -0
- nest_core-0.1.0/nest_core/layers/transport.py +55 -0
- nest_core-0.1.0/nest_core/layers/trust.py +62 -0
- nest_core-0.1.0/nest_core/metrics.py +408 -0
- nest_core-0.1.0/nest_core/plugins.py +113 -0
- nest_core-0.1.0/nest_core/py.typed +0 -0
- nest_core-0.1.0/nest_core/runner.py +188 -0
- nest_core-0.1.0/nest_core/scenario.py +159 -0
- nest_core-0.1.0/nest_core/scenarios.py +73 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/__init__.py +2 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/auction.py +146 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/consensus.py +130 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/marketplace.py +425 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/reputation.py +183 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/supply_chain.py +135 -0
- nest_core-0.1.0/nest_core/scenarios_builtin/voting.py +154 -0
- nest_core-0.1.0/nest_core/sim/__init__.py +27 -0
- nest_core-0.1.0/nest_core/sim/agent.py +144 -0
- nest_core-0.1.0/nest_core/sim/clock.py +49 -0
- nest_core-0.1.0/nest_core/sim/events.py +92 -0
- nest_core-0.1.0/nest_core/sim/simulator.py +381 -0
- nest_core-0.1.0/nest_core/sim/trace.py +71 -0
- nest_core-0.1.0/nest_core/sim/transport.py +95 -0
- nest_core-0.1.0/nest_core/types.py +545 -0
- nest_core-0.1.0/nest_core/validators.py +882 -0
- nest_core-0.1.0/pyproject.toml +32 -0
- nest_core-0.1.0/tests/test_failures.py +187 -0
- nest_core-0.1.0/tests/test_imports.py +30 -0
- nest_core-0.1.0/tests/test_inspect.py +107 -0
- nest_core-0.1.0/tests/test_layers.py +57 -0
- nest_core-0.1.0/tests/test_metrics.py +320 -0
- nest_core-0.1.0/tests/test_properties.py +442 -0
- nest_core-0.1.0/tests/test_scenario.py +177 -0
- nest_core-0.1.0/tests/test_scenarios.py +334 -0
- nest_core-0.1.0/tests/test_sim.py +236 -0
- nest_core-0.1.0/tests/test_types.py +73 -0
- nest_core-0.1.0/tests/test_validators.py +589 -0
nest_core-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nest-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NEST core engine: layer interfaces, runtime, event loop, and simulator
|
|
5
|
+
Project-URL: Homepage, https://github.com/mariagorskikh/nest
|
|
6
|
+
Project-URL: Repository, https://github.com/mariagorskikh/nest
|
|
7
|
+
License-Expression: Apache-2.0
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
13
|
+
Classifier: Topic :: Software Development :: Testing
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: pydantic>=2.0
|
|
17
|
+
Requires-Dist: pyyaml>=6.0
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# nest-core
|
|
21
|
+
|
|
22
|
+
NEST core engine: layer interfaces, runtime, event loop, and simulator
|
|
23
|
+
|
|
24
|
+
Part of [NEST](https://github.com/mariagorskikh/nest) (Network Environment for Swarm Testing), built at MIT Media Lab.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install nest-core
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
See the [main repository](https://github.com/mariagorskikh/nest) for full documentation.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# nest-core
|
|
2
|
+
|
|
3
|
+
NEST core engine: layer interfaces, runtime, event loop, and simulator
|
|
4
|
+
|
|
5
|
+
Part of [NEST](https://github.com/mariagorskikh/nest) (Network Environment for Swarm Testing), built at MIT Media Lab.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install nest-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
See the [main repository](https://github.com/mariagorskikh/nest) for full documentation.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Trace file inspection and analysis.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
summary = analyze_trace("traces/marketplace.jsonl")
|
|
7
|
+
print(summary.total_events)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from collections import Counter
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class AgentStats:
|
|
20
|
+
"""Per-agent statistics extracted from a trace."""
|
|
21
|
+
|
|
22
|
+
sends: int = 0
|
|
23
|
+
receives: int = 0
|
|
24
|
+
started: bool = False
|
|
25
|
+
stopped: bool = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class TraceSummary:
|
|
30
|
+
"""Summary statistics for a JSONL trace file."""
|
|
31
|
+
|
|
32
|
+
total_events: int = 0
|
|
33
|
+
duration: float = 0.0
|
|
34
|
+
event_kinds: dict[str, int] = field(default_factory=lambda: dict[str, int]())
|
|
35
|
+
agent_count: int = 0
|
|
36
|
+
message_count: int = 0
|
|
37
|
+
unique_correlations: int = 0
|
|
38
|
+
agents: dict[str, AgentStats] = field(default_factory=lambda: dict[str, AgentStats]())
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def analyze_trace(path: str | Path) -> TraceSummary:
|
|
42
|
+
"""Analyze a JSONL trace file and return summary statistics.
|
|
43
|
+
|
|
44
|
+
Example::
|
|
45
|
+
|
|
46
|
+
summary = analyze_trace("trace.jsonl")
|
|
47
|
+
"""
|
|
48
|
+
path = Path(path)
|
|
49
|
+
summary = TraceSummary()
|
|
50
|
+
kind_counter: Counter[str] = Counter()
|
|
51
|
+
agents_seen: set[str] = set()
|
|
52
|
+
corr_ids: set[str] = set()
|
|
53
|
+
min_ts = float("inf")
|
|
54
|
+
max_ts = float("-inf")
|
|
55
|
+
|
|
56
|
+
with path.open() as f:
|
|
57
|
+
for line in f:
|
|
58
|
+
line = line.strip()
|
|
59
|
+
if not line:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
event = json.loads(line)
|
|
63
|
+
summary.total_events += 1
|
|
64
|
+
|
|
65
|
+
ts = event.get("ts", 0.0)
|
|
66
|
+
if ts < min_ts:
|
|
67
|
+
min_ts = ts
|
|
68
|
+
if ts > max_ts:
|
|
69
|
+
max_ts = ts
|
|
70
|
+
|
|
71
|
+
kind = event.get("kind", "unknown")
|
|
72
|
+
kind_counter[kind] += 1
|
|
73
|
+
|
|
74
|
+
agent = event.get("agent", "")
|
|
75
|
+
if agent:
|
|
76
|
+
agents_seen.add(agent)
|
|
77
|
+
if agent not in summary.agents:
|
|
78
|
+
summary.agents[agent] = AgentStats()
|
|
79
|
+
|
|
80
|
+
stats = summary.agents[agent]
|
|
81
|
+
if kind == "send":
|
|
82
|
+
stats.sends += 1
|
|
83
|
+
elif kind == "receive":
|
|
84
|
+
stats.receives += 1
|
|
85
|
+
elif kind == "start":
|
|
86
|
+
stats.started = True
|
|
87
|
+
elif kind == "stop":
|
|
88
|
+
stats.stopped = True
|
|
89
|
+
|
|
90
|
+
corr = event.get("corr")
|
|
91
|
+
if corr is not None:
|
|
92
|
+
corr_ids.add(str(corr))
|
|
93
|
+
|
|
94
|
+
if kind in ("send", "receive"):
|
|
95
|
+
summary.message_count += 1
|
|
96
|
+
|
|
97
|
+
summary.event_kinds = dict(kind_counter)
|
|
98
|
+
summary.agent_count = len(agents_seen)
|
|
99
|
+
summary.unique_correlations = len(corr_ids)
|
|
100
|
+
if max_ts > min_ts:
|
|
101
|
+
summary.duration = max_ts - min_ts
|
|
102
|
+
|
|
103
|
+
return summary
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def format_summary(summary: TraceSummary) -> str:
|
|
107
|
+
"""Format a TraceSummary as a human-readable report.
|
|
108
|
+
|
|
109
|
+
Example::
|
|
110
|
+
|
|
111
|
+
print(format_summary(summary))
|
|
112
|
+
"""
|
|
113
|
+
lines: list[str] = []
|
|
114
|
+
lines.append("NEST Trace Summary")
|
|
115
|
+
lines.append("=" * 40)
|
|
116
|
+
lines.append(f" Total events: {summary.total_events}")
|
|
117
|
+
lines.append(f" Agents: {summary.agent_count}")
|
|
118
|
+
lines.append(f" Messages: {summary.message_count}")
|
|
119
|
+
lines.append(f" Correlation IDs: {summary.unique_correlations}")
|
|
120
|
+
lines.append(f" Duration: {summary.duration:.1f} ticks")
|
|
121
|
+
lines.append("")
|
|
122
|
+
lines.append("Event breakdown:")
|
|
123
|
+
for kind, count in sorted(summary.event_kinds.items()):
|
|
124
|
+
lines.append(f" {kind:20s} {count:>6d}")
|
|
125
|
+
|
|
126
|
+
lines.append("")
|
|
127
|
+
lines.append("Top agents by sends:")
|
|
128
|
+
top_senders = sorted(
|
|
129
|
+
summary.agents.items(),
|
|
130
|
+
key=lambda kv: kv[1].sends,
|
|
131
|
+
reverse=True,
|
|
132
|
+
)[:10]
|
|
133
|
+
for agent_name, stats in top_senders:
|
|
134
|
+
lines.append(f" {agent_name:20s} sent={stats.sends:>4d} recv={stats.receives:>4d}")
|
|
135
|
+
|
|
136
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Layer interface definitions for all 12 pluggable layers.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
from nest_core.layers import Transport, Payments, Registry
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from nest_core.layers.auth import Auth
|
|
10
|
+
from nest_core.layers.comms import CommsProtocol
|
|
11
|
+
from nest_core.layers.coordination import Coordination
|
|
12
|
+
from nest_core.layers.datafacts import DataFacts
|
|
13
|
+
from nest_core.layers.identity import Identity
|
|
14
|
+
from nest_core.layers.memory import Memory
|
|
15
|
+
from nest_core.layers.negotiation import Negotiation
|
|
16
|
+
from nest_core.layers.payments import Payments
|
|
17
|
+
from nest_core.layers.privacy import Privacy
|
|
18
|
+
from nest_core.layers.registry import Registry
|
|
19
|
+
from nest_core.layers.transport import Transport
|
|
20
|
+
from nest_core.layers.trust import Trust
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Auth",
|
|
24
|
+
"CommsProtocol",
|
|
25
|
+
"Coordination",
|
|
26
|
+
"DataFacts",
|
|
27
|
+
"Identity",
|
|
28
|
+
"Memory",
|
|
29
|
+
"Negotiation",
|
|
30
|
+
"Payments",
|
|
31
|
+
"Privacy",
|
|
32
|
+
"Registry",
|
|
33
|
+
"Transport",
|
|
34
|
+
"Trust",
|
|
35
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Auth layer interface: authentication and authorization.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyAuth(Auth):
|
|
7
|
+
async def issue(self, subject, scopes):
|
|
8
|
+
return Token(jwt.encode({"sub": subject, "scopes": scopes}, key))
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from nest_core.types import AgentId, AuthContext, Token
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@runtime_checkable
|
|
19
|
+
class Auth(Protocol):
|
|
20
|
+
"""Authentication and authorization for agents.
|
|
21
|
+
|
|
22
|
+
Example::
|
|
23
|
+
|
|
24
|
+
auth: Auth = JwtAuth(identity)
|
|
25
|
+
token = await auth.issue(AgentId("a1"), ["read", "write"])
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
async def issue(self, subject: AgentId, scopes: list[str]) -> Token:
|
|
29
|
+
"""Issue an auth token for a subject with given scopes.
|
|
30
|
+
|
|
31
|
+
Example::
|
|
32
|
+
|
|
33
|
+
token = await auth.issue(AgentId("a1"), ["read"])
|
|
34
|
+
"""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
async def verify(self, token: Token) -> AuthContext:
|
|
38
|
+
"""Verify a token and return its auth context.
|
|
39
|
+
|
|
40
|
+
Example::
|
|
41
|
+
|
|
42
|
+
ctx = await auth.verify(token)
|
|
43
|
+
"""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
async def revoke(self, token: Token) -> None:
|
|
47
|
+
"""Revoke a previously issued token.
|
|
48
|
+
|
|
49
|
+
Example::
|
|
50
|
+
|
|
51
|
+
await auth.revoke(token)
|
|
52
|
+
"""
|
|
53
|
+
...
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Communication layer interface: message format, request/response, discovery.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyComms(CommsProtocol):
|
|
7
|
+
def serialize(self, msg):
|
|
8
|
+
return json.dumps(msg.model_dump()).encode()
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from nest_core.types import AgentCard, AgentId, Message, Query, Response
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@runtime_checkable
|
|
19
|
+
class CommsProtocol(Protocol):
|
|
20
|
+
"""Wire protocol for agent communication.
|
|
21
|
+
|
|
22
|
+
Example::
|
|
23
|
+
|
|
24
|
+
comms: CommsProtocol = NestNativeComms()
|
|
25
|
+
raw = comms.serialize(msg)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def serialize(self, msg: Message) -> bytes:
|
|
29
|
+
"""Serialize a Message into bytes for transport.
|
|
30
|
+
|
|
31
|
+
Example::
|
|
32
|
+
|
|
33
|
+
raw = comms.serialize(msg)
|
|
34
|
+
"""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def deserialize(self, raw: bytes) -> Message:
|
|
38
|
+
"""Deserialize bytes back into a Message.
|
|
39
|
+
|
|
40
|
+
Example::
|
|
41
|
+
|
|
42
|
+
msg = comms.deserialize(raw)
|
|
43
|
+
"""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
async def send(self, to: AgentId, msg: Message) -> Response:
|
|
47
|
+
"""Send a message and wait for a response.
|
|
48
|
+
|
|
49
|
+
Example::
|
|
50
|
+
|
|
51
|
+
resp = await comms.send(AgentId("a2"), msg)
|
|
52
|
+
"""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
async def advertise(self, card: AgentCard) -> None:
|
|
56
|
+
"""Advertise this agent's capabilities.
|
|
57
|
+
|
|
58
|
+
Example::
|
|
59
|
+
|
|
60
|
+
await comms.advertise(my_card)
|
|
61
|
+
"""
|
|
62
|
+
...
|
|
63
|
+
|
|
64
|
+
async def discover(self, query: Query) -> list[AgentCard]:
|
|
65
|
+
"""Discover agents matching a query.
|
|
66
|
+
|
|
67
|
+
Example::
|
|
68
|
+
|
|
69
|
+
cards = await comms.discover(Query(capabilities=["sell_data"]))
|
|
70
|
+
"""
|
|
71
|
+
...
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Coordination layer interface: how groups decide.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyCoordination(Coordination):
|
|
7
|
+
async def propose(self, task):
|
|
8
|
+
return Round(id="r1", task=task)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from nest_core.types import Bid, Outcome, Round, Task, Vote
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@runtime_checkable
|
|
19
|
+
class Coordination(Protocol):
|
|
20
|
+
"""Group decision-making protocol.
|
|
21
|
+
|
|
22
|
+
Example::
|
|
23
|
+
|
|
24
|
+
coord: Coordination = ContractNet()
|
|
25
|
+
rnd = await coord.propose(task)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
async def propose(self, task: Task) -> Round:
|
|
29
|
+
"""Propose a task for group coordination.
|
|
30
|
+
|
|
31
|
+
Example::
|
|
32
|
+
|
|
33
|
+
rnd = await coord.propose(Task(id="t1", description="process data"))
|
|
34
|
+
"""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
async def participate(self, round: Round) -> Vote | Bid:
|
|
38
|
+
"""Participate in a coordination round with a vote or bid.
|
|
39
|
+
|
|
40
|
+
Example::
|
|
41
|
+
|
|
42
|
+
bid = await coord.participate(rnd)
|
|
43
|
+
"""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
async def resolve(self, round: Round) -> Outcome:
|
|
47
|
+
"""Resolve a coordination round to an outcome.
|
|
48
|
+
|
|
49
|
+
Example::
|
|
50
|
+
|
|
51
|
+
outcome = await coord.resolve(rnd)
|
|
52
|
+
"""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
async def commit(self, outcome: Outcome) -> None:
|
|
56
|
+
"""Commit to a resolved outcome.
|
|
57
|
+
|
|
58
|
+
Example::
|
|
59
|
+
|
|
60
|
+
await coord.commit(outcome)
|
|
61
|
+
"""
|
|
62
|
+
...
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""DataFacts layer interface: dataset metadata, freshness, integrity, access.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyDataFacts(DataFacts):
|
|
7
|
+
async def publish(self, dataset):
|
|
8
|
+
url = DataFactsUrl(f"df://{dataset.name}")
|
|
9
|
+
self._store[url] = dataset
|
|
10
|
+
return url
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Protocol, runtime_checkable
|
|
16
|
+
|
|
17
|
+
from nest_core.types import AccessGrant, AgentId, DataFactsUrl, DatasetMetadata
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@runtime_checkable
|
|
21
|
+
class DataFacts(Protocol):
|
|
22
|
+
"""Dataset metadata exchange protocol.
|
|
23
|
+
|
|
24
|
+
Example::
|
|
25
|
+
|
|
26
|
+
df: DataFacts = DataFactsV1()
|
|
27
|
+
url = await df.publish(meta)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
async def publish(self, dataset: DatasetMetadata) -> DataFactsUrl:
|
|
31
|
+
"""Publish dataset metadata and return its URL.
|
|
32
|
+
|
|
33
|
+
Example::
|
|
34
|
+
|
|
35
|
+
url = await df.publish(DatasetMetadata(name="weather", owner=AgentId("a1")))
|
|
36
|
+
"""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
async def fetch(self, url: DataFactsUrl) -> DatasetMetadata:
|
|
40
|
+
"""Fetch metadata for a published dataset.
|
|
41
|
+
|
|
42
|
+
Example::
|
|
43
|
+
|
|
44
|
+
meta = await df.fetch(DataFactsUrl("df://weather"))
|
|
45
|
+
"""
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
async def request_access(self, url: DataFactsUrl, requester: AgentId) -> AccessGrant:
|
|
49
|
+
"""Request access to a dataset.
|
|
50
|
+
|
|
51
|
+
Example::
|
|
52
|
+
|
|
53
|
+
grant = await df.request_access(url, AgentId("a2"))
|
|
54
|
+
"""
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
async def verify_freshness(self, url: DataFactsUrl) -> bool:
|
|
58
|
+
"""Check if a dataset's metadata is still fresh.
|
|
59
|
+
|
|
60
|
+
Example::
|
|
61
|
+
|
|
62
|
+
fresh = await df.verify_freshness(url)
|
|
63
|
+
"""
|
|
64
|
+
...
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Identity layer interface: who is this agent, and how do I verify them?
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyIdentity(Identity):
|
|
7
|
+
def sign(self, payload):
|
|
8
|
+
return Signature(signer=self.agent_id, value=do_sign(payload))
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from nest_core.types import AgentId, AgentIdentity, Signature
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@runtime_checkable
|
|
19
|
+
class Identity(Protocol):
|
|
20
|
+
"""Agent identity, signing, and verification.
|
|
21
|
+
|
|
22
|
+
Example::
|
|
23
|
+
|
|
24
|
+
identity: Identity = DidKeyIdentity(agent_id)
|
|
25
|
+
sig = identity.sign(b"data")
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def sign(self, payload: bytes) -> Signature:
|
|
29
|
+
"""Sign a payload with this agent's private key.
|
|
30
|
+
|
|
31
|
+
Example::
|
|
32
|
+
|
|
33
|
+
sig = identity.sign(b"important data")
|
|
34
|
+
"""
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def verify(self, payload: bytes, sig: Signature, agent: AgentId) -> bool:
|
|
38
|
+
"""Verify a signature from a given agent.
|
|
39
|
+
|
|
40
|
+
Example::
|
|
41
|
+
|
|
42
|
+
ok = identity.verify(b"data", sig, AgentId("a2"))
|
|
43
|
+
"""
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
async def resolve(self, agent: AgentId) -> AgentIdentity:
|
|
47
|
+
"""Resolve an agent ID to its full identity record.
|
|
48
|
+
|
|
49
|
+
Example::
|
|
50
|
+
|
|
51
|
+
info = await identity.resolve(AgentId("a2"))
|
|
52
|
+
"""
|
|
53
|
+
...
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Memory layer interface: shared state between agents.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyMemory(Memory):
|
|
7
|
+
async def read(self, key):
|
|
8
|
+
return self._store.get(key)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from collections.abc import AsyncIterator
|
|
14
|
+
from typing import Protocol, runtime_checkable
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@runtime_checkable
|
|
18
|
+
class Memory(Protocol):
|
|
19
|
+
"""Shared key-value state accessible to agents.
|
|
20
|
+
|
|
21
|
+
Example::
|
|
22
|
+
|
|
23
|
+
mem: Memory = Blackboard()
|
|
24
|
+
await mem.write("counter", b"42")
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
async def read(self, key: str) -> bytes | None:
|
|
28
|
+
"""Read a value by key, returning None if absent.
|
|
29
|
+
|
|
30
|
+
Example::
|
|
31
|
+
|
|
32
|
+
val = await mem.read("counter")
|
|
33
|
+
"""
|
|
34
|
+
...
|
|
35
|
+
|
|
36
|
+
async def write(self, key: str, value: bytes) -> None:
|
|
37
|
+
"""Write a value for a key.
|
|
38
|
+
|
|
39
|
+
Example::
|
|
40
|
+
|
|
41
|
+
await mem.write("counter", b"42")
|
|
42
|
+
"""
|
|
43
|
+
...
|
|
44
|
+
|
|
45
|
+
async def subscribe(self, key: str) -> AsyncIterator[bytes]:
|
|
46
|
+
"""Subscribe to changes for a key.
|
|
47
|
+
|
|
48
|
+
Example::
|
|
49
|
+
|
|
50
|
+
async for val in mem.subscribe("counter"):
|
|
51
|
+
print(val)
|
|
52
|
+
"""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
async def cas(self, key: str, expected: bytes, new: bytes) -> bool:
|
|
56
|
+
"""Compare-and-swap: update only if the current value matches expected.
|
|
57
|
+
|
|
58
|
+
Example::
|
|
59
|
+
|
|
60
|
+
ok = await mem.cas("counter", b"42", b"43")
|
|
61
|
+
"""
|
|
62
|
+
...
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Negotiation layer interface: bargaining between agents over terms.
|
|
3
|
+
|
|
4
|
+
Example::
|
|
5
|
+
|
|
6
|
+
class MyNegotiation(Negotiation):
|
|
7
|
+
async def open(self, partner, terms):
|
|
8
|
+
return NegotiationSession(id="n1", initiator=self._me, partner=partner)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import Protocol, runtime_checkable
|
|
14
|
+
|
|
15
|
+
from nest_core.types import (
|
|
16
|
+
AgentId,
|
|
17
|
+
Agreement,
|
|
18
|
+
NegotiationResponse,
|
|
19
|
+
NegotiationSession,
|
|
20
|
+
Terms,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@runtime_checkable
|
|
25
|
+
class Negotiation(Protocol):
|
|
26
|
+
"""Bargaining protocol between two or more agents.
|
|
27
|
+
|
|
28
|
+
Example::
|
|
29
|
+
|
|
30
|
+
neg: Negotiation = AlternatingOffers(agent_id)
|
|
31
|
+
session = await neg.open(AgentId("a2"), Terms(price=Money(amount=100)))
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
async def open(self, partner: AgentId, terms: Terms) -> NegotiationSession:
|
|
35
|
+
"""Open a negotiation session with initial terms.
|
|
36
|
+
|
|
37
|
+
Example::
|
|
38
|
+
|
|
39
|
+
session = await neg.open(AgentId("a2"), terms)
|
|
40
|
+
"""
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
async def offer(self, session: NegotiationSession, terms: Terms) -> None:
|
|
44
|
+
"""Make an offer in an ongoing negotiation.
|
|
45
|
+
|
|
46
|
+
Example::
|
|
47
|
+
|
|
48
|
+
await neg.offer(session, Terms(price=Money(amount=80)))
|
|
49
|
+
"""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
async def respond(self, session: NegotiationSession) -> NegotiationResponse:
|
|
53
|
+
"""Respond to the latest offer in a negotiation.
|
|
54
|
+
|
|
55
|
+
Example::
|
|
56
|
+
|
|
57
|
+
resp = await neg.respond(session)
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
async def close(self, session: NegotiationSession) -> Agreement | None:
|
|
62
|
+
"""Close a negotiation session, returning an agreement if reached.
|
|
63
|
+
|
|
64
|
+
Example::
|
|
65
|
+
|
|
66
|
+
agreement = await neg.close(session)
|
|
67
|
+
"""
|
|
68
|
+
...
|