koi-net 1.0.0b11__py3-none-any.whl → 1.0.0b13__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.

@@ -1,163 +1,163 @@
1
- """Provides implementations of default knowledge handlers."""
2
-
3
- import logging
4
- from rid_lib.ext.bundle import Bundle
5
- from rid_lib.types import KoiNetNode, KoiNetEdge
6
- from koi_net.protocol.node import NodeType
7
- from .interface import ProcessorInterface
8
- from .handler import KnowledgeHandler, HandlerType, STOP_CHAIN
9
- from .knowledge_object import KnowledgeObject, KnowledgeSource
10
- from ..protocol.event import Event, EventType
11
- from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
- # RID handlers
16
-
17
- @KnowledgeHandler.create(HandlerType.RID)
18
- def basic_rid_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
19
- """Default RID handler.
20
-
21
- Blocks external events about this node. Allows `FORGET` events if RID is known to this node.
22
- """
23
- if (kobj.rid == processor.identity.rid and
24
- kobj.source == KnowledgeSource.External):
25
- logger.debug("Don't let anyone else tell me who I am!")
26
- return STOP_CHAIN
27
-
28
- if kobj.event_type == EventType.FORGET:
29
- kobj.normalized_event_type = EventType.FORGET
30
- return kobj
31
-
32
- # Manifest handlers
33
-
34
- @KnowledgeHandler.create(HandlerType.Manifest)
35
- def basic_manifest_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
36
- """Default manifest handler.
37
-
38
- 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.
39
- """
40
- prev_bundle = processor.cache.read(kobj.rid)
41
-
42
- if prev_bundle:
43
- if kobj.manifest.sha256_hash == prev_bundle.manifest.sha256_hash:
44
- logger.debug("Hash of incoming manifest is same as existing knowledge, ignoring")
45
- return STOP_CHAIN
46
- if kobj.manifest.timestamp <= prev_bundle.manifest.timestamp:
47
- logger.debug("Timestamp of incoming manifest is the same or older than existing knowledge, ignoring")
48
- return STOP_CHAIN
49
-
50
- logger.debug("RID previously known to me, labeling as 'UPDATE'")
51
- kobj.normalized_event_type = EventType.UPDATE
52
-
53
- else:
54
- logger.debug("RID previously unknown to me, labeling as 'NEW'")
55
- kobj.normalized_event_type = EventType.NEW
56
-
57
- return kobj
58
-
59
-
60
- # Bundle handlers
61
-
62
- @KnowledgeHandler.create(
63
- handler_type=HandlerType.Bundle,
64
- rid_types=[KoiNetEdge],
65
- source=KnowledgeSource.External,
66
- event_types=[EventType.NEW, EventType.UPDATE])
67
- def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
68
- """Handles basic edge negotiation process.
69
-
70
- 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.
71
- """
72
-
73
- edge_profile = EdgeProfile.model_validate(kobj.contents)
74
-
75
- # indicates peer subscribing to me
76
- if edge_profile.source == processor.identity.rid:
77
- if edge_profile.status != EdgeStatus.PROPOSED:
78
- return
79
-
80
- logger.debug("Handling edge negotiation")
81
-
82
- peer_rid = edge_profile.target
83
- peer_profile = processor.network.graph.get_node_profile(peer_rid)
84
-
85
- if not peer_profile:
86
- logger.warning(f"Peer {peer_rid} unknown to me")
87
- return STOP_CHAIN
88
-
89
- # explicitly provided event RID types and (self) node + edge objects
90
- provided_events = (
91
- *processor.identity.profile.provides.event,
92
- KoiNetNode, KoiNetEdge
93
- )
94
-
95
-
96
- abort = False
97
- if (edge_profile.edge_type == EdgeType.WEBHOOK and
98
- peer_profile.node_type == NodeType.PARTIAL):
99
- logger.debug("Partial nodes cannot use webhooks")
100
- abort = True
101
-
102
- if not set(edge_profile.rid_types).issubset(provided_events):
103
- logger.debug("Requested RID types not provided by this node")
104
- abort = True
105
-
106
- if abort:
107
- event = Event.from_rid(EventType.FORGET, kobj.rid)
108
- processor.network.push_event_to(event, peer_rid, flush=True)
109
- return STOP_CHAIN
110
-
111
- else:
112
- # approve edge profile
113
- logger.debug("Approving proposed edge")
114
- edge_profile.status = EdgeStatus.APPROVED
115
- updated_bundle = Bundle.generate(kobj.rid, edge_profile.model_dump())
116
-
117
- processor.handle(bundle=updated_bundle, event_type=EventType.UPDATE)
118
- return
119
-
120
- elif edge_profile.target == processor.identity.rid:
121
- if edge_profile.status == EdgeStatus.APPROVED:
122
- logger.debug("Edge approved by other node!")
123
-
124
-
125
- # Network handlers
126
-
127
- @KnowledgeHandler.create(HandlerType.Network)
128
- def basic_network_output_filter(processor: ProcessorInterface, kobj: KnowledgeObject):
129
- """Default network handler.
130
-
131
- 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)."""
132
-
133
- involves_me = False
134
- if kobj.source == KnowledgeSource.Internal:
135
- if (type(kobj.rid) == KoiNetNode):
136
- if (kobj.rid == processor.identity.rid):
137
- involves_me = True
138
-
139
- elif type(kobj.rid) == KoiNetEdge:
140
- edge_profile = kobj.bundle.validate_contents(EdgeProfile)
141
-
142
- if edge_profile.source == processor.identity.rid:
143
- logger.debug(f"Adding edge target '{edge_profile.target!r}' to network targets")
144
- kobj.network_targets.update([edge_profile.target])
145
- involves_me = True
146
-
147
- elif edge_profile.target == processor.identity.rid:
148
- logger.debug(f"Adding edge source '{edge_profile.source!r}' to network targets")
149
- kobj.network_targets.update([edge_profile.source])
150
- involves_me = True
151
-
152
- if (type(kobj.rid) in processor.identity.profile.provides.event or involves_me):
153
- # broadcasts to subscribers if I'm an event provider of this RID type OR it involves me
154
- subscribers = processor.network.graph.get_neighbors(
155
- direction="out",
156
- allowed_type=type(kobj.rid)
157
- )
158
-
159
- logger.debug(f"Updating network targets with '{type(kobj.rid)}' subscribers: {subscribers}")
160
- kobj.network_targets.update(subscribers)
161
-
162
- return kobj
1
+ """Provides implementations of default knowledge handlers."""
2
+
3
+ import logging
4
+ from rid_lib.ext.bundle import Bundle
5
+ from rid_lib.types import KoiNetNode, KoiNetEdge
6
+ from koi_net.protocol.node import NodeType
7
+ from .interface import ProcessorInterface
8
+ from .handler import KnowledgeHandler, HandlerType, STOP_CHAIN
9
+ from .knowledge_object import KnowledgeObject, KnowledgeSource
10
+ from ..protocol.event import Event, EventType
11
+ from ..protocol.edge import EdgeProfile, EdgeStatus, EdgeType
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # RID handlers
16
+
17
+ @KnowledgeHandler.create(HandlerType.RID)
18
+ def basic_rid_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
19
+ """Default RID handler.
20
+
21
+ Blocks external events about this node. Allows `FORGET` events if RID is known to this node.
22
+ """
23
+ if (kobj.rid == processor.identity.rid and
24
+ kobj.source == KnowledgeSource.External):
25
+ logger.debug("Don't let anyone else tell me who I am!")
26
+ return STOP_CHAIN
27
+
28
+ if kobj.event_type == EventType.FORGET:
29
+ kobj.normalized_event_type = EventType.FORGET
30
+ return kobj
31
+
32
+ # Manifest handlers
33
+
34
+ @KnowledgeHandler.create(HandlerType.Manifest)
35
+ def basic_manifest_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
36
+ """Default manifest handler.
37
+
38
+ 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.
39
+ """
40
+ prev_bundle = processor.cache.read(kobj.rid)
41
+
42
+ if prev_bundle:
43
+ if kobj.manifest.sha256_hash == prev_bundle.manifest.sha256_hash:
44
+ logger.debug("Hash of incoming manifest is same as existing knowledge, ignoring")
45
+ return STOP_CHAIN
46
+ if kobj.manifest.timestamp <= prev_bundle.manifest.timestamp:
47
+ logger.debug("Timestamp of incoming manifest is the same or older than existing knowledge, ignoring")
48
+ return STOP_CHAIN
49
+
50
+ logger.debug("RID previously known to me, labeling as 'UPDATE'")
51
+ kobj.normalized_event_type = EventType.UPDATE
52
+
53
+ else:
54
+ logger.debug("RID previously unknown to me, labeling as 'NEW'")
55
+ kobj.normalized_event_type = EventType.NEW
56
+
57
+ return kobj
58
+
59
+
60
+ # Bundle handlers
61
+
62
+ @KnowledgeHandler.create(
63
+ handler_type=HandlerType.Bundle,
64
+ rid_types=[KoiNetEdge],
65
+ source=KnowledgeSource.External,
66
+ event_types=[EventType.NEW, EventType.UPDATE])
67
+ def edge_negotiation_handler(processor: ProcessorInterface, kobj: KnowledgeObject):
68
+ """Handles basic edge negotiation process.
69
+
70
+ 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.
71
+ """
72
+
73
+ edge_profile = EdgeProfile.model_validate(kobj.contents)
74
+
75
+ # indicates peer subscribing to me
76
+ if edge_profile.source == processor.identity.rid:
77
+ if edge_profile.status != EdgeStatus.PROPOSED:
78
+ return
79
+
80
+ logger.debug("Handling edge negotiation")
81
+
82
+ peer_rid = edge_profile.target
83
+ peer_profile = processor.network.graph.get_node_profile(peer_rid)
84
+
85
+ if not peer_profile:
86
+ logger.warning(f"Peer {peer_rid} unknown to me")
87
+ return STOP_CHAIN
88
+
89
+ # explicitly provided event RID types and (self) node + edge objects
90
+ provided_events = (
91
+ *processor.identity.profile.provides.event,
92
+ KoiNetNode, KoiNetEdge
93
+ )
94
+
95
+
96
+ abort = False
97
+ if (edge_profile.edge_type == EdgeType.WEBHOOK and
98
+ peer_profile.node_type == NodeType.PARTIAL):
99
+ logger.debug("Partial nodes cannot use webhooks")
100
+ abort = True
101
+
102
+ if not set(edge_profile.rid_types).issubset(provided_events):
103
+ logger.debug("Requested RID types not provided by this node")
104
+ abort = True
105
+
106
+ if abort:
107
+ event = Event.from_rid(EventType.FORGET, kobj.rid)
108
+ processor.network.push_event_to(event, peer_rid, flush=True)
109
+ return STOP_CHAIN
110
+
111
+ else:
112
+ # approve edge profile
113
+ logger.debug("Approving proposed edge")
114
+ edge_profile.status = EdgeStatus.APPROVED
115
+ updated_bundle = Bundle.generate(kobj.rid, edge_profile.model_dump())
116
+
117
+ processor.handle(bundle=updated_bundle, event_type=EventType.UPDATE)
118
+ return
119
+
120
+ elif edge_profile.target == processor.identity.rid:
121
+ if edge_profile.status == EdgeStatus.APPROVED:
122
+ logger.debug("Edge approved by other node!")
123
+
124
+
125
+ # Network handlers
126
+
127
+ @KnowledgeHandler.create(HandlerType.Network)
128
+ def basic_network_output_filter(processor: ProcessorInterface, kobj: KnowledgeObject):
129
+ """Default network handler.
130
+
131
+ 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)."""
132
+
133
+ involves_me = False
134
+ if kobj.source == KnowledgeSource.Internal:
135
+ if (type(kobj.rid) == KoiNetNode):
136
+ if (kobj.rid == processor.identity.rid):
137
+ involves_me = True
138
+
139
+ elif type(kobj.rid) == KoiNetEdge:
140
+ edge_profile = kobj.bundle.validate_contents(EdgeProfile)
141
+
142
+ if edge_profile.source == processor.identity.rid:
143
+ logger.debug(f"Adding edge target '{edge_profile.target!r}' to network targets")
144
+ kobj.network_targets.update([edge_profile.target])
145
+ involves_me = True
146
+
147
+ elif edge_profile.target == processor.identity.rid:
148
+ logger.debug(f"Adding edge source '{edge_profile.source!r}' to network targets")
149
+ kobj.network_targets.update([edge_profile.source])
150
+ involves_me = True
151
+
152
+ if (type(kobj.rid) in processor.identity.profile.provides.event or involves_me):
153
+ # broadcasts to subscribers if I'm an event provider of this RID type OR it involves me
154
+ subscribers = processor.network.graph.get_neighbors(
155
+ direction="out",
156
+ allowed_type=type(kobj.rid)
157
+ )
158
+
159
+ logger.debug(f"Updating network targets with '{type(kobj.rid)}' subscribers: {subscribers}")
160
+ kobj.network_targets.update(subscribers)
161
+
162
+ return kobj
163
163
 
