koi-net 1.2.0b2__tar.gz → 1.2.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.2.0b2 → koi_net-1.2.0b3}/PKG-INFO +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/examples/coordinator.py +27 -32
- koi_net-1.2.0b3/examples/partial.py +30 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/pyproject.toml +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/__init__.py +0 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/assembler.py +20 -7
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/cli/models.py +2 -2
- koi_net-1.2.0b3/src/koi_net/config/core.py +71 -0
- koi_net-1.2.0b3/src/koi_net/config/full_node.py +31 -0
- koi_net-1.2.0b3/src/koi_net/config/loader.py +46 -0
- koi_net-1.2.0b3/src/koi_net/config/partial_node.py +18 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/core.py +15 -23
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/default_actions.py +1 -2
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/effector.py +1 -1
- koi_net-1.2.0b3/src/koi_net/entrypoints/__init__.py +2 -0
- koi_net-1.2.0b3/src/koi_net/entrypoints/base.py +5 -0
- {koi_net-1.2.0b2/src/koi_net → koi_net-1.2.0b3/src/koi_net/entrypoints}/poller.py +9 -9
- {koi_net-1.2.0b2/src/koi_net → koi_net-1.2.0b3/src/koi_net/entrypoints}/server.py +9 -10
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/handshaker.py +2 -2
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/identity.py +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/lifecycle.py +6 -6
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/error_handler.py +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/event_queue.py +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/poll_event_buffer.py +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/resolver.py +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/response_handler.py +1 -1
- {koi_net-1.2.0b2/src/koi_net → koi_net-1.2.0b3/src/koi_net/processor}/context.py +6 -6
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/processor/handler.py +4 -1
- koi_net-1.2.0b2/src/koi_net/processor/handlers.py → koi_net-1.2.0b3/src/koi_net/processor/knowledge_handlers.py +6 -6
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/processor/knowledge_object.py +2 -3
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/processor/kobj_queue.py +1 -1
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/processor/pipeline.py +2 -2
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/node.py +3 -3
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/secure.py +6 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/secure.py +1 -2
- koi_net-1.2.0b3/src/koi_net/workers/__init__.py +2 -0
- {koi_net-1.2.0b2/src/koi_net/processor → koi_net-1.2.0b3/src/koi_net/workers}/event_worker.py +8 -8
- {koi_net-1.2.0b2/src/koi_net/processor → koi_net-1.2.0b3/src/koi_net/workers}/kobj_worker.py +3 -3
- koi_net-1.2.0b2/examples/partial.py +0 -35
- koi_net-1.2.0b2/src/koi_net/config.py +0 -161
- koi_net-1.2.0b2/src/koi_net/interfaces/entrypoint.py +0 -5
- koi_net-1.2.0b2/src/koi_net/sentry.py +0 -13
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/.github/workflows/publish-to-pypi.yml +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/.gitignore +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/LICENSE +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/README.md +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/koi-net-protocol-openapi.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/requirements.txt +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/bundle.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/bundles_payload.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/edge_profile.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/error_response.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/event.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/events_payload.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/fetch_bundles.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/fetch_manifests.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/fetch_rids.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/manifest.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/manifests_payload.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/node_profile.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/poll_events.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/rids_payload.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/signed_envelope.schema.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/schemas/unsigned_envelope.json +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/cli/__init__.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/cli/commands.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/logger.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/__init__.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/graph.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/network/request_handler.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/processor/__init__.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/__init__.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/api_models.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/consts.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/edge.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/envelope.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/errors.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/event.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/protocol/model_map.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/src/koi_net/utils.py +0 -0
- /koi_net-1.2.0b2/src/koi_net/interfaces/worker.py → /koi_net-1.2.0b3/src/koi_net/workers/base.py +0 -0
- {koi_net-1.2.0b2 → koi_net-1.2.0b3}/uv.lock +0 -0
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from rich.logging import RichHandler
|
|
3
|
-
from pydantic import Field
|
|
4
3
|
from rid_lib.types import KoiNetNode, KoiNetEdge
|
|
5
|
-
from koi_net.config import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
from koi_net.config.full_node import (
|
|
5
|
+
FullNodeConfig,
|
|
6
|
+
ServerConfig,
|
|
7
|
+
KoiNetConfig,
|
|
8
|
+
NodeProfile,
|
|
9
|
+
NodeProvides
|
|
10
|
+
)
|
|
11
|
+
from koi_net.core import FullNode
|
|
12
|
+
from koi_net.processor.context import HandlerContext
|
|
9
13
|
from koi_net.processor.handler import HandlerType, KnowledgeHandler
|
|
10
14
|
from koi_net.processor.knowledge_object import KnowledgeObject
|
|
11
15
|
from koi_net.protocol.event import Event, EventType
|
|
@@ -22,22 +26,17 @@ logging.getLogger("koi_net").setLevel(logging.DEBUG)
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
24
28
|
|
|
25
|
-
class CoordinatorConfig(
|
|
26
|
-
server: ServerConfig =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
state=[KoiNetNode, KoiNetEdge]
|
|
37
|
-
)
|
|
38
|
-
),
|
|
39
|
-
rid_types_of_interest=[KoiNetNode, KoiNetEdge]
|
|
40
|
-
)
|
|
29
|
+
class CoordinatorConfig(FullNodeConfig):
|
|
30
|
+
server: ServerConfig = ServerConfig(port=8080)
|
|
31
|
+
koi_net: KoiNetConfig = KoiNetConfig(
|
|
32
|
+
node_name="coordinator",
|
|
33
|
+
node_profile=NodeProfile(
|
|
34
|
+
provides=NodeProvides(
|
|
35
|
+
event=[KoiNetNode, KoiNetEdge],
|
|
36
|
+
state=[KoiNetNode, KoiNetEdge]
|
|
37
|
+
)
|
|
38
|
+
),
|
|
39
|
+
rid_types_of_interest=[KoiNetNode, KoiNetEdge]
|
|
41
40
|
)
|
|
42
41
|
|
|
43
42
|
@KnowledgeHandler.create(
|
|
@@ -52,7 +51,7 @@ def handshake_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
52
51
|
|
|
53
52
|
logger.info("Sharing this node's bundle with peer")
|
|
54
53
|
identity_bundle = ctx.cache.read(ctx.identity.rid)
|
|
55
|
-
ctx.event_queue.
|
|
54
|
+
ctx.event_queue.push(
|
|
56
55
|
event=Event.from_bundle(EventType.NEW, identity_bundle),
|
|
57
56
|
target=kobj.rid
|
|
58
57
|
)
|
|
@@ -67,17 +66,13 @@ def handshake_handler(ctx: HandlerContext, kobj: KnowledgeObject):
|
|
|
67
66
|
rid_types=[KoiNetNode, KoiNetEdge]
|
|
68
67
|
)
|
|
69
68
|
|
|
70
|
-
ctx.kobj_queue.
|
|
71
|
-
ctx.kobj_queue.
|
|
69
|
+
ctx.kobj_queue.push(rid=edge_bundle.rid, event_type=EventType.FORGET)
|
|
70
|
+
ctx.kobj_queue.push(bundle=edge_bundle)
|
|
72
71
|
|
|
73
|
-
class
|
|
72
|
+
class CoordinatorNode(FullNode):
|
|
74
73
|
config = CoordinatorConfig
|
|
75
|
-
knowledge_handlers = [
|
|
76
|
-
*NodeAssembler.knowledge_handlers,
|
|
77
|
-
handshake_handler
|
|
78
|
-
]
|
|
79
|
-
|
|
74
|
+
knowledge_handlers = FullNode.knowledge_handlers + [handshake_handler]
|
|
80
75
|
|
|
81
76
|
if __name__ == "__main__":
|
|
82
|
-
node =
|
|
83
|
-
node.
|
|
77
|
+
node = CoordinatorNode()
|
|
78
|
+
node.entrypoint.run()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from rich.logging import RichHandler
|
|
3
|
+
from koi_net.config.partial_node import PartialNodeConfig, KoiNetConfig, NodeProfile
|
|
4
|
+
from koi_net.core import PartialNode
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
logging.basicConfig(
|
|
8
|
+
level=logging.INFO,
|
|
9
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
10
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
11
|
+
handlers=[RichHandler()]
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
logging.getLogger("koi_net").setLevel(logging.DEBUG)
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MyPartialNodeConfig(PartialNodeConfig):
|
|
19
|
+
koi_net: KoiNetConfig = KoiNetConfig(
|
|
20
|
+
node_name="partial",
|
|
21
|
+
node_profile=NodeProfile()
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
class MyPartialNode(PartialNode):
|
|
25
|
+
config_cls = MyPartialNodeConfig
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
node = MyPartialNode()
|
|
30
|
+
# node.entrypoint.run()
|
|
@@ -2,9 +2,10 @@ import inspect
|
|
|
2
2
|
from typing import Protocol
|
|
3
3
|
from dataclasses import make_dataclass
|
|
4
4
|
|
|
5
|
+
from pydantic import BaseModel
|
|
5
6
|
import structlog
|
|
6
7
|
|
|
7
|
-
from .
|
|
8
|
+
from .entrypoints.base import EntryPoint
|
|
8
9
|
|
|
9
10
|
log = structlog.stdlib.get_logger()
|
|
10
11
|
|
|
@@ -41,13 +42,25 @@ class NodeAssembler(metaclass=BuildOrderer):
|
|
|
41
42
|
@classmethod
|
|
42
43
|
def _build(cls) -> NodeContainer:
|
|
43
44
|
components = {}
|
|
44
|
-
for comp_name in cls._build_order:
|
|
45
|
-
|
|
45
|
+
for comp_name in cls._build_order:
|
|
46
|
+
comp = getattr(cls, comp_name, None)
|
|
46
47
|
|
|
47
|
-
if
|
|
48
|
+
if comp is None:
|
|
48
49
|
raise Exception(f"Couldn't find factory for component '{comp_name}'")
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
print(comp_name)
|
|
52
|
+
|
|
53
|
+
if not callable(comp):
|
|
54
|
+
print(f"Treating {comp_name} as a literal")
|
|
55
|
+
components[comp_name] = comp
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
if issubclass(comp, BaseModel):
|
|
59
|
+
print(f"Treating {comp_name} as a pydantic model")
|
|
60
|
+
components[comp_name] = comp
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
sig = inspect.signature(comp)
|
|
51
64
|
|
|
52
65
|
required_comps = []
|
|
53
66
|
for name, param in sig.parameters.items():
|
|
@@ -58,7 +71,7 @@ class NodeAssembler(metaclass=BuildOrderer):
|
|
|
58
71
|
else:
|
|
59
72
|
s = f"{comp_name} -> {', '.join([name for name, _type in required_comps])}"
|
|
60
73
|
|
|
61
|
-
print(s.replace("graph", "_graph"), end=";\n")
|
|
74
|
+
# print(s.replace("graph", "_graph"), end=";\n")
|
|
62
75
|
|
|
63
76
|
dependencies = {}
|
|
64
77
|
for req_comp_name, req_comp_type in required_comps:
|
|
@@ -67,7 +80,7 @@ class NodeAssembler(metaclass=BuildOrderer):
|
|
|
67
80
|
|
|
68
81
|
dependencies[req_comp_name] = components[req_comp_name]
|
|
69
82
|
|
|
70
|
-
components[comp_name] =
|
|
83
|
+
components[comp_name] = comp(**dependencies)
|
|
71
84
|
|
|
72
85
|
NodeContainer = make_dataclass(
|
|
73
86
|
cls_name="NodeContainer",
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from pydantic import BaseModel,
|
|
1
|
+
from pydantic import BaseModel, PrivateAttr
|
|
2
2
|
from ruamel.yaml import YAML
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class KoiNetworkConfig(BaseModel):
|
|
6
|
-
nodes: dict[str, str] =
|
|
6
|
+
nodes: dict[str, str] = {}
|
|
7
7
|
_file_path: str = PrivateAttr(default="koi-net-config.yaml")
|
|
8
8
|
|
|
9
9
|
@classmethod
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pydantic import BaseModel, model_validator
|
|
3
|
+
from dotenv import load_dotenv
|
|
4
|
+
from rid_lib import RIDType
|
|
5
|
+
from rid_lib.types import KoiNetNode
|
|
6
|
+
|
|
7
|
+
from koi_net.protocol.secure import PrivateKey
|
|
8
|
+
from ..protocol.node import NodeProfile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class NodeContact(BaseModel):
|
|
12
|
+
rid: KoiNetNode | None = None
|
|
13
|
+
url: str | None = None
|
|
14
|
+
|
|
15
|
+
class KoiNetConfig(BaseModel):
|
|
16
|
+
"""Config for KOI-net."""
|
|
17
|
+
|
|
18
|
+
node_name: str
|
|
19
|
+
node_rid: KoiNetNode | None = None
|
|
20
|
+
node_profile: NodeProfile
|
|
21
|
+
|
|
22
|
+
rid_types_of_interest: list[RIDType] = [KoiNetNode]
|
|
23
|
+
|
|
24
|
+
cache_directory_path: str = ".rid_cache"
|
|
25
|
+
event_queues_path: str = "event_queues.json"
|
|
26
|
+
private_key_pem_path: str = "priv_key.pem"
|
|
27
|
+
|
|
28
|
+
first_contact: NodeContact = NodeContact()
|
|
29
|
+
|
|
30
|
+
class EnvConfig(BaseModel):
|
|
31
|
+
"""Config for environment variables.
|
|
32
|
+
|
|
33
|
+
Values set in the config are the variables names, and are loaded
|
|
34
|
+
from the environment at runtime. For example, if the config YAML
|
|
35
|
+
sets `priv_key_password: PRIV_KEY_PASSWORD` accessing
|
|
36
|
+
`priv_key_password` would retrieve the value of `PRIV_KEY_PASSWORD`
|
|
37
|
+
from the environment.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
priv_key_password: str = "PRIV_KEY_PASSWORD"
|
|
41
|
+
|
|
42
|
+
def __init__(self, **kwargs):
|
|
43
|
+
super().__init__(**kwargs)
|
|
44
|
+
load_dotenv()
|
|
45
|
+
|
|
46
|
+
def __getattribute__(self, name):
|
|
47
|
+
value = super().__getattribute__(name)
|
|
48
|
+
if name in type(self).model_fields:
|
|
49
|
+
env_val = os.getenv(value)
|
|
50
|
+
if env_val is None:
|
|
51
|
+
raise ValueError(f"Required environment variable {value} not set")
|
|
52
|
+
return env_val
|
|
53
|
+
return value
|
|
54
|
+
|
|
55
|
+
class NodeConfig(BaseModel):
|
|
56
|
+
koi_net: KoiNetConfig
|
|
57
|
+
env: EnvConfig = EnvConfig()
|
|
58
|
+
|
|
59
|
+
@model_validator(mode="after")
|
|
60
|
+
def generate_rid_cascade(self):
|
|
61
|
+
if not self.koi_net.node_rid:
|
|
62
|
+
priv_key = PrivateKey.generate()
|
|
63
|
+
pub_key = priv_key.public_key()
|
|
64
|
+
|
|
65
|
+
self.koi_net.node_rid = pub_key.to_node_rid(self.koi_net.node_name)
|
|
66
|
+
|
|
67
|
+
with open(self.koi_net.private_key_pem_path, "w") as f:
|
|
68
|
+
f.write(priv_key.to_pem(self.env.priv_key_password))
|
|
69
|
+
|
|
70
|
+
self.koi_net.node_profile.public_key = pub_key.to_der()
|
|
71
|
+
return self
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from pydantic import BaseModel, model_validator
|
|
2
|
+
from koi_net.config.core import NodeConfig, KoiNetConfig as BaseKoiNetConfig
|
|
3
|
+
from ..protocol.node import NodeProfile as BaseNodeProfile, NodeType, NodeProvides
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NodeProfile(BaseNodeProfile):
|
|
7
|
+
node_type: NodeType = NodeType.FULL
|
|
8
|
+
|
|
9
|
+
class KoiNetConfig(BaseKoiNetConfig):
|
|
10
|
+
node_profile: NodeProfile
|
|
11
|
+
|
|
12
|
+
class ServerConfig(BaseModel):
|
|
13
|
+
"""Config for the node server (full node only)."""
|
|
14
|
+
|
|
15
|
+
host: str = "127.0.0.1"
|
|
16
|
+
port: int = 8000
|
|
17
|
+
path: str | None = "/koi-net"
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def url(self) -> str:
|
|
21
|
+
return f"http://{self.host}:{self.port}{self.path or ''}"
|
|
22
|
+
|
|
23
|
+
class FullNodeConfig(NodeConfig):
|
|
24
|
+
koi_net: KoiNetConfig
|
|
25
|
+
server: ServerConfig = ServerConfig()
|
|
26
|
+
|
|
27
|
+
@model_validator(mode="after")
|
|
28
|
+
def check_url(self):
|
|
29
|
+
if not self.koi_net.node_profile.base_url:
|
|
30
|
+
self.koi_net.node_profile.base_url = self.server.url
|
|
31
|
+
return self
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from ruamel.yaml import YAML
|
|
2
|
+
from .core import NodeConfig
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ConfigLoader:
|
|
6
|
+
_config: NodeConfig
|
|
7
|
+
|
|
8
|
+
_file_path: str = "config.yaml"
|
|
9
|
+
_file_content: str
|
|
10
|
+
|
|
11
|
+
def __init__(self, config_cls: type[NodeConfig]):
|
|
12
|
+
self._config_cls = config_cls
|
|
13
|
+
self.load_from_yaml()
|
|
14
|
+
|
|
15
|
+
def __getattr__(self, name):
|
|
16
|
+
return getattr(self._config, name)
|
|
17
|
+
|
|
18
|
+
def load_from_yaml(self):
|
|
19
|
+
"""Loads config state from YAML file."""
|
|
20
|
+
yaml = YAML()
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
with open(self._file_path, "r") as f:
|
|
24
|
+
self._file_content = f.read()
|
|
25
|
+
config_data = yaml.load(self._file_content)
|
|
26
|
+
self._config = self._config_cls.model_validate(config_data)
|
|
27
|
+
|
|
28
|
+
except FileNotFoundError:
|
|
29
|
+
self._config = self._config_cls()
|
|
30
|
+
|
|
31
|
+
self.save_to_yaml()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def save_to_yaml(self):
|
|
35
|
+
yaml = YAML()
|
|
36
|
+
|
|
37
|
+
with open(self._file_path, "w") as f:
|
|
38
|
+
try:
|
|
39
|
+
config_data = self._config.model_dump(mode="json")
|
|
40
|
+
yaml.dump(config_data, f)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
if self._file_content:
|
|
43
|
+
f.seek(0)
|
|
44
|
+
f.truncate()
|
|
45
|
+
f.write(self._file_content)
|
|
46
|
+
raise e
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from koi_net.config.core import NodeConfig, KoiNetConfig
|
|
3
|
+
from ..protocol.node import NodeProfile, NodeType, NodeProvides
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NodeProfile(NodeProfile):
|
|
7
|
+
base_url: str | None = None
|
|
8
|
+
node_type: NodeType = NodeType.PARTIAL
|
|
9
|
+
|
|
10
|
+
class KoiNetConfig(KoiNetConfig):
|
|
11
|
+
node_profile: NodeProfile
|
|
12
|
+
|
|
13
|
+
class PollerConfig(BaseModel):
|
|
14
|
+
polling_interval: int = 5
|
|
15
|
+
|
|
16
|
+
class PartialNodeConfig(NodeConfig):
|
|
17
|
+
koi_net: KoiNetConfig
|
|
18
|
+
poller: PollerConfig = PollerConfig()
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from rid_lib.ext import Cache
|
|
2
|
+
|
|
3
|
+
from .config.loader import ConfigLoader
|
|
2
4
|
from .assembler import NodeAssembler
|
|
3
|
-
from .config.
|
|
4
|
-
from .context import HandlerContext
|
|
5
|
+
from .config.core import NodeConfig
|
|
6
|
+
from .processor.context import HandlerContext
|
|
5
7
|
from .effector import Effector
|
|
6
8
|
from .handshaker import Handshaker
|
|
7
9
|
from .identity import NodeIdentity
|
|
8
|
-
from .
|
|
10
|
+
from .workers import KnowledgeProcessingWorker, EventProcessingWorker
|
|
9
11
|
from .lifecycle import NodeLifecycle
|
|
10
12
|
from .network.error_handler import ErrorHandler
|
|
11
13
|
from .network.event_queue import EventQueue
|
|
@@ -14,8 +16,11 @@ from .network.request_handler import RequestHandler
|
|
|
14
16
|
from .network.resolver import NetworkResolver
|
|
15
17
|
from .network.response_handler import ResponseHandler
|
|
16
18
|
from .network.poll_event_buffer import PollEventBuffer
|
|
17
|
-
from .
|
|
18
|
-
from .processor.
|
|
19
|
+
from .processor.pipeline import KnowledgePipeline
|
|
20
|
+
from .processor.kobj_queue import KobjQueue
|
|
21
|
+
from .secure import Secure
|
|
22
|
+
from .entrypoints import NodeServer, NodePoller
|
|
23
|
+
from .processor.knowledge_handlers import (
|
|
19
24
|
basic_manifest_handler,
|
|
20
25
|
basic_network_output_filter,
|
|
21
26
|
basic_rid_handler,
|
|
@@ -24,28 +29,15 @@ from .processor.handlers import (
|
|
|
24
29
|
forget_edge_on_node_deletion,
|
|
25
30
|
secure_profile_handler
|
|
26
31
|
)
|
|
27
|
-
from .processor.event_worker import EventProcessingWorker
|
|
28
|
-
from .processor.pipeline import KnowledgePipeline
|
|
29
|
-
from .processor.kobj_queue import KobjQueue
|
|
30
|
-
from .secure import Secure
|
|
31
|
-
from .server import NodeServer
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# factory functions for components with non standard initializiation
|
|
35
|
-
|
|
36
|
-
def make_config() -> BaseConfig:
|
|
37
|
-
return BaseConfig.load_from_yaml()
|
|
38
|
-
|
|
39
|
-
def make_cache(config: BaseConfig) -> Cache:
|
|
40
|
-
return Cache(directory_path=config.koi_net.cache_directory_path)
|
|
41
32
|
|
|
42
33
|
|
|
43
34
|
class BaseNode(NodeAssembler):
|
|
44
|
-
|
|
35
|
+
config_cls = NodeConfig
|
|
45
36
|
kobj_queue = KobjQueue
|
|
46
37
|
event_queue = EventQueue
|
|
47
38
|
poll_event_buf = PollEventBuffer
|
|
48
|
-
|
|
39
|
+
config = ConfigLoader
|
|
40
|
+
knowledge_handlers = [
|
|
49
41
|
basic_rid_handler,
|
|
50
42
|
basic_manifest_handler,
|
|
51
43
|
secure_profile_handler,
|
|
@@ -54,7 +46,8 @@ class BaseNode(NodeAssembler):
|
|
|
54
46
|
basic_network_output_filter,
|
|
55
47
|
forget_edge_on_node_deletion
|
|
56
48
|
]
|
|
57
|
-
cache =
|
|
49
|
+
cache = lambda config: Cache(
|
|
50
|
+
directory_path=config.koi_net.cache_directory_path)
|
|
58
51
|
identity = NodeIdentity
|
|
59
52
|
graph = NetworkGraph
|
|
60
53
|
secure = Secure
|
|
@@ -70,7 +63,6 @@ class BaseNode(NodeAssembler):
|
|
|
70
63
|
event_worker = EventProcessingWorker
|
|
71
64
|
lifecycle = NodeLifecycle
|
|
72
65
|
|
|
73
|
-
|
|
74
66
|
class FullNode(BaseNode):
|
|
75
67
|
entrypoint = NodeServer
|
|
76
68
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Implementations of default dereference actions."""
|
|
2
2
|
|
|
3
|
-
from .context import ActionContext
|
|
4
3
|
from rid_lib.types import KoiNetNode
|
|
5
4
|
from rid_lib.ext import Bundle
|
|
6
|
-
from .effector import Effector
|
|
5
|
+
from .effector import Effector, ActionContext
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
@Effector.register_default_action(KoiNetNode)
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import time
|
|
3
3
|
import structlog
|
|
4
4
|
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from .
|
|
5
|
+
from .base import EntryPoint
|
|
6
|
+
from ..processor.kobj_queue import KobjQueue
|
|
7
|
+
from ..lifecycle import NodeLifecycle
|
|
8
|
+
from ..network.resolver import NetworkResolver
|
|
9
|
+
from ..config.partial_node import PartialNodeConfig
|
|
10
10
|
|
|
11
11
|
log = structlog.stdlib.get_logger()
|
|
12
12
|
|
|
@@ -16,11 +16,11 @@ class NodePoller(EntryPoint):
|
|
|
16
16
|
kobj_queue: KobjQueue
|
|
17
17
|
lifecycle: NodeLifecycle
|
|
18
18
|
resolver: NetworkResolver
|
|
19
|
-
config:
|
|
19
|
+
config: PartialNodeConfig
|
|
20
20
|
|
|
21
21
|
def __init__(
|
|
22
22
|
self,
|
|
23
|
-
config:
|
|
23
|
+
config: PartialNodeConfig,
|
|
24
24
|
lifecycle: NodeLifecycle,
|
|
25
25
|
kobj_queue: KobjQueue,
|
|
26
26
|
resolver: NetworkResolver,
|
|
@@ -35,7 +35,7 @@ class NodePoller(EntryPoint):
|
|
|
35
35
|
neighbors = self.resolver.poll_neighbors()
|
|
36
36
|
for node_rid in neighbors:
|
|
37
37
|
for event in neighbors[node_rid]:
|
|
38
|
-
self.kobj_queue.
|
|
38
|
+
self.kobj_queue.push(event=event, source=node_rid)
|
|
39
39
|
|
|
40
40
|
def run(self):
|
|
41
41
|
"""Runs polling event loop."""
|
|
@@ -44,6 +44,6 @@ class NodePoller(EntryPoint):
|
|
|
44
44
|
start_time = time.time()
|
|
45
45
|
self.poll()
|
|
46
46
|
elapsed = time.time() - start_time
|
|
47
|
-
sleep_time = self.config.
|
|
47
|
+
sleep_time = self.config.poller.polling_interval - elapsed
|
|
48
48
|
if sleep_time > 0:
|
|
49
49
|
time.sleep(sleep_time)
|
|
@@ -4,21 +4,20 @@ from contextlib import asynccontextmanager
|
|
|
4
4
|
from fastapi import FastAPI, APIRouter
|
|
5
5
|
from fastapi.responses import JSONResponse
|
|
6
6
|
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
from .
|
|
10
|
-
from
|
|
11
|
-
from
|
|
12
|
-
from
|
|
13
|
-
from .
|
|
14
|
-
from .config import NodeConfig
|
|
7
|
+
from .base import EntryPoint
|
|
8
|
+
from ..network.response_handler import ResponseHandler
|
|
9
|
+
from ..protocol.model_map import API_MODEL_MAP
|
|
10
|
+
from ..protocol.api_models import ErrorResponse
|
|
11
|
+
from ..protocol.errors import ProtocolError
|
|
12
|
+
from ..lifecycle import NodeLifecycle
|
|
13
|
+
from ..config.full_node import FullNodeConfig
|
|
15
14
|
|
|
16
15
|
log = structlog.stdlib.get_logger()
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class NodeServer(EntryPoint):
|
|
20
19
|
"""Manages FastAPI server and event handling for full nodes."""
|
|
21
|
-
config:
|
|
20
|
+
config: FullNodeConfig
|
|
22
21
|
lifecycle: NodeLifecycle
|
|
23
22
|
response_handler: ResponseHandler
|
|
24
23
|
app: FastAPI
|
|
@@ -26,7 +25,7 @@ class NodeServer(EntryPoint):
|
|
|
26
25
|
|
|
27
26
|
def __init__(
|
|
28
27
|
self,
|
|
29
|
-
config:
|
|
28
|
+
config: FullNodeConfig,
|
|
30
29
|
lifecycle: NodeLifecycle,
|
|
31
30
|
response_handler: ResponseHandler,
|
|
32
31
|
):
|
|
@@ -25,13 +25,13 @@ class Handshaker:
|
|
|
25
25
|
reset the target's cache in case it already knew this node.
|
|
26
26
|
"""
|
|
27
27
|
log.debug(f"Initiating handshake with {target}")
|
|
28
|
-
self.event_queue.
|
|
28
|
+
self.event_queue.push(
|
|
29
29
|
Event.from_rid(
|
|
30
30
|
event_type=EventType.FORGET,
|
|
31
31
|
rid=self.identity.rid),
|
|
32
32
|
target=target
|
|
33
33
|
)
|
|
34
|
-
self.event_queue.
|
|
34
|
+
self.event_queue.push(
|
|
35
35
|
event=Event.from_bundle(
|
|
36
36
|
event_type=EventType.NEW,
|
|
37
37
|
bundle=self.cache.read(self.identity.rid)),
|
|
@@ -6,12 +6,12 @@ from rid_lib.types import KoiNetNode
|
|
|
6
6
|
|
|
7
7
|
from .handshaker import Handshaker
|
|
8
8
|
from .network.request_handler import RequestHandler
|
|
9
|
-
from .
|
|
9
|
+
from .workers.kobj_worker import KnowledgeProcessingWorker
|
|
10
10
|
from .network.event_queue import EventQueue
|
|
11
|
-
from .
|
|
11
|
+
from .workers import EventProcessingWorker
|
|
12
12
|
from .protocol.api_models import ErrorResponse
|
|
13
|
-
from .
|
|
14
|
-
from .config import NodeConfig
|
|
13
|
+
from .workers.base import STOP_WORKER
|
|
14
|
+
from .config.core import NodeConfig
|
|
15
15
|
from .processor.kobj_queue import KobjQueue
|
|
16
16
|
from .network.graph import NetworkGraph
|
|
17
17
|
from .identity import NodeIdentity
|
|
@@ -99,7 +99,7 @@ class NodeLifecycle:
|
|
|
99
99
|
|
|
100
100
|
# refresh to reflect changes (if any) in config.yaml
|
|
101
101
|
|
|
102
|
-
self.kobj_queue.
|
|
102
|
+
self.kobj_queue.push(bundle=Bundle.generate(
|
|
103
103
|
rid=self.identity.rid,
|
|
104
104
|
contents=self.identity.profile.model_dump()
|
|
105
105
|
))
|
|
@@ -119,7 +119,7 @@ class NodeLifecycle:
|
|
|
119
119
|
continue
|
|
120
120
|
|
|
121
121
|
for manifest in payload.manifests:
|
|
122
|
-
self.kobj_queue.
|
|
122
|
+
self.kobj_queue.push(
|
|
123
123
|
manifest=manifest,
|
|
124
124
|
source=coordinator
|
|
125
125
|
)
|
|
@@ -31,7 +31,7 @@ class ErrorHandler:
|
|
|
31
31
|
|
|
32
32
|
if self.timeout_counter[node] > 3:
|
|
33
33
|
log.debug(f"Exceeded time out limit, forgetting node")
|
|
34
|
-
self.kobj_queue.
|
|
34
|
+
self.kobj_queue.push(rid=node, event_type=EventType.FORGET)
|
|
35
35
|
# do something
|
|
36
36
|
|
|
37
37
|
|
|
@@ -20,7 +20,7 @@ class EventQueue:
|
|
|
20
20
|
def __init__(self):
|
|
21
21
|
self.q = Queue()
|
|
22
22
|
|
|
23
|
-
def
|
|
23
|
+
def push(self, event: Event, target: KoiNetNode):
|
|
24
24
|
"""Pushes event to queue of specified node.
|
|
25
25
|
|
|
26
26
|
Event will be sent to webhook or poll queue depending on the
|