koi-net 1.1.0b1__tar.gz → 1.1.0b3__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.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/PKG-INFO +3 -3
- koi_net-1.1.0b3/examples/coordinator.py +76 -0
- koi_net-1.1.0b1/examples/basic_partial_node.py → koi_net-1.1.0b3/examples/partial.py +8 -19
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/pyproject.toml +4 -4
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/config.py +1 -0
- koi_net-1.1.0b3/src/koi_net/core.py +194 -0
- koi_net-1.1.0b3/src/koi_net/lifecycle.py +76 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/response_handler.py +4 -3
- koi_net-1.1.0b3/src/koi_net/poller.py +47 -0
- koi_net-1.1.0b3/src/koi_net/server.py +128 -0
- koi_net-1.1.0b1/examples/basic_coordinator_node.py +0 -111
- koi_net-1.1.0b1/src/koi_net/core.py +0 -192
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/.github/workflows/publish-to-pypi.yml +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/.gitignore +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/LICENSE +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/README.md +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/requirements.txt +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/__init__.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/context.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/default_actions.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/effector.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/identity.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/__init__.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/behavior.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/error_handler.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/event_queue.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/graph.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/request_handler.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/network/resolver.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/processor/__init__.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/processor/default_handlers.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/processor/handler.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/processor/interface.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/processor/knowledge_object.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/processor/knowledge_pipeline.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/__init__.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/api_models.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/consts.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/edge.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/envelope.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/errors.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/event.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/node.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/protocol/secure.py +0 -0
- {koi_net-1.1.0b1 → koi_net-1.1.0b3}/src/koi_net/secure.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: koi-net
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.0b3
|
|
4
4
|
Summary: Implementation of KOI-net protocol in Python
|
|
5
5
|
Project-URL: Homepage, https://github.com/BlockScience/koi-net/
|
|
6
6
|
Author-email: Luke Miller <luke@block.science>
|
|
@@ -28,19 +28,19 @@ License: MIT License
|
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
Requires-Python: >=3.10
|
|
30
30
|
Requires-Dist: cryptography>=45.0.3
|
|
31
|
+
Requires-Dist: fastapi>=0.115.12
|
|
31
32
|
Requires-Dist: httpx>=0.28.1
|
|
32
33
|
Requires-Dist: networkx>=3.4.2
|
|
33
34
|
Requires-Dist: pydantic>=2.10.6
|
|
34
35
|
Requires-Dist: python-dotenv>=1.1.0
|
|
35
36
|
Requires-Dist: rid-lib>=3.2.7
|
|
36
37
|
Requires-Dist: ruamel-yaml>=0.18.10
|
|
38
|
+
Requires-Dist: uvicorn>=0.34.2
|
|
37
39
|
Provides-Extra: dev
|
|
38
40
|
Requires-Dist: build; extra == 'dev'
|
|
39
41
|
Requires-Dist: twine>=6.0; extra == 'dev'
|
|
40
42
|
Provides-Extra: examples
|
|
41
|
-
Requires-Dist: fastapi; extra == 'examples'
|
|
42
43
|
Requires-Dist: rich; extra == 'examples'
|
|
43
|
-
Requires-Dist: uvicorn; extra == 'examples'
|
|
44
44
|
Description-Content-Type: text/markdown
|
|
45
45
|
|
|
46
46
|
# KOI-net
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from rich.logging import RichHandler
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
from rid_lib.types import KoiNetNode, KoiNetEdge
|
|
5
|
+
from koi_net.config import NodeConfig, KoiNetConfig
|
|
6
|
+
from koi_net.protocol.node import NodeProfile, NodeProvides, NodeType
|
|
7
|
+
from koi_net import NodeInterface
|
|
8
|
+
from koi_net.context import HandlerContext
|
|
9
|
+
from koi_net.processor.handler import HandlerType
|
|
10
|
+
from koi_net.processor.knowledge_object import KnowledgeObject
|
|
11
|
+
from koi_net.protocol.event import Event, EventType
|
|
12
|
+
from koi_net.protocol.edge import EdgeType, generate_edge_bundle
|
|
13
|
+
|
|
14
|
+
logging.basicConfig(
|
|
15
|
+
level=logging.INFO,
|
|
16
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
17
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
18
|
+
handlers=[RichHandler()]
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
logging.getLogger("koi_net").setLevel(logging.DEBUG)
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
class CoordinatorConfig(NodeConfig):
|
|
25
|
+
koi_net: KoiNetConfig = Field(default_factory = lambda:
|
|
26
|
+
KoiNetConfig(
|
|
27
|
+
node_name="coordinator",
|
|
28
|
+
node_profile=NodeProfile(
|
|
29
|
+
node_type=NodeType.FULL,
|
|
30
|
+
provides=NodeProvides(
|
|
31
|
+
event=[KoiNetNode, KoiNetEdge],
|
|
32
|
+
state=[KoiNetNode, KoiNetEdge]
|
|
33
|
+
)
|
|
34
|
+
),
|
|
35
|
+
cache_directory_path=".coordinator_rid_cache",
|
|
36
|
+
event_queues_path="coordinator_event_queues.json",
|
|
37
|
+
private_key_pem_path="coordinator_priv_key.pem"
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
node = NodeInterface(
|
|
42
|
+
config=CoordinatorConfig.load_from_yaml("coordinator_config.yaml"),
|
|
43
|
+
use_kobj_processor_thread=True
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@node.processor.pipeline.register_handler(HandlerType.Network, rid_types=[KoiNetNode])
|
|
47
|
+
def handshake_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
48
|
+
logger.info("Handling node handshake")
|
|
49
|
+
|
|
50
|
+
# only respond if node declares itself as NEW
|
|
51
|
+
if kobj.event_type != EventType.NEW:
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
logger.info("Sharing this node's bundle with peer")
|
|
55
|
+
identity_bundle = ctx.effector.deref(ctx.identity.rid)
|
|
56
|
+
ctx.event_queue.push_event_to(
|
|
57
|
+
event=Event.from_bundle(EventType.NEW, identity_bundle),
|
|
58
|
+
node=kobj.rid,
|
|
59
|
+
flush=True
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
logger.info("Proposing new edge")
|
|
63
|
+
# defer handling of proposed edge
|
|
64
|
+
|
|
65
|
+
edge_bundle = generate_edge_bundle(
|
|
66
|
+
source=kobj.rid,
|
|
67
|
+
target=ctx.identity.rid,
|
|
68
|
+
edge_type=EdgeType.WEBHOOK,
|
|
69
|
+
rid_types=[KoiNetNode, KoiNetEdge]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
ctx.handle(rid=edge_bundle.rid, event_type=EventType.FORGET)
|
|
73
|
+
ctx.handle(bundle=edge_bundle)
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
node.server.run()
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import time
|
|
2
1
|
import logging
|
|
3
2
|
from pydantic import Field
|
|
4
3
|
from rich.logging import RichHandler
|
|
5
4
|
from koi_net import NodeInterface
|
|
6
5
|
from koi_net.protocol.node import NodeProfile, NodeType
|
|
7
|
-
from koi_net.config import NodeConfig, KoiNetConfig
|
|
6
|
+
from koi_net.config import NodeConfig, KoiNetConfig
|
|
8
7
|
|
|
9
8
|
logging.basicConfig(
|
|
10
9
|
level=logging.INFO,
|
|
@@ -18,31 +17,21 @@ logger = logging.getLogger(__name__)
|
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
class PartialNodeConfig(NodeConfig):
|
|
21
|
-
koi_net: KoiNetConfig
|
|
20
|
+
koi_net: KoiNetConfig = Field(default_factory = lambda:
|
|
22
21
|
KoiNetConfig(
|
|
23
22
|
node_name="partial",
|
|
24
23
|
node_profile=NodeProfile(
|
|
25
24
|
node_type=NodeType.PARTIAL
|
|
26
25
|
),
|
|
27
|
-
cache_directory_path=".
|
|
28
|
-
event_queues_path="
|
|
26
|
+
cache_directory_path=".partial_rid_cache",
|
|
27
|
+
event_queues_path="partial_event_queues.json",
|
|
28
|
+
private_key_pem_path="partial_priv_key.pem"
|
|
29
29
|
)
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
node = NodeInterface(
|
|
34
|
-
config=PartialNodeConfig.load_from_yaml("
|
|
33
|
+
config=PartialNodeConfig.load_from_yaml("partial_config.yaml")
|
|
35
34
|
)
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
node.start()
|
|
39
|
-
|
|
40
|
-
while True:
|
|
41
|
-
neighbors = node.resolver.poll_neighbors()
|
|
42
|
-
for node_rid in neighbors:
|
|
43
|
-
events = neighbors[node_rid]
|
|
44
|
-
for event in events:
|
|
45
|
-
node.processor.handle(event=event, source=node_rid)
|
|
46
|
-
node.processor.flush_kobj_queue()
|
|
47
|
-
|
|
48
|
-
time.sleep(5)
|
|
36
|
+
if __name__ == "__main__":
|
|
37
|
+
node.lifecycle.start()
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "koi-net"
|
|
7
|
-
version = "1.1.0-beta.
|
|
7
|
+
version = "1.1.0-beta.3"
|
|
8
8
|
description = "Implementation of KOI-net protocol in Python"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Luke Miller", email = "luke@block.science"}
|
|
@@ -19,15 +19,15 @@ dependencies = [
|
|
|
19
19
|
"pydantic>=2.10.6",
|
|
20
20
|
"ruamel.yaml>=0.18.10",
|
|
21
21
|
"python-dotenv>=1.1.0",
|
|
22
|
-
"cryptography>=45.0.3"
|
|
22
|
+
"cryptography>=45.0.3",
|
|
23
|
+
"fastapi>=0.115.12",
|
|
24
|
+
"uvicorn>=0.34.2"
|
|
23
25
|
]
|
|
24
26
|
|
|
25
27
|
[project.optional-dependencies]
|
|
26
28
|
dev = ["twine>=6.0", "build"]
|
|
27
29
|
examples = [
|
|
28
30
|
"rich",
|
|
29
|
-
"fastapi",
|
|
30
|
-
"uvicorn"
|
|
31
31
|
]
|
|
32
32
|
|
|
33
33
|
[project.urls]
|
|
@@ -29,6 +29,7 @@ class KoiNetConfig(BaseModel):
|
|
|
29
29
|
cache_directory_path: str = ".rid_cache"
|
|
30
30
|
event_queues_path: str = "event_queues.json"
|
|
31
31
|
private_key_pem_path: str = "priv_key.pem"
|
|
32
|
+
polling_interval: int = 5
|
|
32
33
|
|
|
33
34
|
first_contact: NodeContact = Field(default_factory=NodeContact)
|
|
34
35
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Generic, TypeVar
|
|
3
|
+
from rid_lib.ext import Cache
|
|
4
|
+
from .network.resolver import NetworkResolver
|
|
5
|
+
from .network.event_queue import NetworkEventQueue
|
|
6
|
+
from .network.graph import NetworkGraph
|
|
7
|
+
from .network.request_handler import RequestHandler
|
|
8
|
+
from .network.response_handler import ResponseHandler
|
|
9
|
+
from .network.error_handler import ErrorHandler
|
|
10
|
+
from .network.behavior import Actor
|
|
11
|
+
from .processor.interface import ProcessorInterface
|
|
12
|
+
from .processor import default_handlers
|
|
13
|
+
from .processor.handler import KnowledgeHandler
|
|
14
|
+
from .processor.knowledge_pipeline import KnowledgePipeline
|
|
15
|
+
from .identity import NodeIdentity
|
|
16
|
+
from .secure import Secure
|
|
17
|
+
from .config import NodeConfig
|
|
18
|
+
from .context import HandlerContext, ActionContext
|
|
19
|
+
from .effector import Effector
|
|
20
|
+
from .server import NodeServer
|
|
21
|
+
from .lifecycle import NodeLifecycle
|
|
22
|
+
from .poller import NodePoller
|
|
23
|
+
from . import default_actions
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
T = TypeVar("T", bound=NodeConfig)
|
|
29
|
+
|
|
30
|
+
class NodeInterface(Generic[T]):
|
|
31
|
+
config: T
|
|
32
|
+
cache: Cache
|
|
33
|
+
identity: NodeIdentity
|
|
34
|
+
resolver: NetworkResolver
|
|
35
|
+
event_queue: NetworkEventQueue
|
|
36
|
+
graph: NetworkGraph
|
|
37
|
+
processor: ProcessorInterface
|
|
38
|
+
secure: Secure
|
|
39
|
+
server: NodeServer
|
|
40
|
+
|
|
41
|
+
use_kobj_processor_thread: bool
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
config: T,
|
|
46
|
+
use_kobj_processor_thread: bool = False,
|
|
47
|
+
handlers: list[KnowledgeHandler] | None = None,
|
|
48
|
+
|
|
49
|
+
CacheOverride: type[Cache] | None = None,
|
|
50
|
+
NodeIdentityOverride: type[NodeIdentity] | None = None,
|
|
51
|
+
EffectorOverride: type[Effector] | None = None,
|
|
52
|
+
NetworkGraphOverride: type[NetworkGraph] | None = None,
|
|
53
|
+
SecureOverride: type[Secure] | None = None,
|
|
54
|
+
RequestHandlerOverride: type[RequestHandler] | None = None,
|
|
55
|
+
ResponseHandlerOverride: type[ResponseHandler] | None = None,
|
|
56
|
+
NetworkResolverOverride: type[NetworkResolver] | None = None,
|
|
57
|
+
NetworkEventQueueOverride: type[NetworkEventQueue] | None = None,
|
|
58
|
+
ActorOverride: type[Actor] | None = None,
|
|
59
|
+
ActionContextOverride: type[ActionContext] | None = None,
|
|
60
|
+
HandlerContextOverride: type[HandlerContext] | None = None,
|
|
61
|
+
KnowledgePipelineOverride: type[KnowledgePipeline] | None = None,
|
|
62
|
+
ProcessorInterfaceOverride: type[ProcessorInterface] | None = None,
|
|
63
|
+
ErrorHandlerOverride: type[ErrorHandler] | None = None,
|
|
64
|
+
NodeLifecycleOverride: type[NodeLifecycle] | None = None,
|
|
65
|
+
NodeServerOverride: type[NodeServer] | None = None,
|
|
66
|
+
NodePollerOverride: type[NodePoller] | None = None,
|
|
67
|
+
):
|
|
68
|
+
self.config = config
|
|
69
|
+
self.cache = (CacheOverride or Cache)(
|
|
70
|
+
directory_path=self.config.koi_net.cache_directory_path
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.identity = (NodeIdentityOverride or NodeIdentity)(config=self.config)
|
|
74
|
+
self.effector = (EffectorOverride or Effector)(cache=self.cache)
|
|
75
|
+
|
|
76
|
+
self.graph = (NetworkGraphOverride or NetworkGraph)(
|
|
77
|
+
cache=self.cache,
|
|
78
|
+
identity=self.identity
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
self.secure = (SecureOverride or Secure)(
|
|
82
|
+
identity=self.identity,
|
|
83
|
+
effector=self.effector,
|
|
84
|
+
config=self.config
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self.request_handler = (RequestHandlerOverride or RequestHandler)(
|
|
88
|
+
effector=self.effector,
|
|
89
|
+
identity=self.identity,
|
|
90
|
+
secure=self.secure
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
self.response_handler = (ResponseHandlerOverride or ResponseHandler)(self.cache, self.effector)
|
|
94
|
+
|
|
95
|
+
self.resolver = (NetworkResolverOverride or NetworkResolver)(
|
|
96
|
+
config=self.config,
|
|
97
|
+
cache=self.cache,
|
|
98
|
+
identity=self.identity,
|
|
99
|
+
graph=self.graph,
|
|
100
|
+
request_handler=self.request_handler,
|
|
101
|
+
effector=self.effector
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
self.event_queue = (NetworkEventQueueOverride or NetworkEventQueue)(
|
|
105
|
+
config=self.config,
|
|
106
|
+
cache=self.cache,
|
|
107
|
+
identity=self.identity,
|
|
108
|
+
graph=self.graph,
|
|
109
|
+
request_handler=self.request_handler,
|
|
110
|
+
effector=self.effector
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
self.actor = (ActorOverride or Actor)(
|
|
114
|
+
identity=self.identity,
|
|
115
|
+
effector=self.effector,
|
|
116
|
+
event_queue=self.event_queue
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# pull all handlers defined in default_handlers module
|
|
120
|
+
if handlers is None:
|
|
121
|
+
handlers = [
|
|
122
|
+
obj for obj in vars(default_handlers).values()
|
|
123
|
+
if isinstance(obj, KnowledgeHandler)
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
self.use_kobj_processor_thread = use_kobj_processor_thread
|
|
127
|
+
|
|
128
|
+
self.action_context = (ActionContextOverride or ActionContext)(
|
|
129
|
+
identity=self.identity,
|
|
130
|
+
effector=self.effector
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
self.handler_context = (HandlerContextOverride or HandlerContext)(
|
|
134
|
+
identity=self.identity,
|
|
135
|
+
cache=self.cache,
|
|
136
|
+
event_queue=self.event_queue,
|
|
137
|
+
graph=self.graph,
|
|
138
|
+
request_handler=self.request_handler,
|
|
139
|
+
effector=self.effector
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self.pipeline = (KnowledgePipelineOverride or KnowledgePipeline)(
|
|
143
|
+
handler_context=self.handler_context,
|
|
144
|
+
cache=self.cache,
|
|
145
|
+
request_handler=self.request_handler,
|
|
146
|
+
event_queue=self.event_queue,
|
|
147
|
+
graph=self.graph,
|
|
148
|
+
default_handlers=handlers
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
self.processor = (ProcessorInterfaceOverride or ProcessorInterface)(
|
|
152
|
+
pipeline=self.pipeline,
|
|
153
|
+
use_kobj_processor_thread=self.use_kobj_processor_thread
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self.error_handler = (ErrorHandlerOverride or ErrorHandler)(
|
|
157
|
+
processor=self.processor,
|
|
158
|
+
actor=self.actor
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
self.request_handler.set_error_handler(self.error_handler)
|
|
162
|
+
|
|
163
|
+
self.handler_context.set_processor(self.processor)
|
|
164
|
+
|
|
165
|
+
self.effector.set_processor(self.processor)
|
|
166
|
+
self.effector.set_resolver(self.resolver)
|
|
167
|
+
self.effector.set_action_context(self.action_context)
|
|
168
|
+
|
|
169
|
+
self.lifecycle = (NodeLifecycleOverride or NodeLifecycle)(
|
|
170
|
+
config=self.config,
|
|
171
|
+
identity=self.identity,
|
|
172
|
+
graph=self.graph,
|
|
173
|
+
processor=self.processor,
|
|
174
|
+
effector=self.effector,
|
|
175
|
+
actor=self.actor,
|
|
176
|
+
use_kobj_processor_thread=use_kobj_processor_thread
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# if self.config.koi_net.node_profile.node_type == NodeType.FULL:
|
|
180
|
+
self.server = (NodeServerOverride or NodeServer)(
|
|
181
|
+
config=self.config,
|
|
182
|
+
lifecycle=self.lifecycle,
|
|
183
|
+
secure=self.secure,
|
|
184
|
+
processor=self.processor,
|
|
185
|
+
event_queue=self.event_queue,
|
|
186
|
+
response_handler=self.response_handler
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
self.poller = (NodePollerOverride or NodePoller)(
|
|
190
|
+
processor=self.processor,
|
|
191
|
+
lifecycle=self.lifecycle,
|
|
192
|
+
resolver=self.resolver,
|
|
193
|
+
config=self.config
|
|
194
|
+
)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from .network.behavior import Actor
|
|
4
|
+
from .effector import Effector
|
|
5
|
+
from .config import NodeConfig
|
|
6
|
+
from .processor.interface import ProcessorInterface
|
|
7
|
+
from .network.graph import NetworkGraph
|
|
8
|
+
from .identity import NodeIdentity
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NodeLifecycle:
|
|
14
|
+
config: NodeConfig
|
|
15
|
+
graph: NetworkGraph
|
|
16
|
+
processor: ProcessorInterface
|
|
17
|
+
effector: Effector
|
|
18
|
+
actor: Actor
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
config: NodeConfig,
|
|
23
|
+
identity: NodeIdentity,
|
|
24
|
+
graph: NetworkGraph,
|
|
25
|
+
processor: ProcessorInterface,
|
|
26
|
+
effector: Effector,
|
|
27
|
+
actor: Actor,
|
|
28
|
+
use_kobj_processor_thread: bool
|
|
29
|
+
):
|
|
30
|
+
self.config = config
|
|
31
|
+
self.identity = identity
|
|
32
|
+
self.graph = graph
|
|
33
|
+
self.processor = processor
|
|
34
|
+
self.effector = effector
|
|
35
|
+
self.actor = actor
|
|
36
|
+
self.use_kobj_processor_thread = use_kobj_processor_thread
|
|
37
|
+
|
|
38
|
+
def start(self) -> None:
|
|
39
|
+
"""Starts a node, call this method first.
|
|
40
|
+
|
|
41
|
+
Starts the processor thread (if enabled). Loads event queues into memory. Generates network graph from nodes and edges in cache. Processes any state changes of node bundle. Initiates handshake with first contact (if provided) if node doesn't have any neighbors.
|
|
42
|
+
"""
|
|
43
|
+
if self.use_kobj_processor_thread:
|
|
44
|
+
logger.info("Starting processor worker thread")
|
|
45
|
+
self.processor.worker_thread.start()
|
|
46
|
+
|
|
47
|
+
self.graph.generate()
|
|
48
|
+
|
|
49
|
+
# refresh to reflect changes (if any) in config.yaml
|
|
50
|
+
self.effector.deref(self.identity.rid, refresh_cache=True)
|
|
51
|
+
|
|
52
|
+
logger.debug("Waiting for kobj queue to empty")
|
|
53
|
+
if self.use_kobj_processor_thread:
|
|
54
|
+
self.processor.kobj_queue.join()
|
|
55
|
+
else:
|
|
56
|
+
self.processor.flush_kobj_queue()
|
|
57
|
+
logger.debug("Done")
|
|
58
|
+
|
|
59
|
+
if not self.graph.get_neighbors() and self.config.koi_net.first_contact.rid:
|
|
60
|
+
logger.debug(f"I don't have any neighbors, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
|
|
61
|
+
|
|
62
|
+
self.actor.handshake_with(self.config.koi_net.first_contact.rid)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def stop(self):
|
|
66
|
+
"""Stops a node, call this method last.
|
|
67
|
+
|
|
68
|
+
Finishes processing knowledge object queue. Saves event queues to storage.
|
|
69
|
+
"""
|
|
70
|
+
logger.info("Stopping node...")
|
|
71
|
+
|
|
72
|
+
if self.use_kobj_processor_thread:
|
|
73
|
+
logger.info(f"Waiting for kobj queue to empty ({self.processor.kobj_queue.unfinished_tasks} tasks remaining)")
|
|
74
|
+
self.processor.kobj_queue.join()
|
|
75
|
+
else:
|
|
76
|
+
self.processor.flush_kobj_queue()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from rid_lib import RID
|
|
3
|
+
from rid_lib.types import KoiNetNode
|
|
3
4
|
from rid_lib.ext import Manifest, Cache
|
|
4
5
|
from rid_lib.ext.bundle import Bundle
|
|
5
6
|
|
|
@@ -30,13 +31,13 @@ class ResponseHandler:
|
|
|
30
31
|
self.cache = cache
|
|
31
32
|
self.effector = effector
|
|
32
33
|
|
|
33
|
-
def fetch_rids(self, req: FetchRids) -> RidsPayload:
|
|
34
|
+
def fetch_rids(self, req: FetchRids, source: KoiNetNode) -> RidsPayload:
|
|
34
35
|
logger.info(f"Request to fetch rids, allowed types {req.rid_types}")
|
|
35
36
|
rids = self.cache.list_rids(req.rid_types)
|
|
36
37
|
|
|
37
38
|
return RidsPayload(rids=rids)
|
|
38
39
|
|
|
39
|
-
def fetch_manifests(self, req: FetchManifests) -> ManifestsPayload:
|
|
40
|
+
def fetch_manifests(self, req: FetchManifests, source: KoiNetNode) -> ManifestsPayload:
|
|
40
41
|
logger.info(f"Request to fetch manifests, allowed types {req.rid_types}, rids {req.rids}")
|
|
41
42
|
|
|
42
43
|
manifests: list[Manifest] = []
|
|
@@ -51,7 +52,7 @@ class ResponseHandler:
|
|
|
51
52
|
|
|
52
53
|
return ManifestsPayload(manifests=manifests, not_found=not_found)
|
|
53
54
|
|
|
54
|
-
def fetch_bundles(self, req: FetchBundles) -> BundlesPayload:
|
|
55
|
+
def fetch_bundles(self, req: FetchBundles, source: KoiNetNode) -> BundlesPayload:
|
|
55
56
|
logger.info(f"Request to fetch bundles, requested rids {req.rids}")
|
|
56
57
|
|
|
57
58
|
bundles: list[Bundle] = []
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
from .processor.interface import ProcessorInterface
|
|
5
|
+
from .lifecycle import NodeLifecycle
|
|
6
|
+
from .network.resolver import NetworkResolver
|
|
7
|
+
from .config import NodeConfig
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class NodePoller:
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
processor: ProcessorInterface,
|
|
16
|
+
lifecycle: NodeLifecycle,
|
|
17
|
+
resolver: NetworkResolver,
|
|
18
|
+
config: NodeConfig
|
|
19
|
+
):
|
|
20
|
+
self.processor = processor
|
|
21
|
+
self.lifecycle = lifecycle
|
|
22
|
+
self.resolver = resolver
|
|
23
|
+
self.config = config
|
|
24
|
+
|
|
25
|
+
def poll(self):
|
|
26
|
+
neighbors = self.resolver.poll_neighbors()
|
|
27
|
+
for node_rid in neighbors:
|
|
28
|
+
for event in neighbors[node_rid]:
|
|
29
|
+
self.processor.handle(event=event, source=node_rid)
|
|
30
|
+
self.processor.flush_kobj_queue()
|
|
31
|
+
|
|
32
|
+
def run(self):
|
|
33
|
+
try:
|
|
34
|
+
self.lifecycle.start()
|
|
35
|
+
while True:
|
|
36
|
+
start_time = time.time()
|
|
37
|
+
self.poll()
|
|
38
|
+
elapsed = time.time() - start_time
|
|
39
|
+
sleep_time = self.config.koi_net.polling_interval - elapsed
|
|
40
|
+
if sleep_time > 0:
|
|
41
|
+
time.sleep(sleep_time)
|
|
42
|
+
|
|
43
|
+
except KeyboardInterrupt:
|
|
44
|
+
logger.info("Polling interrupted by user.")
|
|
45
|
+
|
|
46
|
+
finally:
|
|
47
|
+
self.lifecycle.stop()
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import uvicorn
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from fastapi import FastAPI, APIRouter
|
|
5
|
+
from fastapi.responses import JSONResponse
|
|
6
|
+
from .network.event_queue import NetworkEventQueue
|
|
7
|
+
from .network.response_handler import ResponseHandler
|
|
8
|
+
from .processor.interface import ProcessorInterface
|
|
9
|
+
from .protocol.api_models import (
|
|
10
|
+
PollEvents,
|
|
11
|
+
FetchRids,
|
|
12
|
+
FetchManifests,
|
|
13
|
+
FetchBundles,
|
|
14
|
+
EventsPayload,
|
|
15
|
+
RidsPayload,
|
|
16
|
+
ManifestsPayload,
|
|
17
|
+
BundlesPayload,
|
|
18
|
+
ErrorResponse
|
|
19
|
+
)
|
|
20
|
+
from .protocol.errors import ProtocolError
|
|
21
|
+
from .protocol.envelope import SignedEnvelope
|
|
22
|
+
from .protocol.consts import (
|
|
23
|
+
BROADCAST_EVENTS_PATH,
|
|
24
|
+
POLL_EVENTS_PATH,
|
|
25
|
+
FETCH_RIDS_PATH,
|
|
26
|
+
FETCH_MANIFESTS_PATH,
|
|
27
|
+
FETCH_BUNDLES_PATH
|
|
28
|
+
)
|
|
29
|
+
from .secure import Secure
|
|
30
|
+
from .lifecycle import NodeLifecycle
|
|
31
|
+
from .config import NodeConfig
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class NodeServer:
|
|
37
|
+
lifecycle: NodeLifecycle
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
config: NodeConfig,
|
|
42
|
+
lifecycle: NodeLifecycle,
|
|
43
|
+
secure: Secure,
|
|
44
|
+
processor: ProcessorInterface,
|
|
45
|
+
event_queue: NetworkEventQueue,
|
|
46
|
+
response_handler: ResponseHandler
|
|
47
|
+
):
|
|
48
|
+
self.config = config
|
|
49
|
+
self.lifecycle = lifecycle
|
|
50
|
+
self.secure = secure
|
|
51
|
+
self.processor = processor
|
|
52
|
+
self.event_queue = event_queue
|
|
53
|
+
self.response_handler = response_handler
|
|
54
|
+
self._build_app()
|
|
55
|
+
|
|
56
|
+
def _build_app(self):
|
|
57
|
+
self.app = FastAPI(
|
|
58
|
+
lifespan=self.lifespan,
|
|
59
|
+
title="KOI-net Protocol API",
|
|
60
|
+
version="1.0.0"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.router = APIRouter(prefix="/koi-net")
|
|
64
|
+
self.app.add_exception_handler(ProtocolError, self.protocol_error_handler)
|
|
65
|
+
|
|
66
|
+
def _add_endpoint(path, func):
|
|
67
|
+
self.router.add_api_route(
|
|
68
|
+
path=path,
|
|
69
|
+
endpoint=self.secure.envelope_handler(func),
|
|
70
|
+
methods=["POST"]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
_add_endpoint(BROADCAST_EVENTS_PATH, self.broadcast_events)
|
|
74
|
+
_add_endpoint(POLL_EVENTS_PATH, self.poll_events)
|
|
75
|
+
_add_endpoint(FETCH_RIDS_PATH, self.fetch_rids)
|
|
76
|
+
_add_endpoint(FETCH_MANIFESTS_PATH, self.fetch_manifests)
|
|
77
|
+
_add_endpoint(FETCH_BUNDLES_PATH, self.fetch_bundles)
|
|
78
|
+
|
|
79
|
+
self.app.include_router(self.router)
|
|
80
|
+
|
|
81
|
+
def run(self):
|
|
82
|
+
uvicorn.run(
|
|
83
|
+
app=self.app,
|
|
84
|
+
host=self.config.server.host,
|
|
85
|
+
port=self.config.server.port
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
@asynccontextmanager
|
|
89
|
+
async def lifespan(self, app: FastAPI):
|
|
90
|
+
self.lifecycle.start()
|
|
91
|
+
yield
|
|
92
|
+
self.lifecycle.stop()
|
|
93
|
+
|
|
94
|
+
def protocol_error_handler(self, request, exc: ProtocolError):
|
|
95
|
+
logger.info(f"caught protocol error: {exc}")
|
|
96
|
+
resp = ErrorResponse(error=exc.error_type)
|
|
97
|
+
logger.info(f"returning error response: {resp}")
|
|
98
|
+
return JSONResponse(
|
|
99
|
+
status_code=400,
|
|
100
|
+
content=resp.model_dump(mode="json")
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
async def broadcast_events(self, req: SignedEnvelope[EventsPayload]):
|
|
104
|
+
logger.info(f"Request to {BROADCAST_EVENTS_PATH}, received {len(req.payload.events)} event(s)")
|
|
105
|
+
for event in req.payload.events:
|
|
106
|
+
self.processor.handle(event=event, source=req.source_node)
|
|
107
|
+
|
|
108
|
+
async def poll_events(
|
|
109
|
+
self, req: SignedEnvelope[PollEvents]
|
|
110
|
+
) -> SignedEnvelope[EventsPayload] | ErrorResponse:
|
|
111
|
+
logger.info(f"Request to {POLL_EVENTS_PATH}")
|
|
112
|
+
events = self.event_queue.flush_poll_queue(req.source_node)
|
|
113
|
+
return EventsPayload(events=events)
|
|
114
|
+
|
|
115
|
+
async def fetch_rids(
|
|
116
|
+
self, req: SignedEnvelope[FetchRids]
|
|
117
|
+
) -> SignedEnvelope[RidsPayload] | ErrorResponse:
|
|
118
|
+
return self.response_handler.fetch_rids(req.payload, req.source_node)
|
|
119
|
+
|
|
120
|
+
async def fetch_manifests(
|
|
121
|
+
self, req: SignedEnvelope[FetchManifests]
|
|
122
|
+
) -> SignedEnvelope[ManifestsPayload] | ErrorResponse:
|
|
123
|
+
return self.response_handler.fetch_manifests(req.payload, req.source_node)
|
|
124
|
+
|
|
125
|
+
async def fetch_bundles(
|
|
126
|
+
self, req: SignedEnvelope[FetchBundles]
|
|
127
|
+
) -> SignedEnvelope[BundlesPayload] | ErrorResponse:
|
|
128
|
+
return self.response_handler.fetch_bundles(req.payload, req.source_node)
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
import uvicorn
|
|
5
|
-
from contextlib import asynccontextmanager
|
|
6
|
-
from rich.logging import RichHandler
|
|
7
|
-
from fastapi import FastAPI
|
|
8
|
-
from rid_lib.types import KoiNetNode, KoiNetEdge
|
|
9
|
-
from koi_net import NodeInterface
|
|
10
|
-
from koi_net.config import NodeConfig, KoiNetConfig
|
|
11
|
-
from koi_net.protocol.node import NodeProfile, NodeType, NodeProvides
|
|
12
|
-
from koi_net.protocol.api_models import (
|
|
13
|
-
PollEvents,
|
|
14
|
-
FetchRids,
|
|
15
|
-
FetchManifests,
|
|
16
|
-
FetchBundles,
|
|
17
|
-
EventsPayload,
|
|
18
|
-
RidsPayload,
|
|
19
|
-
ManifestsPayload,
|
|
20
|
-
BundlesPayload
|
|
21
|
-
)
|
|
22
|
-
from koi_net.protocol.consts import (
|
|
23
|
-
BROADCAST_EVENTS_PATH,
|
|
24
|
-
POLL_EVENTS_PATH,
|
|
25
|
-
FETCH_RIDS_PATH,
|
|
26
|
-
FETCH_MANIFESTS_PATH,
|
|
27
|
-
FETCH_BUNDLES_PATH
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
logging.basicConfig(
|
|
32
|
-
level=logging.INFO,
|
|
33
|
-
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
34
|
-
datefmt="%Y-%m-%d %H:%M:%S",
|
|
35
|
-
handlers=[RichHandler()]
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
# TODO: UPDATE OR REMOVE, THIS EXAMPLE IS OUT OF DATE
|
|
39
|
-
|
|
40
|
-
logging.getLogger("koi_net").setLevel(logging.DEBUG)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class CoordinatorNodeConfig(NodeConfig):
|
|
44
|
-
koi_net: KoiNetConfig | None = Field(default_factory = lambda:
|
|
45
|
-
KoiNetConfig(
|
|
46
|
-
node_name="coordinator",
|
|
47
|
-
node_profile=NodeProfile(
|
|
48
|
-
node_type=NodeType.FULL,
|
|
49
|
-
provides=NodeProvides(
|
|
50
|
-
event=[KoiNetNode, KoiNetEdge],
|
|
51
|
-
state=[KoiNetNode, KoiNetEdge]
|
|
52
|
-
)
|
|
53
|
-
),
|
|
54
|
-
cache_directory_path=".coordinator_rid_cache",
|
|
55
|
-
event_queues_path="coordinator_event_queues.json"
|
|
56
|
-
)
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
node = NodeInterface(
|
|
60
|
-
config=CoordinatorNodeConfig.load_from_yaml("coordinator_config.yaml"),
|
|
61
|
-
use_kobj_processor_thread=True
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
logger = logging.getLogger(__name__)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@asynccontextmanager
|
|
69
|
-
async def lifespan(app: FastAPI):
|
|
70
|
-
node.start()
|
|
71
|
-
yield
|
|
72
|
-
node.stop()
|
|
73
|
-
|
|
74
|
-
app = FastAPI(
|
|
75
|
-
lifespan=lifespan,
|
|
76
|
-
root_path="/koi-net",
|
|
77
|
-
title="KOI-net Protocol API",
|
|
78
|
-
version="1.0.0"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
@app.post(BROADCAST_EVENTS_PATH)
|
|
82
|
-
def broadcast_events(req: EventsPayload):
|
|
83
|
-
logger.info(f"Request to {BROADCAST_EVENTS_PATH}, received {len(req.events)} event(s)")
|
|
84
|
-
for event in req.events:
|
|
85
|
-
node.processor.handle(event=event, source=KnowledgeSource.External)
|
|
86
|
-
|
|
87
|
-
@app.post(POLL_EVENTS_PATH)
|
|
88
|
-
def poll_events(req: PollEvents) -> EventsPayload:
|
|
89
|
-
logger.info(f"Request to {POLL_EVENTS_PATH}")
|
|
90
|
-
events = node.event_queue.flush_poll_queue(req.rid)
|
|
91
|
-
return EventsPayload(events=events)
|
|
92
|
-
|
|
93
|
-
@app.post(FETCH_RIDS_PATH)
|
|
94
|
-
def fetch_rids(req: FetchRids) -> RidsPayload:
|
|
95
|
-
return node.response_handler.fetch_rids(req)
|
|
96
|
-
|
|
97
|
-
@app.post(FETCH_MANIFESTS_PATH)
|
|
98
|
-
def fetch_manifests(req: FetchManifests) -> ManifestsPayload:
|
|
99
|
-
return node.response_handler.fetch_manifests(req)
|
|
100
|
-
|
|
101
|
-
@app.post(FETCH_BUNDLES_PATH)
|
|
102
|
-
def fetch_bundles(req: FetchBundles) -> BundlesPayload:
|
|
103
|
-
return node.response_handler.fetch_bundles(req)
|
|
104
|
-
|
|
105
|
-
if __name__ == "__main__":
|
|
106
|
-
openapi_spec = app.openapi()
|
|
107
|
-
|
|
108
|
-
with open("koi-net-protocol-openapi.json", "w") as f:
|
|
109
|
-
json.dump(openapi_spec, f, indent=2)
|
|
110
|
-
|
|
111
|
-
uvicorn.run("examples.basic_coordinator_node:app", port=node.config.server.port)
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import httpx
|
|
3
|
-
from rid_lib.ext import Cache
|
|
4
|
-
from .network.resolver import NetworkResolver
|
|
5
|
-
from .network.event_queue import NetworkEventQueue
|
|
6
|
-
from .network.graph import NetworkGraph
|
|
7
|
-
from .network.request_handler import RequestHandler
|
|
8
|
-
from .network.response_handler import ResponseHandler
|
|
9
|
-
from .network.error_handler import ErrorHandler
|
|
10
|
-
from .network.behavior import Actor
|
|
11
|
-
from .processor.interface import ProcessorInterface
|
|
12
|
-
from .processor import default_handlers
|
|
13
|
-
from .processor.handler import KnowledgeHandler
|
|
14
|
-
from .processor.knowledge_pipeline import KnowledgePipeline
|
|
15
|
-
from .identity import NodeIdentity
|
|
16
|
-
from .secure import Secure
|
|
17
|
-
from .config import NodeConfig
|
|
18
|
-
from .context import HandlerContext, ActionContext
|
|
19
|
-
from .effector import Effector
|
|
20
|
-
from . import default_actions
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class NodeInterface:
|
|
27
|
-
config: NodeConfig
|
|
28
|
-
cache: Cache
|
|
29
|
-
identity: NodeIdentity
|
|
30
|
-
resolver: NetworkResolver
|
|
31
|
-
event_queue: NetworkEventQueue
|
|
32
|
-
graph: NetworkGraph
|
|
33
|
-
processor: ProcessorInterface
|
|
34
|
-
secure: Secure
|
|
35
|
-
|
|
36
|
-
use_kobj_processor_thread: bool
|
|
37
|
-
|
|
38
|
-
def __init__(
|
|
39
|
-
self,
|
|
40
|
-
config: NodeConfig,
|
|
41
|
-
use_kobj_processor_thread: bool = False,
|
|
42
|
-
|
|
43
|
-
handlers: list[KnowledgeHandler] | None = None,
|
|
44
|
-
|
|
45
|
-
cache: Cache | None = None,
|
|
46
|
-
processor: ProcessorInterface | None = None
|
|
47
|
-
):
|
|
48
|
-
self.config = config
|
|
49
|
-
self.cache = cache or Cache(
|
|
50
|
-
directory_path=self.config.koi_net.cache_directory_path
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
self.identity = NodeIdentity(config=self.config)
|
|
54
|
-
|
|
55
|
-
self.effector = Effector(cache=self.cache)
|
|
56
|
-
|
|
57
|
-
self.graph = NetworkGraph(
|
|
58
|
-
cache=self.cache,
|
|
59
|
-
identity=self.identity
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
self.secure = Secure(
|
|
63
|
-
identity=self.identity,
|
|
64
|
-
effector=self.effector,
|
|
65
|
-
config=self.config
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
self.request_handler = RequestHandler(
|
|
69
|
-
effector=self.effector,
|
|
70
|
-
identity=self.identity,
|
|
71
|
-
secure=self.secure
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
self.response_handler = ResponseHandler(self.cache, self.effector)
|
|
75
|
-
|
|
76
|
-
self.resolver = NetworkResolver(
|
|
77
|
-
config=self.config,
|
|
78
|
-
cache=self.cache,
|
|
79
|
-
identity=self.identity,
|
|
80
|
-
graph=self.graph,
|
|
81
|
-
request_handler=self.request_handler,
|
|
82
|
-
effector=self.effector
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
self.event_queue = NetworkEventQueue(
|
|
86
|
-
config=self.config,
|
|
87
|
-
cache=self.cache,
|
|
88
|
-
identity=self.identity,
|
|
89
|
-
graph=self.graph,
|
|
90
|
-
request_handler=self.request_handler,
|
|
91
|
-
effector=self.effector
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
self.actor = Actor(
|
|
95
|
-
identity=self.identity,
|
|
96
|
-
effector=self.effector,
|
|
97
|
-
event_queue=self.event_queue
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
# pull all handlers defined in default_handlers module
|
|
101
|
-
if handlers is None:
|
|
102
|
-
handlers = [
|
|
103
|
-
obj for obj in vars(default_handlers).values()
|
|
104
|
-
if isinstance(obj, KnowledgeHandler)
|
|
105
|
-
]
|
|
106
|
-
|
|
107
|
-
self.use_kobj_processor_thread = use_kobj_processor_thread
|
|
108
|
-
|
|
109
|
-
self.action_context = ActionContext(
|
|
110
|
-
identity=self.identity,
|
|
111
|
-
effector=self.effector
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
self.handler_context = HandlerContext(
|
|
115
|
-
identity=self.identity,
|
|
116
|
-
cache=self.cache,
|
|
117
|
-
event_queue=self.event_queue,
|
|
118
|
-
graph=self.graph,
|
|
119
|
-
request_handler=self.request_handler,
|
|
120
|
-
effector=self.effector
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
self.pipeline = KnowledgePipeline(
|
|
124
|
-
handler_context=self.handler_context,
|
|
125
|
-
cache=self.cache,
|
|
126
|
-
request_handler=self.request_handler,
|
|
127
|
-
event_queue=self.event_queue,
|
|
128
|
-
graph=self.graph,
|
|
129
|
-
default_handlers=handlers
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
self.processor = processor or ProcessorInterface(
|
|
133
|
-
pipeline=self.pipeline,
|
|
134
|
-
use_kobj_processor_thread=self.use_kobj_processor_thread
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
self.error_handler = ErrorHandler(
|
|
138
|
-
processor=self.processor,
|
|
139
|
-
actor=self.actor
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
self.request_handler.set_error_handler(self.error_handler)
|
|
143
|
-
|
|
144
|
-
self.handler_context.set_processor(self.processor)
|
|
145
|
-
|
|
146
|
-
self.effector.set_processor(self.processor)
|
|
147
|
-
self.effector.set_resolver(self.resolver)
|
|
148
|
-
self.effector.set_action_context(self.action_context)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def start(self) -> None:
|
|
152
|
-
"""Starts a node, call this method first.
|
|
153
|
-
|
|
154
|
-
Starts the processor thread (if enabled). Loads event queues into memory. Generates network graph from nodes and edges in cache. Processes any state changes of node bundle. Initiates handshake with first contact (if provided) if node doesn't have any neighbors.
|
|
155
|
-
"""
|
|
156
|
-
if self.use_kobj_processor_thread:
|
|
157
|
-
logger.info("Starting processor worker thread")
|
|
158
|
-
self.processor.worker_thread.start()
|
|
159
|
-
|
|
160
|
-
# self.network._load_event_queues()
|
|
161
|
-
self.graph.generate()
|
|
162
|
-
|
|
163
|
-
# refresh to reflect changes (if any) in config.yaml
|
|
164
|
-
self.effector.deref(self.identity.rid, refresh_cache=True)
|
|
165
|
-
|
|
166
|
-
logger.debug("Waiting for kobj queue to empty")
|
|
167
|
-
if self.use_kobj_processor_thread:
|
|
168
|
-
self.processor.kobj_queue.join()
|
|
169
|
-
else:
|
|
170
|
-
self.processor.flush_kobj_queue()
|
|
171
|
-
logger.debug("Done")
|
|
172
|
-
|
|
173
|
-
if not self.graph.get_neighbors() and self.config.koi_net.first_contact.rid:
|
|
174
|
-
logger.debug(f"I don't have any neighbors, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
|
|
175
|
-
|
|
176
|
-
self.actor.handshake_with(self.config.koi_net.first_contact.rid)
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def stop(self):
|
|
180
|
-
"""Stops a node, call this method last.
|
|
181
|
-
|
|
182
|
-
Finishes processing knowledge object queue. Saves event queues to storage.
|
|
183
|
-
"""
|
|
184
|
-
logger.info("Stopping node...")
|
|
185
|
-
|
|
186
|
-
if self.use_kobj_processor_thread:
|
|
187
|
-
logger.info(f"Waiting for kobj queue to empty ({self.processor.kobj_queue.unfinished_tasks} tasks remaining)")
|
|
188
|
-
self.processor.kobj_queue.join()
|
|
189
|
-
else:
|
|
190
|
-
self.processor.flush_kobj_queue()
|
|
191
|
-
|
|
192
|
-
# self.network._save_event_queues()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|