koi-net 1.1.0b8__py3-none-any.whl → 1.2.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of koi-net might be problematic. Click here for more details.
- koi_net/__init__.py +1 -1
- koi_net/behaviors.py +51 -0
- koi_net/cli/__init__.py +1 -0
- koi_net/cli/commands.py +99 -0
- koi_net/cli/models.py +41 -0
- koi_net/config.py +34 -0
- koi_net/context.py +12 -21
- koi_net/core.py +209 -170
- koi_net/default_actions.py +10 -1
- koi_net/effector.py +39 -24
- koi_net/handshaker.py +39 -0
- koi_net/kobj_worker.py +45 -0
- koi_net/lifecycle.py +67 -38
- koi_net/models.py +14 -0
- koi_net/network/error_handler.py +12 -10
- koi_net/network/event_queue.py +14 -184
- koi_net/network/graph.py +7 -2
- koi_net/network/request_handler.py +31 -19
- koi_net/network/resolver.py +5 -8
- koi_net/network/response_handler.py +5 -6
- koi_net/poll_event_buffer.py +17 -0
- koi_net/poller.py +12 -5
- koi_net/processor/default_handlers.py +84 -35
- koi_net/processor/event_worker.py +121 -0
- koi_net/processor/handler.py +4 -2
- koi_net/processor/knowledge_object.py +19 -7
- koi_net/processor/knowledge_pipeline.py +7 -26
- koi_net/processor/kobj_queue.py +51 -0
- koi_net/protocol/api_models.py +3 -2
- koi_net/protocol/node.py +3 -3
- koi_net/secure.py +28 -8
- koi_net/server.py +25 -9
- koi_net/utils.py +18 -0
- koi_net/worker.py +10 -0
- {koi_net-1.1.0b8.dist-info → koi_net-1.2.0b1.dist-info}/METADATA +7 -3
- koi_net-1.2.0b1.dist-info/RECORD +49 -0
- koi_net-1.2.0b1.dist-info/entry_points.txt +2 -0
- koi_net/actor.py +0 -60
- koi_net/processor/interface.py +0 -101
- koi_net-1.1.0b8.dist-info/RECORD +0 -38
- {koi_net-1.1.0b8.dist-info → koi_net-1.2.0b1.dist-info}/WHEEL +0 -0
- {koi_net-1.1.0b8.dist-info → koi_net-1.2.0b1.dist-info}/licenses/LICENSE +0 -0
koi_net/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
from .core import
|
|
1
|
+
from .core import NodeContainer
|
koi_net/behaviors.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
from rid_lib.ext import Cache
|
|
3
|
+
from rid_lib.types import KoiNetNode
|
|
4
|
+
from rid_lib import RIDType
|
|
5
|
+
from koi_net.identity import NodeIdentity
|
|
6
|
+
from koi_net.network.event_queue import EventQueue
|
|
7
|
+
from koi_net.network.request_handler import RequestHandler
|
|
8
|
+
from koi_net.network.resolver import NetworkResolver
|
|
9
|
+
from koi_net.processor.kobj_queue import KobjQueue
|
|
10
|
+
from koi_net.protocol.api_models import ErrorResponse
|
|
11
|
+
from .protocol.event import Event, EventType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
logger = getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Behaviors:
|
|
19
|
+
def __init__(self, cache: Cache, identity: NodeIdentity, event_queue: EventQueue, resolver: NetworkResolver, request_handler: RequestHandler, kobj_queue: KobjQueue):
|
|
20
|
+
self.cache = cache
|
|
21
|
+
self.identity = identity
|
|
22
|
+
self.event_queue = event_queue
|
|
23
|
+
self.resolver = resolver
|
|
24
|
+
self.request_handler = request_handler
|
|
25
|
+
self.kobj_queue = kobj_queue
|
|
26
|
+
|
|
27
|
+
def identify_coordinators(self) -> list[KoiNetNode]:
|
|
28
|
+
"""Returns node's providing state for `orn:koi-net.node`."""
|
|
29
|
+
return self.resolver.get_state_providers(KoiNetNode)
|
|
30
|
+
|
|
31
|
+
def catch_up_with(self, target: KoiNetNode, rid_types: list[RIDType] = []):
|
|
32
|
+
"""Fetches and processes knowledge objects from target node.
|
|
33
|
+
Args:
|
|
34
|
+
target: Node to catch up with
|
|
35
|
+
rid_types: RID types to fetch from target (all types if list is empty)
|
|
36
|
+
"""
|
|
37
|
+
logger.debug(f"catching up with {target} on {rid_types or 'all types'}")
|
|
38
|
+
payload = self.request_handler.fetch_manifests(
|
|
39
|
+
node=target,
|
|
40
|
+
rid_types=rid_types
|
|
41
|
+
)
|
|
42
|
+
if type(payload) == ErrorResponse:
|
|
43
|
+
logger.debug("failed to reach node")
|
|
44
|
+
return
|
|
45
|
+
for manifest in payload.manifests:
|
|
46
|
+
if manifest.rid == self.identity.rid:
|
|
47
|
+
continue
|
|
48
|
+
self.kobj_queue.put_kobj(
|
|
49
|
+
manifest=manifest,
|
|
50
|
+
source=target
|
|
51
|
+
)
|
koi_net/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .commands import app
|
koi_net/cli/commands.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import typer
|
|
3
|
+
from typing import Callable
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
from importlib.metadata import entry_points
|
|
8
|
+
|
|
9
|
+
from koi_net.cli.models import KoiNetworkConfig
|
|
10
|
+
from koi_net.core import NodeInterface
|
|
11
|
+
import shutil
|
|
12
|
+
|
|
13
|
+
app = typer.Typer()
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
installed_nodes = entry_points(group='koi_net.node')
|
|
17
|
+
|
|
18
|
+
net_config = KoiNetworkConfig.load_from_yaml()
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def list_node_types():
|
|
22
|
+
table = Table(title="installed node types")
|
|
23
|
+
table.add_column("name", style="cyan")
|
|
24
|
+
table.add_column("module", style="magenta")
|
|
25
|
+
|
|
26
|
+
for node in installed_nodes:
|
|
27
|
+
table.add_row(node.name, node.module)
|
|
28
|
+
console.print(table)
|
|
29
|
+
|
|
30
|
+
@app.command()
|
|
31
|
+
def list_nodes():
|
|
32
|
+
table = Table(title="created nodes")
|
|
33
|
+
table.add_column("name", style="cyan")
|
|
34
|
+
table.add_column("rid", style="magenta")
|
|
35
|
+
|
|
36
|
+
for dir in os.listdir('.'):
|
|
37
|
+
if not os.path.isdir(dir):
|
|
38
|
+
continue
|
|
39
|
+
for file in os.listdir(dir):
|
|
40
|
+
file_path = os.path.join(dir, file)
|
|
41
|
+
if not (os.path.isfile(file_path) and file == "config.yaml"):
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
print(os.getcwd())
|
|
45
|
+
os.chdir(dir)
|
|
46
|
+
print(os.getcwd())
|
|
47
|
+
|
|
48
|
+
node_type = net_config.nodes.get(dir)
|
|
49
|
+
|
|
50
|
+
ep = list(installed_nodes.select(name=node_type))[0]
|
|
51
|
+
create_node: Callable[[], NodeInterface] = ep.load()
|
|
52
|
+
|
|
53
|
+
node = create_node()
|
|
54
|
+
|
|
55
|
+
print(ep)
|
|
56
|
+
print(dir)
|
|
57
|
+
print(node.identity.rid)
|
|
58
|
+
|
|
59
|
+
table.add_row(dir, str(node.identity.rid))
|
|
60
|
+
|
|
61
|
+
os.chdir('..')
|
|
62
|
+
print(os.getcwd())
|
|
63
|
+
|
|
64
|
+
console.print(table)
|
|
65
|
+
|
|
66
|
+
@app.command()
|
|
67
|
+
def create(type: str, name: str):
|
|
68
|
+
# if name not in installed_nodes:
|
|
69
|
+
# console.print(f"[bold red]Error:[/bold red] node type '{name}' doesn't exist")
|
|
70
|
+
# raise typer.Exit(code=1)
|
|
71
|
+
|
|
72
|
+
eps = installed_nodes.select(name=type)
|
|
73
|
+
if eps:
|
|
74
|
+
ep = list(eps)[0]
|
|
75
|
+
|
|
76
|
+
os.mkdir(name)
|
|
77
|
+
os.chdir(name)
|
|
78
|
+
|
|
79
|
+
ep.load()
|
|
80
|
+
|
|
81
|
+
os.chdir('..')
|
|
82
|
+
|
|
83
|
+
net_config.nodes[name] = type
|
|
84
|
+
net_config.save_to_yaml()
|
|
85
|
+
|
|
86
|
+
@app.command()
|
|
87
|
+
def remove(name: str):
|
|
88
|
+
shutil.rmtree(name)
|
|
89
|
+
net_config.nodes.pop(name, None)
|
|
90
|
+
net_config.save_to_yaml()
|
|
91
|
+
|
|
92
|
+
@app.command()
|
|
93
|
+
def start(name: str):
|
|
94
|
+
os.chdir(name)
|
|
95
|
+
node_type = net_config.nodes.get(name)
|
|
96
|
+
ep = list(installed_nodes.select(name=node_type))[0]
|
|
97
|
+
create_node: Callable[[], NodeInterface] = ep.load()
|
|
98
|
+
|
|
99
|
+
create_node().server.run()
|
koi_net/cli/models.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field, PrivateAttr
|
|
2
|
+
from ruamel.yaml import YAML
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class KoiNetworkConfig(BaseModel):
|
|
6
|
+
nodes: dict[str, str] = Field(default_factory=dict)
|
|
7
|
+
_file_path: str = PrivateAttr(default="koi-net-config.yaml")
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def load_from_yaml(
|
|
11
|
+
cls,
|
|
12
|
+
file_path: str = "koi-net-config.yaml",
|
|
13
|
+
):
|
|
14
|
+
yaml = YAML()
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
with open(file_path, "r") as f:
|
|
18
|
+
file_content = f.read()
|
|
19
|
+
config_data = yaml.load(file_content)
|
|
20
|
+
config = cls.model_validate(config_data)
|
|
21
|
+
|
|
22
|
+
except FileNotFoundError:
|
|
23
|
+
config = cls()
|
|
24
|
+
|
|
25
|
+
config._file_path = file_path
|
|
26
|
+
config.save_to_yaml()
|
|
27
|
+
return config
|
|
28
|
+
|
|
29
|
+
def save_to_yaml(self):
|
|
30
|
+
yaml = YAML()
|
|
31
|
+
|
|
32
|
+
with open(self._file_path, "w") as f:
|
|
33
|
+
try:
|
|
34
|
+
config_data = self.model_dump(mode="json")
|
|
35
|
+
yaml.dump(config_data, f)
|
|
36
|
+
except Exception as e:
|
|
37
|
+
if self._file_content:
|
|
38
|
+
f.seek(0)
|
|
39
|
+
f.truncate()
|
|
40
|
+
f.write(self._file_content)
|
|
41
|
+
raise e
|
koi_net/config.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from rid_lib import RIDType
|
|
2
3
|
from ruamel.yaml import YAML
|
|
3
4
|
from pydantic import BaseModel, Field, PrivateAttr
|
|
4
5
|
from dotenv import load_dotenv
|
|
@@ -9,6 +10,8 @@ from .protocol.node import NodeProfile, NodeType
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class ServerConfig(BaseModel):
|
|
13
|
+
"""Config for the node server (full node only)."""
|
|
14
|
+
|
|
12
15
|
host: str = "127.0.0.1"
|
|
13
16
|
port: int = 8000
|
|
14
17
|
path: str | None = "/koi-net"
|
|
@@ -22,10 +25,15 @@ class NodeContact(BaseModel):
|
|
|
22
25
|
url: str | None = None
|
|
23
26
|
|
|
24
27
|
class KoiNetConfig(BaseModel):
|
|
28
|
+
"""Config for KOI-net."""
|
|
29
|
+
|
|
25
30
|
node_name: str
|
|
26
31
|
node_rid: KoiNetNode | None = None
|
|
27
32
|
node_profile: NodeProfile
|
|
28
33
|
|
|
34
|
+
rid_types_of_interest: list[RIDType] = Field(
|
|
35
|
+
default_factory=lambda: [KoiNetNode])
|
|
36
|
+
|
|
29
37
|
cache_directory_path: str = ".rid_cache"
|
|
30
38
|
event_queues_path: str = "event_queues.json"
|
|
31
39
|
private_key_pem_path: str = "priv_key.pem"
|
|
@@ -36,6 +44,15 @@ class KoiNetConfig(BaseModel):
|
|
|
36
44
|
_priv_key: PrivateKey | None = PrivateAttr(default=None)
|
|
37
45
|
|
|
38
46
|
class EnvConfig(BaseModel):
|
|
47
|
+
"""Config for environment variables.
|
|
48
|
+
|
|
49
|
+
Values set in the config are the variables names, and are loaded
|
|
50
|
+
from the environment at runtime. For example, if the config YAML
|
|
51
|
+
sets `priv_key_password: PRIV_KEY_PASSWORD` accessing
|
|
52
|
+
`priv_key_password` would retrieve the value of `PRIV_KEY_PASSWORD`
|
|
53
|
+
from the environment.
|
|
54
|
+
"""
|
|
55
|
+
|
|
39
56
|
priv_key_password: str | None = "PRIV_KEY_PASSWORD"
|
|
40
57
|
|
|
41
58
|
def __init__(self, **kwargs):
|
|
@@ -52,6 +69,12 @@ class EnvConfig(BaseModel):
|
|
|
52
69
|
return value
|
|
53
70
|
|
|
54
71
|
class NodeConfig(BaseModel):
|
|
72
|
+
"""Base configuration class for all nodes.
|
|
73
|
+
|
|
74
|
+
Designed to be extensible for custom node implementations. Classes
|
|
75
|
+
inheriting from `NodeConfig` may add additional config groups.
|
|
76
|
+
"""
|
|
77
|
+
|
|
55
78
|
server: ServerConfig = Field(default_factory=ServerConfig)
|
|
56
79
|
koi_net: KoiNetConfig
|
|
57
80
|
env: EnvConfig = Field(default_factory=EnvConfig)
|
|
@@ -65,6 +88,12 @@ class NodeConfig(BaseModel):
|
|
|
65
88
|
file_path: str = "config.yaml",
|
|
66
89
|
generate_missing: bool = True
|
|
67
90
|
):
|
|
91
|
+
"""Loads config state from YAML file.
|
|
92
|
+
|
|
93
|
+
Defaults to `config.yaml`. If `generate_missing` is set to
|
|
94
|
+
`True`, a private key and RID will be generated if not already
|
|
95
|
+
present in the config.
|
|
96
|
+
"""
|
|
68
97
|
yaml = YAML()
|
|
69
98
|
|
|
70
99
|
try:
|
|
@@ -112,6 +141,11 @@ class NodeConfig(BaseModel):
|
|
|
112
141
|
return config
|
|
113
142
|
|
|
114
143
|
def save_to_yaml(self):
|
|
144
|
+
"""Saves config state to YAML file.
|
|
145
|
+
|
|
146
|
+
File path is set by `load_from_yaml` class method.
|
|
147
|
+
"""
|
|
148
|
+
|
|
115
149
|
yaml = YAML()
|
|
116
150
|
|
|
117
151
|
with open(self._file_path, "w") as f:
|
koi_net/context.py
CHANGED
|
@@ -2,62 +2,53 @@ from rid_lib.ext import Cache
|
|
|
2
2
|
|
|
3
3
|
from koi_net.network.resolver import NetworkResolver
|
|
4
4
|
from .config import NodeConfig
|
|
5
|
-
from .effector import Effector
|
|
6
5
|
from .network.graph import NetworkGraph
|
|
7
|
-
from .network.event_queue import
|
|
6
|
+
from .network.event_queue import EventQueue
|
|
8
7
|
from .network.request_handler import RequestHandler
|
|
9
8
|
from .identity import NodeIdentity
|
|
10
|
-
from .processor.
|
|
9
|
+
from .processor.kobj_queue import KobjQueue
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class ActionContext:
|
|
13
|
+
"""Provides action handlers access to other subsystems."""
|
|
14
|
+
|
|
14
15
|
identity: NodeIdentity
|
|
15
|
-
effector: Effector
|
|
16
16
|
|
|
17
17
|
def __init__(
|
|
18
18
|
self,
|
|
19
19
|
identity: NodeIdentity,
|
|
20
|
-
effector: Effector
|
|
21
20
|
):
|
|
22
21
|
self.identity = identity
|
|
23
|
-
self.effector = effector
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
class HandlerContext:
|
|
25
|
+
"""Provides knowledge handlers access to other subsystems."""
|
|
26
|
+
|
|
27
27
|
identity: NodeIdentity
|
|
28
28
|
config: NodeConfig
|
|
29
29
|
cache: Cache
|
|
30
|
-
event_queue:
|
|
30
|
+
event_queue: EventQueue
|
|
31
|
+
kobj_queue: KobjQueue
|
|
31
32
|
graph: NetworkGraph
|
|
32
33
|
request_handler: RequestHandler
|
|
33
34
|
resolver: NetworkResolver
|
|
34
|
-
effector: Effector
|
|
35
|
-
_processor: ProcessorInterface | None
|
|
36
35
|
|
|
37
36
|
def __init__(
|
|
38
37
|
self,
|
|
39
38
|
identity: NodeIdentity,
|
|
40
39
|
config: NodeConfig,
|
|
41
40
|
cache: Cache,
|
|
42
|
-
event_queue:
|
|
41
|
+
event_queue: EventQueue,
|
|
42
|
+
kobj_queue: KobjQueue,
|
|
43
43
|
graph: NetworkGraph,
|
|
44
44
|
request_handler: RequestHandler,
|
|
45
45
|
resolver: NetworkResolver,
|
|
46
|
-
effector: Effector
|
|
47
46
|
):
|
|
48
47
|
self.identity = identity
|
|
49
48
|
self.config = config
|
|
50
49
|
self.cache = cache
|
|
51
50
|
self.event_queue = event_queue
|
|
51
|
+
self.kobj_queue = kobj_queue
|
|
52
52
|
self.graph = graph
|
|
53
53
|
self.request_handler = request_handler
|
|
54
|
-
self.resolver = resolver
|
|
55
|
-
self.effector = effector
|
|
56
|
-
self._processor = None
|
|
57
|
-
|
|
58
|
-
def set_processor(self, processor: ProcessorInterface):
|
|
59
|
-
self._processor = processor
|
|
60
|
-
|
|
61
|
-
@property
|
|
62
|
-
def handle(self):
|
|
63
|
-
return self._processor.handle
|
|
54
|
+
self.resolver = resolver
|