@@ -1,59 +1,59 @@
1
- from dataclasses import dataclass
2
- from enum import StrEnum
3
- from typing import Callable
4
- from rid_lib import RIDType
5
-
6
- from ..protocol.event import EventType
7
- from .knowledge_object import KnowledgeSource, KnowledgeEventType
8
-
9
-
10
- class StopChain:
11
- """Class for a sentinel value by knowledge handlers."""
12
- pass
13
-
14
- STOP_CHAIN = StopChain()
15
-
16
-
17
- class HandlerType(StrEnum):
18
- """Types of handlers used in knowledge processing pipeline.
19
-
20
- - RID - provided RID; if event type is `FORGET`, this handler decides whether to delete the knowledge from the cache by setting the normalized event type to `FORGET`, otherwise this handler decides whether to validate the manifest (and fetch it if not provided).
21
- - Manifest - provided RID, manifest; decides whether to validate the bundle (and fetch it if not provided).
22
- - Bundle - provided RID, manifest, contents (bundle); decides whether to write knowledge to the cache by setting the normalized event type to `NEW` or `UPDATE`.
23
- - Network - provided RID, manifest, contents (bundle); decides which nodes (if any) to broadcast an event about this knowledge to. (Note, if event type is `FORGET`, the manifest and contents will be retrieved from the local cache, and indicate the last state of the knowledge before it was deleted.)
24
- - Final - provided RID, manifests, contents (bundle); final action taken after network broadcast.
25
- """
26
-
27
- RID = "rid",
28
- Manifest = "manifest",
29
- Bundle = "bundle",
30
- Network = "network",
31
- Final = "final"
32
-
33
- @dataclass
34
- class KnowledgeHandler:
35
- """Handles knowledge processing events of the provided types."""
36
-
37
- func: Callable
38
- handler_type: HandlerType
39
- rid_types: list[RIDType] | None
40
- source: KnowledgeSource | None = None
41
- event_types: list[KnowledgeEventType] | None = None
42
-
43
- @classmethod
44
- def create(
45
- cls,
46
- handler_type: HandlerType,
47
- rid_types: list[RIDType] | None = None,
48
- source: KnowledgeSource | None = None,
49
- event_types: list[KnowledgeEventType] | None = None
50
- ):
51
- """Special decorator that returns a KnowledgeHandler instead of a function.
52
-
53
- The function symbol will redefined as a `KnowledgeHandler`, which can be passed into the `ProcessorInterface` constructor. This is used to register default handlers.
54
- """
55
- def decorator(func: Callable) -> KnowledgeHandler:
56
- handler = cls(func, handler_type, rid_types, source, event_types)
57
- return handler
58
- return decorator
59
-
1
+ from dataclasses import dataclass
2
+ from enum import StrEnum
3
+ from typing import Callable
4
+ from rid_lib import RIDType
5
+
6
+ from ..protocol.event import EventType
7
+ from .knowledge_object import KnowledgeSource, KnowledgeEventType
8
+
9
+
10
+ class StopChain:
11
+ """Class for a sentinel value by knowledge handlers."""
12
+ pass
13
+
14
+ STOP_CHAIN = StopChain()
15
+
16
+
17
+ class HandlerType(StrEnum):
18
+ """Types of handlers used in knowledge processing pipeline.
19
+
20
+ - RID - provided RID; if event type is `FORGET`, this handler decides whether to delete the knowledge from the cache by setting the normalized event type to `FORGET`, otherwise this handler decides whether to validate the manifest (and fetch it if not provided).
21
+ - Manifest - provided RID, manifest; decides whether to validate the bundle (and fetch it if not provided).
22
+ - Bundle - provided RID, manifest, contents (bundle); decides whether to write knowledge to the cache by setting the normalized event type to `NEW` or `UPDATE`.
23
+ - Network - provided RID, manifest, contents (bundle); decides which nodes (if any) to broadcast an event about this knowledge to. (Note, if event type is `FORGET`, the manifest and contents will be retrieved from the local cache, and indicate the last state of the knowledge before it was deleted.)
24
+ - Final - provided RID, manifests, contents (bundle); final action taken after network broadcast.
25
+ """
26
+
27
+ RID = "rid",
28
+ Manifest = "manifest",
29
+ Bundle = "bundle",
30
+ Network = "network",
31
+ Final = "final"
32
+
33
+ @dataclass
34
+ class KnowledgeHandler:
35
+ """Handles knowledge processing events of the provided types."""
36
+
37
+ func: Callable
38
+ handler_type: HandlerType
39
+ rid_types: list[RIDType] | None
40
+ source: KnowledgeSource | None = None
41
+ event_types: list[KnowledgeEventType] | None = None
42
+
43
+ @classmethod
44
+ def create(
45
+ cls,
46
+ handler_type: HandlerType,
47
+ rid_types: list[RIDType] | None = None,
48
+ source: KnowledgeSource | None = None,
49
+ event_types: list[KnowledgeEventType] | None = None
50
+ ):
51
+ """Special decorator that returns a KnowledgeHandler instead of a function.
52
+
53
+ The function symbol will redefined as a `KnowledgeHandler`, which can be passed into the `ProcessorInterface` constructor. This is used to register default handlers.
54
+ """
55
+ def decorator(func: Callable) -> KnowledgeHandler:
56
+ handler = cls(func, handler_type, rid_types, source, event_types)
57
+ return handler
58
+ return decorator
59
+