koi-net 1.2.4__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/__init__.py +1 -0
- koi_net/behaviors/handshaker.py +68 -0
- koi_net/behaviors/profile_monitor.py +23 -0
- koi_net/behaviors/sync_manager.py +68 -0
- koi_net/build/artifact.py +209 -0
- koi_net/build/assembler.py +60 -0
- koi_net/build/comp_order.py +6 -0
- koi_net/build/comp_type.py +7 -0
- koi_net/build/consts.py +18 -0
- koi_net/build/container.py +46 -0
- koi_net/cache.py +81 -0
- koi_net/config/core.py +113 -0
- koi_net/config/full_node.py +45 -0
- koi_net/config/loader.py +60 -0
- koi_net/config/partial_node.py +26 -0
- koi_net/config/proxy.py +20 -0
- koi_net/core.py +78 -0
- koi_net/effector.py +147 -0
- koi_net/entrypoints/__init__.py +2 -0
- koi_net/entrypoints/base.py +8 -0
- koi_net/entrypoints/poller.py +43 -0
- koi_net/entrypoints/server.py +85 -0
- koi_net/exceptions.py +107 -0
- koi_net/identity.py +20 -0
- koi_net/log_system.py +133 -0
- koi_net/network/__init__.py +0 -0
- koi_net/network/error_handler.py +63 -0
- koi_net/network/event_buffer.py +91 -0
- koi_net/network/event_queue.py +31 -0
- koi_net/network/graph.py +123 -0
- koi_net/network/request_handler.py +244 -0
- koi_net/network/resolver.py +152 -0
- koi_net/network/response_handler.py +130 -0
- koi_net/processor/__init__.py +0 -0
- koi_net/processor/context.py +36 -0
- koi_net/processor/handler.py +61 -0
- koi_net/processor/knowledge_handlers.py +302 -0
- koi_net/processor/knowledge_object.py +135 -0
- koi_net/processor/kobj_queue.py +51 -0
- koi_net/processor/pipeline.py +222 -0
- koi_net/protocol/__init__.py +0 -0
- koi_net/protocol/api_models.py +67 -0
- koi_net/protocol/consts.py +7 -0
- koi_net/protocol/edge.py +50 -0
- koi_net/protocol/envelope.py +65 -0
- koi_net/protocol/errors.py +24 -0
- koi_net/protocol/event.py +51 -0
- koi_net/protocol/model_map.py +62 -0
- koi_net/protocol/node.py +18 -0
- koi_net/protocol/secure.py +167 -0
- koi_net/secure_manager.py +115 -0
- koi_net/workers/__init__.py +2 -0
- koi_net/workers/base.py +26 -0
- koi_net/workers/event_worker.py +111 -0
- koi_net/workers/kobj_worker.py +51 -0
- koi_net-1.2.4.dist-info/METADATA +485 -0
- koi_net-1.2.4.dist-info/RECORD +59 -0
- koi_net-1.2.4.dist-info/WHEEL +4 -0
- koi_net-1.2.4.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Implementation of default knowledge handlers."""
|
|
2
|
+
|
|
3
|
+
import structlog
|
|
4
|
+
from rid_lib.ext import Bundle
|
|
5
|
+
from rid_lib.ext.utils import sha256_hash
|
|
6
|
+
from rid_lib.types import KoiNetNode, KoiNetEdge
|
|
7
|
+
|
|
8
|
+
from ..exceptions import RequestError
|
|
9
|
+
from ..protocol.node import NodeType
|
|
10
|
+
from ..protocol.event import Event, EventType
|
|
11
|
+
from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType, generate_edge_bundle
|
|
12
|
+
from ..protocol.node import NodeProfile
|
|
13
|
+
from .handler import KnowledgeHandler, HandlerType, STOP_CHAIN
|
|
14
|
+
from .knowledge_object import KnowledgeObject
|
|
15
|
+
from .context import HandlerContext
|
|
16
|
+
|
|
17
|
+
log = structlog.stdlib.get_logger()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# RID handlers
|
|
21
|
+
|
|
22
|
+
@KnowledgeHandler.create(HandlerType.RID)
|
|
23
|
+
def basic_rid_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
24
|
+
"""Default RID handler.
|
|
25
|
+
|
|
26
|
+
Blocks external events about this node. Sets normalized event type
|
|
27
|
+
for `FORGET` events.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
if (kobj.rid == ctx.identity.rid and kobj.source is not None):
|
|
31
|
+
log.debug("Externally sourced events about this node not allowed.")
|
|
32
|
+
return STOP_CHAIN
|
|
33
|
+
|
|
34
|
+
if kobj.event_type == EventType.FORGET:
|
|
35
|
+
kobj.normalized_event_type = EventType.FORGET
|
|
36
|
+
return kobj
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Manifest handlers
|
|
40
|
+
|
|
41
|
+
@KnowledgeHandler.create(HandlerType.Manifest)
|
|
42
|
+
def basic_manifest_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
43
|
+
"""Normalized event decider based on manifest and cache state.
|
|
44
|
+
|
|
45
|
+
Stops processing for manifests which have the same hash, or aren't
|
|
46
|
+
newer than the cached version. Sets the normalized event type to
|
|
47
|
+
`NEW` or `UPDATE` depending on whether the RID was previously known.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
prev_bundle = ctx.cache.read(kobj.rid)
|
|
51
|
+
|
|
52
|
+
if prev_bundle:
|
|
53
|
+
if kobj.manifest.sha256_hash == prev_bundle.manifest.sha256_hash:
|
|
54
|
+
log.debug("Hash of incoming manifest is same as existing knowledge, ignoring")
|
|
55
|
+
return STOP_CHAIN
|
|
56
|
+
if kobj.manifest.timestamp <= prev_bundle.manifest.timestamp:
|
|
57
|
+
log.debug("Timestamp of incoming manifest is the same or older than existing knowledge, ignoring")
|
|
58
|
+
return STOP_CHAIN
|
|
59
|
+
|
|
60
|
+
log.debug("RID previously known to me, labeling as 'UPDATE'")
|
|
61
|
+
kobj.normalized_event_type = EventType.UPDATE
|
|
62
|
+
|
|
63
|
+
else:
|
|
64
|
+
log.debug("RID previously unknown to me, labeling as 'NEW'")
|
|
65
|
+
kobj.normalized_event_type = EventType.NEW
|
|
66
|
+
|
|
67
|
+
return kobj
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Bundle handlers
|
|
71
|
+
|
|
72
|
+
@KnowledgeHandler.create(
|
|
73
|
+
handler_type=HandlerType.Bundle,
|
|
74
|
+
rid_types=(KoiNetNode,),
|
|
75
|
+
event_types=(EventType.NEW, EventType.UPDATE))
|
|
76
|
+
def secure_profile_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
77
|
+
"""Maintains security of cached node profiles.
|
|
78
|
+
|
|
79
|
+
Blocks bundles with a mismatching public keys in their node profile
|
|
80
|
+
and RID from being written to cache.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
node_profile = kobj.bundle.validate_contents(NodeProfile)
|
|
84
|
+
node_rid: KoiNetNode = kobj.rid
|
|
85
|
+
|
|
86
|
+
if sha256_hash(node_profile.public_key) != node_rid.hash:
|
|
87
|
+
log.warning(f"Public key hash mismatch for {node_rid!r}!")
|
|
88
|
+
return STOP_CHAIN
|
|
89
|
+
|
|
90
|
+
@KnowledgeHandler.create(
|
|
91
|
+
handler_type=HandlerType.Bundle,
|
|
92
|
+
rid_types=(KoiNetEdge,),
|
|
93
|
+
event_types=(EventType.NEW, EventType.UPDATE))
|
|
94
|
+
def edge_negotiation_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
95
|
+
"""Handles edge negotiation process.
|
|
96
|
+
|
|
97
|
+
Automatically approves proposed edges if they request RID types this
|
|
98
|
+
node can provide (or KOI node, edge RIDs). Validates the edge type
|
|
99
|
+
is allowed for the node type (partial nodes cannot use webhooks). If
|
|
100
|
+
edge is invalid, a `FORGET` event is sent to the other node.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
# only handle incoming events (ignore internal edge knowledge objects)
|
|
104
|
+
if kobj.source is None:
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
edge_profile = kobj.bundle.validate_contents(EdgeProfile)
|
|
108
|
+
|
|
109
|
+
# indicates peer subscribing to this node
|
|
110
|
+
if edge_profile.source == ctx.identity.rid:
|
|
111
|
+
if edge_profile.status != EdgeStatus.PROPOSED:
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
log.debug("Handling edge negotiation")
|
|
115
|
+
|
|
116
|
+
peer_rid = edge_profile.target
|
|
117
|
+
peer_bundle = ctx.cache.read(peer_rid)
|
|
118
|
+
|
|
119
|
+
if not peer_bundle:
|
|
120
|
+
log.warning(f"Peer {peer_rid!r} unknown to me")
|
|
121
|
+
return STOP_CHAIN
|
|
122
|
+
|
|
123
|
+
peer_profile = peer_bundle.validate_contents(NodeProfile)
|
|
124
|
+
|
|
125
|
+
# explicitly provided event RID types and (self) node + edge objects
|
|
126
|
+
provided_events = (
|
|
127
|
+
*ctx.identity.profile.provides.event,
|
|
128
|
+
KoiNetNode, KoiNetEdge
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
abort = False
|
|
132
|
+
if (edge_profile.edge_type == EdgeType.WEBHOOK and
|
|
133
|
+
peer_profile.node_type == NodeType.PARTIAL):
|
|
134
|
+
log.debug("Partial nodes cannot use webhooks")
|
|
135
|
+
abort = True
|
|
136
|
+
|
|
137
|
+
if not set(edge_profile.rid_types).issubset(provided_events):
|
|
138
|
+
log.debug("Requested RID types not provided by this node")
|
|
139
|
+
abort = True
|
|
140
|
+
|
|
141
|
+
if abort:
|
|
142
|
+
event = Event.from_rid(EventType.FORGET, kobj.rid)
|
|
143
|
+
ctx.event_queue.push(event, peer_rid, flush=True)
|
|
144
|
+
return STOP_CHAIN
|
|
145
|
+
|
|
146
|
+
else:
|
|
147
|
+
log.debug("Approving proposed edge")
|
|
148
|
+
edge_profile.status = EdgeStatus.APPROVED
|
|
149
|
+
updated_bundle = Bundle.generate(kobj.rid, edge_profile.model_dump())
|
|
150
|
+
|
|
151
|
+
ctx.kobj_queue.push(bundle=updated_bundle, event_type=EventType.UPDATE)
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
elif edge_profile.target == ctx.identity.rid:
|
|
155
|
+
if edge_profile.status == EdgeStatus.APPROVED:
|
|
156
|
+
log.debug("Edge approved by other node!")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# Network handlers
|
|
160
|
+
|
|
161
|
+
@KnowledgeHandler.create(HandlerType.Network, rid_types=(KoiNetNode,))
|
|
162
|
+
def node_contact_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
163
|
+
"""Makes contact with providers of RID types of interest.
|
|
164
|
+
|
|
165
|
+
When an incoming node knowledge object is identified as a provider
|
|
166
|
+
of an RID type of interest, this handler will propose a new edge
|
|
167
|
+
subscribing to future node events, and fetch existing nodes to catch
|
|
168
|
+
up to the current state.
|
|
169
|
+
"""
|
|
170
|
+
# prevents nodes from attempting to form a self loop
|
|
171
|
+
if kobj.rid == ctx.identity.rid:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
node_profile = kobj.bundle.validate_contents(NodeProfile)
|
|
175
|
+
|
|
176
|
+
available_rid_types = list(
|
|
177
|
+
set(ctx.config.koi_net.rid_types_of_interest) &
|
|
178
|
+
set(node_profile.provides.event)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if not available_rid_types:
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
edge_rid = ctx.graph.get_edge(
|
|
185
|
+
source=kobj.rid,
|
|
186
|
+
target=ctx.identity.rid,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# already have an edge established
|
|
190
|
+
if edge_rid:
|
|
191
|
+
prev_edge_bundle = ctx.cache.read(edge_rid)
|
|
192
|
+
edge_profile = prev_edge_bundle.validate_contents(EdgeProfile)
|
|
193
|
+
|
|
194
|
+
if set(edge_profile.rid_types) == set(available_rid_types):
|
|
195
|
+
# no change in rid types
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
log.info(f"Proposing updated edge with node provider {available_rid_types}")
|
|
199
|
+
|
|
200
|
+
edge_profile.rid_types = available_rid_types
|
|
201
|
+
edge_profile.status = EdgeStatus.PROPOSED
|
|
202
|
+
edge_bundle = Bundle.generate(edge_rid, edge_profile.model_dump())
|
|
203
|
+
|
|
204
|
+
# no existing edge
|
|
205
|
+
else:
|
|
206
|
+
log.info(f"Proposing new edge with node provider {available_rid_types}")
|
|
207
|
+
edge_bundle = generate_edge_bundle(
|
|
208
|
+
source=kobj.rid,
|
|
209
|
+
target=ctx.identity.rid,
|
|
210
|
+
rid_types=available_rid_types,
|
|
211
|
+
edge_type=(
|
|
212
|
+
EdgeType.WEBHOOK
|
|
213
|
+
if ctx.identity.profile.node_type == NodeType.FULL
|
|
214
|
+
else EdgeType.POLL
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# queued for processing
|
|
219
|
+
ctx.kobj_queue.push(bundle=edge_bundle)
|
|
220
|
+
|
|
221
|
+
log.info("Catching up on network state")
|
|
222
|
+
try:
|
|
223
|
+
payload = ctx.request_handler.fetch_rids(
|
|
224
|
+
node=kobj.rid,
|
|
225
|
+
rid_types=available_rid_types
|
|
226
|
+
)
|
|
227
|
+
except RequestError:
|
|
228
|
+
log.info("Failed to reach node")
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
for rid in payload.rids:
|
|
232
|
+
if rid == ctx.identity.rid:
|
|
233
|
+
log.info("Skipping myself")
|
|
234
|
+
continue
|
|
235
|
+
if ctx.cache.exists(rid):
|
|
236
|
+
log.info(f"Skipping known RID {rid!r}")
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# marked as external since we are handling RIDs from another node
|
|
240
|
+
# will fetch remotely instead of checking local cache
|
|
241
|
+
ctx.kobj_queue.push(rid=rid, source=kobj.rid)
|
|
242
|
+
log.info("Done")
|
|
243
|
+
|
|
244
|
+
@KnowledgeHandler.create(HandlerType.Network)
|
|
245
|
+
def basic_network_output_filter(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
246
|
+
"""Sets network targets of outgoing event for knowledge object.
|
|
247
|
+
|
|
248
|
+
Allows broadcasting of all RID types this node is an event provider
|
|
249
|
+
for (set in node profile), and other nodes have subscribed to. All
|
|
250
|
+
nodes will also broadcast events about their own (internally sourced)
|
|
251
|
+
KOI node, and KOI edges that they are part of, regardless of their
|
|
252
|
+
node profile configuration. Finally, nodes will also broadcast about
|
|
253
|
+
edges to the other node involved (regardless of if they are subscribed).
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
involves_this_node = False
|
|
257
|
+
# internally source knowledge objects
|
|
258
|
+
if kobj.source is None:
|
|
259
|
+
if type(kobj.rid) is KoiNetNode:
|
|
260
|
+
if (kobj.rid == ctx.identity.rid):
|
|
261
|
+
involves_this_node = True
|
|
262
|
+
|
|
263
|
+
elif type(kobj.rid) is KoiNetEdge:
|
|
264
|
+
edge_profile = kobj.bundle.validate_contents(EdgeProfile)
|
|
265
|
+
|
|
266
|
+
if edge_profile.source == ctx.identity.rid:
|
|
267
|
+
log.debug(f"Adding edge target '{edge_profile.target!r}' to network targets")
|
|
268
|
+
kobj.network_targets.add(edge_profile.target)
|
|
269
|
+
involves_this_node = True
|
|
270
|
+
|
|
271
|
+
elif edge_profile.target == ctx.identity.rid:
|
|
272
|
+
log.debug(f"Adding edge source '{edge_profile.source!r}' to network targets")
|
|
273
|
+
kobj.network_targets.add(edge_profile.source)
|
|
274
|
+
involves_this_node = True
|
|
275
|
+
|
|
276
|
+
if (type(kobj.rid) in ctx.identity.profile.provides.event or involves_this_node):
|
|
277
|
+
subscribers = ctx.graph.get_neighbors(
|
|
278
|
+
direction="out",
|
|
279
|
+
allowed_type=type(kobj.rid)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
log.debug(f"Updating network targets with '{type(kobj.rid)}' subscribers: {subscribers}")
|
|
283
|
+
kobj.network_targets.update(subscribers)
|
|
284
|
+
|
|
285
|
+
return kobj
|
|
286
|
+
|
|
287
|
+
@KnowledgeHandler.create(HandlerType.Final, rid_types=[KoiNetNode])
|
|
288
|
+
def forget_edge_on_node_deletion(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
289
|
+
"""Removes edges to forgotten nodes."""
|
|
290
|
+
|
|
291
|
+
if kobj.normalized_event_type != EventType.FORGET:
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
for edge_rid in ctx.graph.get_edges():
|
|
295
|
+
edge_bundle = ctx.cache.read(edge_rid)
|
|
296
|
+
if not edge_bundle:
|
|
297
|
+
continue
|
|
298
|
+
edge_profile = edge_bundle.validate_contents(EdgeProfile)
|
|
299
|
+
|
|
300
|
+
if kobj.rid in (edge_profile.source, edge_profile.target):
|
|
301
|
+
log.debug("Identified edge with forgotten node")
|
|
302
|
+
ctx.kobj_queue.push(rid=edge_rid, event_type=EventType.FORGET)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from rid_lib import RID
|
|
3
|
+
from rid_lib.ext import Manifest, Bundle
|
|
4
|
+
from rid_lib.types import KoiNetNode
|
|
5
|
+
from ..protocol.event import Event, EventType
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KnowledgeObject(BaseModel):
|
|
9
|
+
"""A normalized knowledge representation for internal processing.
|
|
10
|
+
|
|
11
|
+
Represents an RID, manifest, bundle, or event. Contains three fields
|
|
12
|
+
(`normalized_event_type`, `source`, `network_targets`) used for
|
|
13
|
+
decision making in the knowledge processing pipeline.
|
|
14
|
+
|
|
15
|
+
The source indicates which node this object originated from, or
|
|
16
|
+
`None` if it was generated by this node.
|
|
17
|
+
|
|
18
|
+
The normalized event type indicates how the knowledge object is
|
|
19
|
+
viewed from the perspective of this node, and what cache actions
|
|
20
|
+
will take place. (`NEW`, `UPDATE`) -> cache write, `FORGET` ->
|
|
21
|
+
cache delete, `None` -> no cache action, and end of pipeline.
|
|
22
|
+
|
|
23
|
+
The network targets indicate other nodes in the network this
|
|
24
|
+
knowledge object will be sent to. The event sent to them will be
|
|
25
|
+
constructed from this knowledge object's RID, manifest, contents,
|
|
26
|
+
and normalized event type.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
rid: RID
|
|
30
|
+
manifest: Manifest | None = None
|
|
31
|
+
contents: dict | None = None
|
|
32
|
+
event_type: EventType | None = None
|
|
33
|
+
normalized_event_type: EventType | None = None
|
|
34
|
+
source: KoiNetNode | None = None
|
|
35
|
+
network_targets: set[KoiNetNode] = set()
|
|
36
|
+
|
|
37
|
+
def __repr__(self):
|
|
38
|
+
return f"<KObj '{self.rid}' event type: '{self.event_type}' -> '{self.normalized_event_type}', source: '{self.source}'>"
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_rid(
|
|
42
|
+
cls,
|
|
43
|
+
rid: RID,
|
|
44
|
+
event_type: EventType | None = None,
|
|
45
|
+
source: KoiNetNode | None = None
|
|
46
|
+
) -> "KnowledgeObject":
|
|
47
|
+
"""Creates a `KnowledgeObject` from an `RID`."""
|
|
48
|
+
|
|
49
|
+
return cls(
|
|
50
|
+
rid=rid,
|
|
51
|
+
event_type=event_type,
|
|
52
|
+
source=source
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def from_manifest(
|
|
57
|
+
cls,
|
|
58
|
+
manifest: Manifest,
|
|
59
|
+
event_type: EventType | None = None,
|
|
60
|
+
source: KoiNetNode | None = None
|
|
61
|
+
) -> "KnowledgeObject":
|
|
62
|
+
"""Creates a `KnowledgeObject` from a `Manifest`."""
|
|
63
|
+
|
|
64
|
+
return cls(
|
|
65
|
+
rid=manifest.rid,
|
|
66
|
+
manifest=manifest,
|
|
67
|
+
event_type=event_type,
|
|
68
|
+
source=source
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def from_bundle(
|
|
73
|
+
cls,
|
|
74
|
+
bundle: Bundle,
|
|
75
|
+
event_type: EventType | None = None,
|
|
76
|
+
source: KoiNetNode | None = None
|
|
77
|
+
) -> "KnowledgeObject":
|
|
78
|
+
"""Creates a `KnowledgeObject` from a `Bundle`."""
|
|
79
|
+
|
|
80
|
+
return cls(
|
|
81
|
+
rid=bundle.rid,
|
|
82
|
+
manifest=bundle.manifest,
|
|
83
|
+
contents=bundle.contents,
|
|
84
|
+
event_type=event_type,
|
|
85
|
+
source=source
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def from_event(
|
|
90
|
+
cls,
|
|
91
|
+
event: Event,
|
|
92
|
+
source: KoiNetNode | None = None
|
|
93
|
+
) -> "KnowledgeObject":
|
|
94
|
+
"""Creates a `KnowledgeObject` from an `Event`."""
|
|
95
|
+
|
|
96
|
+
return cls(
|
|
97
|
+
rid=event.rid,
|
|
98
|
+
manifest=event.manifest,
|
|
99
|
+
contents=event.contents,
|
|
100
|
+
event_type=event.event_type,
|
|
101
|
+
source=source
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def bundle(self) -> Bundle:
|
|
106
|
+
"""Bundle representation of knowledge object."""
|
|
107
|
+
|
|
108
|
+
if self.manifest is None or self.contents is None:
|
|
109
|
+
raise ValueError("Knowledge object missing manifest or contents, cannot convert to `Bundle`.")
|
|
110
|
+
|
|
111
|
+
return Bundle(
|
|
112
|
+
manifest=self.manifest,
|
|
113
|
+
contents=self.contents
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def normalized_event(self) -> Event:
|
|
118
|
+
"""Event representation of knowledge object."""
|
|
119
|
+
|
|
120
|
+
if self.normalized_event_type is None:
|
|
121
|
+
raise ValueError("Internal event's normalized event type is None, cannot convert to Event")
|
|
122
|
+
|
|
123
|
+
elif self.normalized_event_type == EventType.FORGET:
|
|
124
|
+
return Event(
|
|
125
|
+
rid=self.rid,
|
|
126
|
+
event_type=EventType.FORGET
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
else:
|
|
130
|
+
return Event(
|
|
131
|
+
rid=self.rid,
|
|
132
|
+
event_type=self.normalized_event_type,
|
|
133
|
+
manifest=self.manifest,
|
|
134
|
+
contents=self.contents
|
|
135
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import structlog
|
|
2
|
+
from queue import Queue
|
|
3
|
+
from rid_lib.core import RID
|
|
4
|
+
from rid_lib.ext import Bundle, Manifest
|
|
5
|
+
from rid_lib.types import KoiNetNode
|
|
6
|
+
from ..protocol.event import Event, EventType
|
|
7
|
+
from .knowledge_object import KnowledgeObject
|
|
8
|
+
|
|
9
|
+
log = structlog.stdlib.get_logger()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class KobjQueue:
|
|
13
|
+
"""Queue for knowledge objects entering the processing pipeline."""
|
|
14
|
+
q: Queue[KnowledgeObject]
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.q = Queue()
|
|
18
|
+
|
|
19
|
+
def push(
|
|
20
|
+
self,
|
|
21
|
+
rid: RID | None = None,
|
|
22
|
+
manifest: Manifest | None = None,
|
|
23
|
+
bundle: Bundle | None = None,
|
|
24
|
+
event: Event | None = None,
|
|
25
|
+
kobj: KnowledgeObject | None = None,
|
|
26
|
+
event_type: EventType | None = None,
|
|
27
|
+
source: KoiNetNode | None = None
|
|
28
|
+
):
|
|
29
|
+
"""Pushes knowledge object to queue.
|
|
30
|
+
|
|
31
|
+
Input may take the form of an RID, manifest, bundle, event,
|
|
32
|
+
or knowledge object (with an optional event type for RID,
|
|
33
|
+
manifest, or bundle objects). All objects will be normalized
|
|
34
|
+
to knowledge objects and queued.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
if rid:
|
|
38
|
+
_kobj = KnowledgeObject.from_rid(rid, event_type, source)
|
|
39
|
+
elif manifest:
|
|
40
|
+
_kobj = KnowledgeObject.from_manifest(manifest, event_type, source)
|
|
41
|
+
elif bundle:
|
|
42
|
+
_kobj = KnowledgeObject.from_bundle(bundle, event_type, source)
|
|
43
|
+
elif event:
|
|
44
|
+
_kobj = KnowledgeObject.from_event(event, source)
|
|
45
|
+
elif kobj:
|
|
46
|
+
_kobj = kobj
|
|
47
|
+
else:
|
|
48
|
+
raise ValueError("One of 'rid', 'manifest', 'bundle', 'event', or 'kobj' must be provided")
|
|
49
|
+
|
|
50
|
+
self.q.put(_kobj)
|
|
51
|
+
log.debug(f"Queued {_kobj!r}")
|