koi-net 1.0.0b18__py3-none-any.whl → 1.1.0b1__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.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/config.py +43 -18
- koi_net/context.py +55 -0
- koi_net/core.py +115 -49
- koi_net/default_actions.py +15 -0
- koi_net/effector.py +139 -0
- koi_net/identity.py +4 -22
- koi_net/network/__init__.py +0 -1
- koi_net/network/behavior.py +42 -0
- koi_net/network/error_handler.py +50 -0
- koi_net/network/{interface.py → event_queue.py} +52 -131
- koi_net/network/graph.py +18 -33
- koi_net/network/request_handler.py +112 -66
- koi_net/network/resolver.py +150 -0
- koi_net/network/response_handler.py +11 -3
- koi_net/processor/__init__.py +0 -1
- koi_net/processor/default_handlers.py +57 -41
- koi_net/processor/handler.py +3 -7
- koi_net/processor/interface.py +15 -214
- koi_net/processor/knowledge_object.py +10 -17
- koi_net/processor/knowledge_pipeline.py +220 -0
- koi_net/protocol/api_models.py +7 -1
- koi_net/protocol/edge.py +23 -1
- koi_net/protocol/envelope.py +54 -0
- koi_net/protocol/errors.py +23 -0
- koi_net/protocol/event.py +3 -0
- koi_net/protocol/node.py +2 -1
- koi_net/protocol/secure.py +106 -0
- koi_net/secure.py +117 -0
- {koi_net-1.0.0b18.dist-info → koi_net-1.1.0b1.dist-info}/METADATA +3 -2
- koi_net-1.1.0b1.dist-info/RECORD +35 -0
- koi_net/protocol/helpers.py +0 -25
- koi_net-1.0.0b18.dist-info/RECORD +0 -25
- {koi_net-1.0.0b18.dist-info → koi_net-1.1.0b1.dist-info}/WHEEL +0 -0
- {koi_net-1.0.0b18.dist-info → koi_net-1.1.0b1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import httpx
|
|
3
|
+
from rid_lib import RID
|
|
4
|
+
from rid_lib.core import RIDType
|
|
5
|
+
from rid_lib.ext import Cache, Bundle
|
|
6
|
+
from rid_lib.types import KoiNetNode
|
|
7
|
+
|
|
8
|
+
from .graph import NetworkGraph
|
|
9
|
+
from .request_handler import RequestHandler
|
|
10
|
+
from ..protocol.node import NodeProfile, NodeType
|
|
11
|
+
from ..protocol.event import Event
|
|
12
|
+
from ..protocol.api_models import ErrorResponse
|
|
13
|
+
from ..identity import NodeIdentity
|
|
14
|
+
from ..config import NodeConfig
|
|
15
|
+
from ..effector import Effector
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NetworkResolver:
|
|
21
|
+
"""A collection of functions and classes to interact with the KOI network."""
|
|
22
|
+
|
|
23
|
+
config: NodeConfig
|
|
24
|
+
identity: NodeIdentity
|
|
25
|
+
effector: Effector
|
|
26
|
+
cache: Cache
|
|
27
|
+
graph: NetworkGraph
|
|
28
|
+
request_handler: RequestHandler
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
config: NodeConfig,
|
|
33
|
+
cache: Cache,
|
|
34
|
+
identity: NodeIdentity,
|
|
35
|
+
effector: Effector,
|
|
36
|
+
graph: NetworkGraph,
|
|
37
|
+
request_handler: RequestHandler,
|
|
38
|
+
):
|
|
39
|
+
self.config = config
|
|
40
|
+
self.identity = identity
|
|
41
|
+
self.cache = cache
|
|
42
|
+
self.graph = graph
|
|
43
|
+
self.request_handler = request_handler
|
|
44
|
+
self.effector = effector
|
|
45
|
+
|
|
46
|
+
self.poll_event_queue = dict()
|
|
47
|
+
self.webhook_event_queue = dict()
|
|
48
|
+
|
|
49
|
+
def get_state_providers(self, rid_type: RIDType) -> list[KoiNetNode]:
|
|
50
|
+
"""Returns list of node RIDs which provide state for the specified RID type."""
|
|
51
|
+
|
|
52
|
+
logger.debug(f"Looking for state providers of '{rid_type}'")
|
|
53
|
+
provider_nodes = []
|
|
54
|
+
for node_rid in self.cache.list_rids(rid_types=[KoiNetNode]):
|
|
55
|
+
if node_rid == self.identity.rid:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
node_bundle = self.cache.read(node_rid)
|
|
59
|
+
|
|
60
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
61
|
+
|
|
62
|
+
if (node_profile.node_type == NodeType.FULL) and (rid_type in node_profile.provides.state):
|
|
63
|
+
logger.debug(f"Found provider {node_rid!r}")
|
|
64
|
+
provider_nodes.append(node_rid)
|
|
65
|
+
|
|
66
|
+
if not provider_nodes:
|
|
67
|
+
logger.debug("Failed to find providers")
|
|
68
|
+
return provider_nodes
|
|
69
|
+
|
|
70
|
+
def fetch_remote_bundle(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
71
|
+
"""Attempts to fetch a bundle by RID from known peer nodes."""
|
|
72
|
+
|
|
73
|
+
logger.debug(f"Fetching remote bundle {rid!r}")
|
|
74
|
+
remote_bundle, node_rid = None, None
|
|
75
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
76
|
+
payload = self.request_handler.fetch_bundles(
|
|
77
|
+
node=node_rid, rids=[rid])
|
|
78
|
+
|
|
79
|
+
if payload.bundles:
|
|
80
|
+
remote_bundle = payload.bundles[0]
|
|
81
|
+
logger.debug(f"Got bundle from {node_rid!r}")
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
if not remote_bundle:
|
|
85
|
+
logger.warning("Failed to fetch remote bundle")
|
|
86
|
+
|
|
87
|
+
return remote_bundle, node_rid
|
|
88
|
+
|
|
89
|
+
def fetch_remote_manifest(self, rid: RID) -> tuple[Bundle | None, KoiNetNode | None]:
|
|
90
|
+
"""Attempts to fetch a manifest by RID from known peer nodes."""
|
|
91
|
+
|
|
92
|
+
logger.debug(f"Fetching remote manifest {rid!r}")
|
|
93
|
+
remote_manifest, node_rid = None, None
|
|
94
|
+
for node_rid in self.get_state_providers(type(rid)):
|
|
95
|
+
payload = self.request_handler.fetch_manifests(
|
|
96
|
+
node=node_rid, rids=[rid])
|
|
97
|
+
|
|
98
|
+
if payload.manifests:
|
|
99
|
+
remote_manifest = payload.manifests[0]
|
|
100
|
+
logger.debug(f"Got bundle from {node_rid!r}")
|
|
101
|
+
break
|
|
102
|
+
|
|
103
|
+
if not remote_manifest:
|
|
104
|
+
logger.warning("Failed to fetch remote bundle")
|
|
105
|
+
|
|
106
|
+
return remote_manifest, node_rid
|
|
107
|
+
|
|
108
|
+
def poll_neighbors(self) -> dict[KoiNetNode, list[Event]]:
|
|
109
|
+
"""Polls all neighboring nodes and returns compiled list of events.
|
|
110
|
+
|
|
111
|
+
If this node has no neighbors, it will instead attempt to poll the provided first contact URL.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
graph_neighbors = self.graph.get_neighbors()
|
|
115
|
+
neighbors = []
|
|
116
|
+
|
|
117
|
+
if graph_neighbors:
|
|
118
|
+
for node_rid in graph_neighbors:
|
|
119
|
+
node_bundle = self.cache.read(node_rid)
|
|
120
|
+
if not node_bundle:
|
|
121
|
+
continue
|
|
122
|
+
node_profile = node_bundle.validate_contents(NodeProfile)
|
|
123
|
+
if node_profile.node_type != NodeType.FULL:
|
|
124
|
+
continue
|
|
125
|
+
neighbors.append(node_rid)
|
|
126
|
+
|
|
127
|
+
elif self.config.koi_net.first_contact.rid:
|
|
128
|
+
neighbors.append(self.config.koi_net.first_contact.rid)
|
|
129
|
+
|
|
130
|
+
event_dict = dict()
|
|
131
|
+
for node_rid in neighbors:
|
|
132
|
+
try:
|
|
133
|
+
payload = self.request_handler.poll_events(
|
|
134
|
+
node=node_rid,
|
|
135
|
+
rid=self.identity.rid
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if type(payload) == ErrorResponse:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
if payload.events:
|
|
142
|
+
logger.debug(f"Received {len(payload.events)} events from {node_rid!r}")
|
|
143
|
+
|
|
144
|
+
event_dict[node_rid] = payload.events
|
|
145
|
+
|
|
146
|
+
except httpx.ConnectError:
|
|
147
|
+
logger.debug(f"Failed to reach node {node_rid!r}")
|
|
148
|
+
continue
|
|
149
|
+
|
|
150
|
+
return event_dict
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
from rid_lib import RID
|
|
3
3
|
from rid_lib.ext import Manifest, Cache
|
|
4
4
|
from rid_lib.ext.bundle import Bundle
|
|
5
|
+
|
|
5
6
|
from ..protocol.api_models import (
|
|
6
7
|
RidsPayload,
|
|
7
8
|
ManifestsPayload,
|
|
@@ -10,6 +11,7 @@ from ..protocol.api_models import (
|
|
|
10
11
|
FetchManifests,
|
|
11
12
|
FetchBundles,
|
|
12
13
|
)
|
|
14
|
+
from ..effector import Effector
|
|
13
15
|
|
|
14
16
|
logger = logging.getLogger(__name__)
|
|
15
17
|
|
|
@@ -18,9 +20,15 @@ class ResponseHandler:
|
|
|
18
20
|
"""Handles generating responses to requests from other KOI nodes."""
|
|
19
21
|
|
|
20
22
|
cache: Cache
|
|
23
|
+
effector: Effector
|
|
21
24
|
|
|
22
|
-
def __init__(
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
cache: Cache,
|
|
28
|
+
effector: Effector,
|
|
29
|
+
):
|
|
23
30
|
self.cache = cache
|
|
31
|
+
self.effector = effector
|
|
24
32
|
|
|
25
33
|
def fetch_rids(self, req: FetchRids) -> RidsPayload:
|
|
26
34
|
logger.info(f"Request to fetch rids, allowed types {req.rid_types}")
|
|
@@ -35,7 +43,7 @@ class ResponseHandler:
|
|
|
35
43
|
not_found: list[RID] = []
|
|
36
44
|
|
|
37
45
|
for rid in (req.rids or self.cache.list_rids(req.rid_types)):
|
|
38
|
-
bundle = self.
|
|
46
|
+
bundle = self.effector.deref(rid)
|
|
39
47
|
if bundle:
|
|
40
48
|
manifests.append(bundle.manifest)
|
|
41
49
|
else:
|
|
@@ -50,7 +58,7 @@ class ResponseHandler:
|
|
|
50
58
|
not_found: list[RID] = []
|
|
51
59
|
|
|
52
60
|
for rid in req.rids:
|
|
53
|
-
bundle = self.
|
|
61
|
+
bundle = self.effector.deref(rid)
|
|
54
62
|
if bundle:
|
|
55
63
|
bundles.append(bundle)
|
|
56
64
|
else:
|
koi_net/processor/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .interface import ProcessorInterface
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
"""Provides implementations of default knowledge handlers."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from rid_lib.ext
|
|
4
|
+
from rid_lib.ext import Bundle
|
|
5
|
+
from rid_lib.ext.utils import sha256_hash
|
|
5
6
|
from rid_lib.types import KoiNetNode, KoiNetEdge
|
|
6
7
|
from koi_net.protocol.node import NodeType
|
|
7
|
-
from .interface import ProcessorInterface
|
|
8
8
|
from .handler import KnowledgeHandler, HandlerType, STOP_CHAIN
|
|
9
|
-
from .knowledge_object import KnowledgeObject
|
|
9
|
+
from .knowledge_object import KnowledgeObject
|
|
10
|
+
from ..context import HandlerContext
|
|
10
11
|
from ..protocol.event import Event, EventType
|
|
11
|
-
from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType
|
|
12
|
+
from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType, generate_edge_bundle
|
|
12
13
|
from ..protocol.node import NodeProfile
|
|
13
|
-
from ..protocol.helpers import generate_edge_bundle
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
17
17
|
# RID handlers
|
|
18
18
|
|
|
19
19
|
@KnowledgeHandler.create(HandlerType.RID)
|
|
20
|
-
def basic_rid_handler(
|
|
20
|
+
def basic_rid_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
21
21
|
"""Default RID handler.
|
|
22
22
|
|
|
23
23
|
Blocks external events about this node. Allows `FORGET` events if RID is known to this node.
|
|
24
24
|
"""
|
|
25
|
-
if (kobj.rid ==
|
|
26
|
-
kobj.source == KnowledgeSource.External):
|
|
25
|
+
if (kobj.rid == ctx.identity.rid and kobj.source):
|
|
27
26
|
logger.debug("Don't let anyone else tell me who I am!")
|
|
28
27
|
return STOP_CHAIN
|
|
29
28
|
|
|
@@ -34,12 +33,12 @@ def basic_rid_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
|
|
|
34
33
|
# Manifest handlers
|
|
35
34
|
|
|
36
35
|
@KnowledgeHandler.create(HandlerType.Manifest)
|
|
37
|
-
def basic_manifest_handler(
|
|
36
|
+
def basic_manifest_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
38
37
|
"""Default manifest handler.
|
|
39
38
|
|
|
40
39
|
Blocks manifests with the same hash, or aren't newer than the cached version. Sets the normalized event type to `NEW` or `UPDATE` depending on whether the RID was previously known to this node.
|
|
41
40
|
"""
|
|
42
|
-
prev_bundle =
|
|
41
|
+
prev_bundle = ctx.cache.read(kobj.rid)
|
|
43
42
|
|
|
44
43
|
if prev_bundle:
|
|
45
44
|
if kobj.manifest.sha256_hash == prev_bundle.manifest.sha256_hash:
|
|
@@ -61,36 +60,53 @@ def basic_manifest_handler(processor: ProcessorInterface, kobj: KnowledgeObject)
|
|
|
61
60
|
|
|
62
61
|
# Bundle handlers
|
|
63
62
|
|
|
63
|
+
@KnowledgeHandler.create(
|
|
64
|
+
handler_type=HandlerType.Bundle,
|
|
65
|
+
rid_types=[KoiNetNode],
|
|
66
|
+
event_types=[EventType.NEW, EventType.UPDATE]
|
|
67
|
+
)
|
|
68
|
+
def secure_profile_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
69
|
+
node_profile = kobj.bundle.validate_contents(NodeProfile)
|
|
70
|
+
node_rid: KoiNetNode = kobj.rid
|
|
71
|
+
|
|
72
|
+
if sha256_hash(node_profile.public_key) != node_rid.hash:
|
|
73
|
+
logger.warning(f"Public key hash mismatch for {node_rid!r}!")
|
|
74
|
+
return STOP_CHAIN
|
|
75
|
+
|
|
64
76
|
@KnowledgeHandler.create(
|
|
65
77
|
handler_type=HandlerType.Bundle,
|
|
66
78
|
rid_types=[KoiNetEdge],
|
|
67
|
-
source=KnowledgeSource.External,
|
|
68
79
|
event_types=[EventType.NEW, EventType.UPDATE])
|
|
69
|
-
def edge_negotiation_handler(
|
|
80
|
+
def edge_negotiation_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
70
81
|
"""Handles basic edge negotiation process.
|
|
71
82
|
|
|
72
83
|
Automatically approves proposed edges if they request RID types this node can provide (or KOI nodes/edges). Validates the edge type is allowed for the node type (partial nodes cannot use webhooks). If edge is invalid, a `FORGET` event is sent to the other node.
|
|
73
84
|
"""
|
|
85
|
+
|
|
86
|
+
# only respond when source is another node
|
|
87
|
+
if kobj.source is None: return
|
|
74
88
|
|
|
75
|
-
edge_profile =
|
|
89
|
+
edge_profile = kobj.bundle.validate_contents(EdgeProfile)
|
|
76
90
|
|
|
77
91
|
# indicates peer subscribing to me
|
|
78
|
-
if edge_profile.source ==
|
|
92
|
+
if edge_profile.source == ctx.identity.rid:
|
|
79
93
|
if edge_profile.status != EdgeStatus.PROPOSED:
|
|
80
94
|
return
|
|
81
95
|
|
|
82
96
|
logger.debug("Handling edge negotiation")
|
|
83
97
|
|
|
84
98
|
peer_rid = edge_profile.target
|
|
85
|
-
|
|
99
|
+
peer_bundle = ctx.effector.deref(peer_rid)
|
|
86
100
|
|
|
87
|
-
if not
|
|
88
|
-
logger.warning(f"Peer {peer_rid} unknown to me")
|
|
101
|
+
if not peer_bundle:
|
|
102
|
+
logger.warning(f"Peer {peer_rid!r} unknown to me")
|
|
89
103
|
return STOP_CHAIN
|
|
90
104
|
|
|
105
|
+
peer_profile = peer_bundle.validate_contents(NodeProfile)
|
|
106
|
+
|
|
91
107
|
# explicitly provided event RID types and (self) node + edge objects
|
|
92
108
|
provided_events = (
|
|
93
|
-
*
|
|
109
|
+
*ctx.identity.profile.provides.event,
|
|
94
110
|
KoiNetNode, KoiNetEdge
|
|
95
111
|
)
|
|
96
112
|
|
|
@@ -107,7 +123,7 @@ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObjec
|
|
|
107
123
|
|
|
108
124
|
if abort:
|
|
109
125
|
event = Event.from_rid(EventType.FORGET, kobj.rid)
|
|
110
|
-
|
|
126
|
+
ctx.event_queue.push_event_to(event, peer_rid, flush=True)
|
|
111
127
|
return STOP_CHAIN
|
|
112
128
|
|
|
113
129
|
else:
|
|
@@ -116,10 +132,10 @@ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObjec
|
|
|
116
132
|
edge_profile.status = EdgeStatus.APPROVED
|
|
117
133
|
updated_bundle = Bundle.generate(kobj.rid, edge_profile.model_dump())
|
|
118
134
|
|
|
119
|
-
|
|
135
|
+
ctx.handle(bundle=updated_bundle, event_type=EventType.UPDATE)
|
|
120
136
|
return
|
|
121
137
|
|
|
122
|
-
elif edge_profile.target ==
|
|
138
|
+
elif edge_profile.target == ctx.identity.rid:
|
|
123
139
|
if edge_profile.status == EdgeStatus.APPROVED:
|
|
124
140
|
logger.debug("Edge approved by other node!")
|
|
125
141
|
|
|
@@ -127,88 +143,88 @@ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObjec
|
|
|
127
143
|
# Network handlers
|
|
128
144
|
|
|
129
145
|
@KnowledgeHandler.create(HandlerType.Network, rid_types=[KoiNetNode])
|
|
130
|
-
def coordinator_contact(
|
|
146
|
+
def coordinator_contact(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
131
147
|
node_profile = kobj.bundle.validate_contents(NodeProfile)
|
|
132
|
-
|
|
148
|
+
|
|
133
149
|
# looking for event provider of nodes
|
|
134
150
|
if KoiNetNode not in node_profile.provides.event:
|
|
135
151
|
return
|
|
136
152
|
|
|
137
153
|
# prevents coordinators from attempting to form a self loop
|
|
138
|
-
if kobj.rid ==
|
|
154
|
+
if kobj.rid == ctx.identity.rid:
|
|
139
155
|
return
|
|
140
156
|
|
|
141
157
|
# already have an edge established
|
|
142
|
-
if
|
|
158
|
+
if ctx.graph.get_edge(
|
|
143
159
|
source=kobj.rid,
|
|
144
|
-
target=
|
|
160
|
+
target=ctx.identity.rid,
|
|
145
161
|
) is not None:
|
|
146
162
|
return
|
|
147
163
|
|
|
148
164
|
logger.info("Identified a coordinator!")
|
|
149
165
|
logger.info("Proposing new edge")
|
|
150
166
|
|
|
151
|
-
if
|
|
167
|
+
if ctx.identity.profile.node_type == NodeType.FULL:
|
|
152
168
|
edge_type = EdgeType.WEBHOOK
|
|
153
169
|
else:
|
|
154
170
|
edge_type = EdgeType.POLL
|
|
155
171
|
|
|
156
172
|
# queued for processing
|
|
157
|
-
|
|
173
|
+
ctx.handle(bundle=generate_edge_bundle(
|
|
158
174
|
source=kobj.rid,
|
|
159
|
-
target=
|
|
175
|
+
target=ctx.identity.rid,
|
|
160
176
|
edge_type=edge_type,
|
|
161
177
|
rid_types=[KoiNetNode]
|
|
162
178
|
))
|
|
163
179
|
|
|
164
180
|
logger.info("Catching up on network state")
|
|
165
181
|
|
|
166
|
-
payload =
|
|
182
|
+
payload = ctx.request_handler.fetch_rids(
|
|
167
183
|
node=kobj.rid,
|
|
168
184
|
rid_types=[KoiNetNode]
|
|
169
185
|
)
|
|
170
186
|
for rid in payload.rids:
|
|
171
|
-
if rid ==
|
|
187
|
+
if rid == ctx.identity.rid:
|
|
172
188
|
logger.info("Skipping myself")
|
|
173
189
|
continue
|
|
174
|
-
if
|
|
175
|
-
logger.info(f"Skipping known RID
|
|
190
|
+
if ctx.cache.exists(rid):
|
|
191
|
+
logger.info(f"Skipping known RID {rid!r}")
|
|
176
192
|
continue
|
|
177
193
|
|
|
178
194
|
# marked as external since we are handling RIDs from another node
|
|
179
195
|
# will fetch remotely instead of checking local cache
|
|
180
|
-
|
|
196
|
+
ctx.handle(rid=rid, source=kobj.rid)
|
|
181
197
|
logger.info("Done")
|
|
182
198
|
|
|
183
199
|
|
|
184
200
|
@KnowledgeHandler.create(HandlerType.Network)
|
|
185
|
-
def basic_network_output_filter(
|
|
201
|
+
def basic_network_output_filter(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
186
202
|
"""Default network handler.
|
|
187
203
|
|
|
188
204
|
Allows broadcasting of all RID types this node is an event provider for (set in node profile), and other nodes have subscribed to. All nodes will also broadcast about their own (internally sourced) KOI node, and KOI edges that they are part of, regardless of their node profile configuration. Finally, nodes will also broadcast about edges to the other node involved (regardless of if they are subscribed)."""
|
|
189
205
|
|
|
190
206
|
involves_me = False
|
|
191
|
-
if kobj.source
|
|
207
|
+
if kobj.source is None:
|
|
192
208
|
if (type(kobj.rid) == KoiNetNode):
|
|
193
|
-
if (kobj.rid ==
|
|
209
|
+
if (kobj.rid == ctx.identity.rid):
|
|
194
210
|
involves_me = True
|
|
195
211
|
|
|
196
212
|
elif type(kobj.rid) == KoiNetEdge:
|
|
197
213
|
edge_profile = kobj.bundle.validate_contents(EdgeProfile)
|
|
198
214
|
|
|
199
|
-
if edge_profile.source ==
|
|
215
|
+
if edge_profile.source == ctx.identity.rid:
|
|
200
216
|
logger.debug(f"Adding edge target '{edge_profile.target!r}' to network targets")
|
|
201
217
|
kobj.network_targets.update([edge_profile.target])
|
|
202
218
|
involves_me = True
|
|
203
219
|
|
|
204
|
-
elif edge_profile.target ==
|
|
220
|
+
elif edge_profile.target == ctx.identity.rid:
|
|
205
221
|
logger.debug(f"Adding edge source '{edge_profile.source!r}' to network targets")
|
|
206
222
|
kobj.network_targets.update([edge_profile.source])
|
|
207
223
|
involves_me = True
|
|
208
224
|
|
|
209
|
-
if (type(kobj.rid) in
|
|
225
|
+
if (type(kobj.rid) in ctx.identity.profile.provides.event or involves_me):
|
|
210
226
|
# broadcasts to subscribers if I'm an event provider of this RID type OR it involves me
|
|
211
|
-
subscribers =
|
|
227
|
+
subscribers = ctx.graph.get_neighbors(
|
|
212
228
|
direction="out",
|
|
213
229
|
allowed_type=type(kobj.rid)
|
|
214
230
|
)
|
koi_net/processor/handler.py
CHANGED
|
@@ -2,9 +2,7 @@ from dataclasses import dataclass
|
|
|
2
2
|
from enum import StrEnum
|
|
3
3
|
from typing import Callable
|
|
4
4
|
from rid_lib import RIDType
|
|
5
|
-
|
|
6
5
|
from ..protocol.event import EventType
|
|
7
|
-
from .knowledge_object import KnowledgeSource, KnowledgeEventType
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class StopChain:
|
|
@@ -37,23 +35,21 @@ class KnowledgeHandler:
|
|
|
37
35
|
func: Callable
|
|
38
36
|
handler_type: HandlerType
|
|
39
37
|
rid_types: list[RIDType] | None
|
|
40
|
-
|
|
41
|
-
event_types: list[KnowledgeEventType] | None = None
|
|
38
|
+
event_types: list[EventType | None] | None = None
|
|
42
39
|
|
|
43
40
|
@classmethod
|
|
44
41
|
def create(
|
|
45
42
|
cls,
|
|
46
43
|
handler_type: HandlerType,
|
|
47
44
|
rid_types: list[RIDType] | None = None,
|
|
48
|
-
|
|
49
|
-
event_types: list[KnowledgeEventType] | None = None
|
|
45
|
+
event_types: list[EventType | None] | None = None
|
|
50
46
|
):
|
|
51
47
|
"""Special decorator that returns a KnowledgeHandler instead of a function.
|
|
52
48
|
|
|
53
49
|
The function symbol will redefined as a `KnowledgeHandler`, which can be passed into the `ProcessorInterface` constructor. This is used to register default handlers.
|
|
54
50
|
"""
|
|
55
51
|
def decorator(func: Callable) -> KnowledgeHandler:
|
|
56
|
-
handler = cls(func, handler_type, rid_types,
|
|
52
|
+
handler = cls(func, handler_type, rid_types, event_types)
|
|
57
53
|
return handler
|
|
58
54
|
return decorator
|
|
59
55
|
|