koi-net 1.2.0b1__tar.gz → 1.2.0b2__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.

Files changed (81) hide show
  1. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/PKG-INFO +2 -1
  2. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/pyproject.toml +2 -1
  3. koi_net-1.2.0b2/src/koi_net/__init__.py +2 -0
  4. koi_net-1.2.0b2/src/koi_net/assembler.py +82 -0
  5. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/context.py +5 -13
  6. koi_net-1.2.0b2/src/koi_net/core.py +78 -0
  7. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/effector.py +26 -14
  8. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/handshaker.py +3 -3
  9. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/identity.py +2 -3
  10. koi_net-1.2.0b2/src/koi_net/interfaces/entrypoint.py +5 -0
  11. {koi_net-1.2.0b1/src/koi_net → koi_net-1.2.0b2/src/koi_net/interfaces}/worker.py +7 -0
  12. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/lifecycle.py +40 -32
  13. koi_net-1.2.0b2/src/koi_net/logger.py +176 -0
  14. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/network/error_handler.py +6 -6
  15. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/network/event_queue.py +8 -6
  16. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/network/graph.py +8 -8
  17. koi_net-1.2.0b2/src/koi_net/network/poll_event_buffer.py +26 -0
  18. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/network/request_handler.py +23 -28
  19. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/network/resolver.py +13 -13
  20. koi_net-1.2.0b2/src/koi_net/network/response_handler.py +132 -0
  21. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/poller.py +7 -5
  22. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/processor/event_worker.py +14 -18
  23. koi_net-1.2.0b1/src/koi_net/processor/default_handlers.py → koi_net-1.2.0b2/src/koi_net/processor/handlers.py +26 -25
  24. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/processor/kobj_queue.py +3 -3
  25. {koi_net-1.2.0b1/src/koi_net → koi_net-1.2.0b2/src/koi_net/processor}/kobj_worker.py +12 -13
  26. koi_net-1.2.0b1/src/koi_net/processor/knowledge_pipeline.py → koi_net-1.2.0b2/src/koi_net/processor/pipeline.py +24 -27
  27. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/api_models.py +5 -2
  28. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/envelope.py +5 -6
  29. koi_net-1.2.0b2/src/koi_net/protocol/model_map.py +61 -0
  30. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/secure.py +8 -8
  31. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/secure.py +5 -5
  32. koi_net-1.2.0b2/src/koi_net/sentry.py +13 -0
  33. koi_net-1.2.0b2/src/koi_net/server.py +95 -0
  34. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/uv.lock +15 -1
  35. koi_net-1.2.0b1/src/koi_net/__init__.py +0 -1
  36. koi_net-1.2.0b1/src/koi_net/behaviors.py +0 -51
  37. koi_net-1.2.0b1/src/koi_net/core.py +0 -233
  38. koi_net-1.2.0b1/src/koi_net/models.py +0 -14
  39. koi_net-1.2.0b1/src/koi_net/network/response_handler.py +0 -67
  40. koi_net-1.2.0b1/src/koi_net/poll_event_buffer.py +0 -17
  41. koi_net-1.2.0b1/src/koi_net/server.py +0 -145
  42. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/.github/workflows/publish-to-pypi.yml +0 -0
  43. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/.gitignore +0 -0
  44. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/LICENSE +0 -0
  45. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/README.md +0 -0
  46. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/examples/coordinator.py +0 -0
  47. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/examples/partial.py +0 -0
  48. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/koi-net-protocol-openapi.json +0 -0
  49. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/requirements.txt +0 -0
  50. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/bundle.schema.json +0 -0
  51. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/bundles_payload.schema.json +0 -0
  52. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/edge_profile.schema.json +0 -0
  53. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/error_response.schema.json +0 -0
  54. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/event.schema.json +0 -0
  55. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/events_payload.schema.json +0 -0
  56. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/fetch_bundles.schema.json +0 -0
  57. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/fetch_manifests.schema.json +0 -0
  58. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/fetch_rids.schema.json +0 -0
  59. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/manifest.schema.json +0 -0
  60. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/manifests_payload.schema.json +0 -0
  61. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/node_profile.schema.json +0 -0
  62. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/poll_events.schema.json +0 -0
  63. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/rids_payload.schema.json +0 -0
  64. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/signed_envelope.schema.json +0 -0
  65. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/schemas/unsigned_envelope.json +0 -0
  66. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/cli/__init__.py +0 -0
  67. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/cli/commands.py +0 -0
  68. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/cli/models.py +0 -0
  69. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/config.py +0 -0
  70. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/default_actions.py +0 -0
  71. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/network/__init__.py +0 -0
  72. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/processor/__init__.py +0 -0
  73. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/processor/handler.py +0 -0
  74. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/processor/knowledge_object.py +0 -0
  75. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/__init__.py +0 -0
  76. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/consts.py +0 -0
  77. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/edge.py +0 -0
  78. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/errors.py +0 -0
  79. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/event.py +0 -0
  80. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/protocol/node.py +0 -0
  81. {koi_net-1.2.0b1 → koi_net-1.2.0b2}/src/koi_net/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koi-net
3
- Version: 1.2.0b1
3
+ Version: 1.2.0b2
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>
@@ -36,6 +36,7 @@ Requires-Dist: python-dotenv>=1.1.0
36
36
  Requires-Dist: rich>=14.1.0
37
37
  Requires-Dist: rid-lib>=3.2.7
38
38
  Requires-Dist: ruamel-yaml>=0.18.10
39
+ Requires-Dist: structlog>=25.4.0
39
40
  Requires-Dist: uvicorn>=0.34.2
40
41
  Provides-Extra: dev
41
42
  Requires-Dist: build; extra == 'dev'
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "koi-net"
7
- version = "1.2.0b1"
7
+ version = "1.2.0b2"
8
8
  description = "Implementation of KOI-net protocol in Python"
9
9
  authors = [
10
10
  {name = "Luke Miller", email = "luke@block.science"}
@@ -23,6 +23,7 @@ dependencies = [
23
23
  "fastapi>=0.115.12",
24
24
  "uvicorn>=0.34.2",
25
25
  "rich>=14.1.0",
26
+ "structlog>=25.4.0",
26
27
  ]
27
28
 
28
29
  [project.optional-dependencies]
@@ -0,0 +1,2 @@
1
+ from . import logger
2
+ from . import sentry
@@ -0,0 +1,82 @@
1
+ import inspect
2
+ from typing import Protocol
3
+ from dataclasses import make_dataclass
4
+
5
+ import structlog
6
+
7
+ from .interfaces.entrypoint import EntryPoint
8
+
9
+ log = structlog.stdlib.get_logger()
10
+
11
+
12
+ class BuildOrderer(type):
13
+ def __new__(cls, name: str, bases: tuple, dct: dict[str]):
14
+ """Sets `cls._build_order` from component order in class definition."""
15
+ cls = super().__new__(cls, name, bases, dct)
16
+
17
+ if "_build_order" not in dct:
18
+ components = {}
19
+ # adds components from base classes (including cls)
20
+ for base in reversed(inspect.getmro(cls)[:-1]):
21
+ for k, v in vars(base).items():
22
+ # excludes built in and private attributes
23
+ if not k.startswith("_"):
24
+ components[k] = v
25
+
26
+ # recipe list constructed from names of non-None components
27
+ cls._build_order = [
28
+ name for name, _type in components.items()
29
+ if _type is not None
30
+ ]
31
+
32
+ return cls
33
+
34
+ class NodeContainer(Protocol):
35
+ entrypoint = EntryPoint
36
+
37
+ class NodeAssembler(metaclass=BuildOrderer):
38
+ def __new__(self) -> NodeContainer:
39
+ return self._build()
40
+
41
+ @classmethod
42
+ def _build(cls) -> NodeContainer:
43
+ components = {}
44
+ for comp_name in cls._build_order:
45
+ comp_factory = getattr(cls, comp_name, None)
46
+
47
+ if comp_factory is None:
48
+ raise Exception(f"Couldn't find factory for component '{comp_name}'")
49
+
50
+ sig = inspect.signature(comp_factory)
51
+
52
+ required_comps = []
53
+ for name, param in sig.parameters.items():
54
+ required_comps.append((name, param.annotation))
55
+
56
+ if len(required_comps) == 0:
57
+ s = comp_name
58
+ else:
59
+ s = f"{comp_name} -> {', '.join([name for name, _type in required_comps])}"
60
+
61
+ print(s.replace("graph", "_graph"), end=";\n")
62
+
63
+ dependencies = {}
64
+ for req_comp_name, req_comp_type in required_comps:
65
+ if req_comp_name not in components:
66
+ raise Exception(f"Couldn't find required component '{req_comp_name}'")
67
+
68
+ dependencies[req_comp_name] = components[req_comp_name]
69
+
70
+ components[comp_name] = comp_factory(**dependencies)
71
+
72
+ NodeContainer = make_dataclass(
73
+ cls_name="NodeContainer",
74
+ fields=[
75
+ (name, type(component))
76
+ for name, component
77
+ in components.items()
78
+ ],
79
+ frozen=True
80
+ )
81
+
82
+ return NodeContainer(**components)
@@ -1,5 +1,6 @@
1
1
  from rid_lib.ext import Cache
2
2
 
3
+ from koi_net.effector import Effector
3
4
  from koi_net.network.resolver import NetworkResolver
4
5
  from .config import NodeConfig
5
6
  from .network.graph import NetworkGraph
@@ -9,18 +10,6 @@ from .identity import NodeIdentity
9
10
  from .processor.kobj_queue import KobjQueue
10
11
 
11
12
 
12
- class ActionContext:
13
- """Provides action handlers access to other subsystems."""
14
-
15
- identity: NodeIdentity
16
-
17
- def __init__(
18
- self,
19
- identity: NodeIdentity,
20
- ):
21
- self.identity = identity
22
-
23
-
24
13
  class HandlerContext:
25
14
  """Provides knowledge handlers access to other subsystems."""
26
15
 
@@ -32,6 +21,7 @@ class HandlerContext:
32
21
  graph: NetworkGraph
33
22
  request_handler: RequestHandler
34
23
  resolver: NetworkResolver
24
+ effector: Effector
35
25
 
36
26
  def __init__(
37
27
  self,
@@ -43,6 +33,7 @@ class HandlerContext:
43
33
  graph: NetworkGraph,
44
34
  request_handler: RequestHandler,
45
35
  resolver: NetworkResolver,
36
+ effector: Effector
46
37
  ):
47
38
  self.identity = identity
48
39
  self.config = config
@@ -51,4 +42,5 @@ class HandlerContext:
51
42
  self.kobj_queue = kobj_queue
52
43
  self.graph = graph
53
44
  self.request_handler = request_handler
54
- self.resolver = resolver
45
+ self.resolver = resolver
46
+ self.effector = effector
@@ -0,0 +1,78 @@
1
+ from rid_lib.ext import Cache
2
+ from .assembler import NodeAssembler
3
+ from .config.base import BaseConfig
4
+ from .context import HandlerContext
5
+ from .effector import Effector
6
+ from .handshaker import Handshaker
7
+ from .identity import NodeIdentity
8
+ from .processor.kobj_worker import KnowledgeProcessingWorker
9
+ from .lifecycle import NodeLifecycle
10
+ from .network.error_handler import ErrorHandler
11
+ from .network.event_queue import EventQueue
12
+ from .network.graph import NetworkGraph
13
+ from .network.request_handler import RequestHandler
14
+ from .network.resolver import NetworkResolver
15
+ from .network.response_handler import ResponseHandler
16
+ from .network.poll_event_buffer import PollEventBuffer
17
+ from .poller import NodePoller
18
+ from .processor.handlers import (
19
+ basic_manifest_handler,
20
+ basic_network_output_filter,
21
+ basic_rid_handler,
22
+ node_contact_handler,
23
+ edge_negotiation_handler,
24
+ forget_edge_on_node_deletion,
25
+ secure_profile_handler
26
+ )
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
+
42
+
43
+ class BaseNode(NodeAssembler):
44
+ config = lambda: None
45
+ kobj_queue = KobjQueue
46
+ event_queue = EventQueue
47
+ poll_event_buf = PollEventBuffer
48
+ knowledge_handlers = lambda: [
49
+ basic_rid_handler,
50
+ basic_manifest_handler,
51
+ secure_profile_handler,
52
+ edge_negotiation_handler,
53
+ node_contact_handler,
54
+ basic_network_output_filter,
55
+ forget_edge_on_node_deletion
56
+ ]
57
+ cache = make_cache
58
+ identity = NodeIdentity
59
+ graph = NetworkGraph
60
+ secure = Secure
61
+ handshaker = Handshaker
62
+ error_handler = ErrorHandler
63
+ request_handler = RequestHandler
64
+ response_handler = ResponseHandler
65
+ resolver = NetworkResolver
66
+ effector = Effector
67
+ handler_context = HandlerContext
68
+ pipeline = KnowledgePipeline
69
+ kobj_worker = KnowledgeProcessingWorker
70
+ event_worker = EventProcessingWorker
71
+ lifecycle = NodeLifecycle
72
+
73
+
74
+ class FullNode(BaseNode):
75
+ entrypoint = NodeServer
76
+
77
+ class PartialNode(BaseNode):
78
+ entrypoint = NodePoller
@@ -1,4 +1,4 @@
1
- import logging
1
+ import structlog
2
2
  from typing import Callable
3
3
  from enum import StrEnum
4
4
  from rid_lib.ext import Cache, Bundle
@@ -6,11 +6,23 @@ from rid_lib.core import RID, RIDType
6
6
  from rid_lib.types import KoiNetNode
7
7
  from .network.resolver import NetworkResolver
8
8
  from .processor.kobj_queue import KobjQueue
9
- from .context import ActionContext
9
+ from .identity import NodeIdentity
10
10
 
11
- logger = logging.getLogger(__name__)
11
+ log = structlog.stdlib.get_logger()
12
12
 
13
13
 
14
+ class ActionContext:
15
+ """Provides action handlers access to other subsystems."""
16
+
17
+ identity: NodeIdentity
18
+
19
+ def __init__(
20
+ self,
21
+ identity: NodeIdentity,
22
+ ):
23
+ self.identity = identity
24
+
25
+
14
26
  class BundleSource(StrEnum):
15
27
  CACHE = "CACHE"
16
28
  ACTION = "ACTION"
@@ -35,12 +47,12 @@ class Effector:
35
47
  cache: Cache,
36
48
  resolver: NetworkResolver,
37
49
  kobj_queue: KobjQueue,
38
- action_context: ActionContext
50
+ identity: NodeIdentity
39
51
  ):
40
52
  self.cache = cache
41
53
  self.resolver = resolver
42
54
  self.kobj_queue = kobj_queue
43
- self.action_context = action_context
55
+ self.action_context = ActionContext(identity)
44
56
  self._action_table = self.__class__._action_table.copy()
45
57
 
46
58
  @classmethod
@@ -70,18 +82,18 @@ class Effector:
70
82
  bundle = self.cache.read(rid)
71
83
 
72
84
  if bundle:
73
- logger.debug("Cache hit")
85
+ log.debug("Cache hit")
74
86
  return bundle, BundleSource.CACHE
75
87
  else:
76
- logger.debug("Cache miss")
88
+ log.debug("Cache miss")
77
89
  return None
78
90
 
79
91
  def _try_action(self, rid: RID) -> tuple[Bundle, BundleSource] | None:
80
92
  if type(rid) not in self._action_table:
81
- logger.debug("No action available")
93
+ log.debug("No action available")
82
94
  return None
83
95
 
84
- logger.debug("Action available")
96
+ log.debug("Action available")
85
97
  func = self._action_table[type(rid)]
86
98
  bundle = func(
87
99
  ctx=self.action_context,
@@ -89,10 +101,10 @@ class Effector:
89
101
  )
90
102
 
91
103
  if bundle:
92
- logger.debug("Action hit")
104
+ log.debug("Action hit")
93
105
  return bundle, BundleSource.ACTION
94
106
  else:
95
- logger.debug("Action miss")
107
+ log.debug("Action miss")
96
108
  return None
97
109
 
98
110
 
@@ -100,10 +112,10 @@ class Effector:
100
112
  bundle, source = self.resolver.fetch_remote_bundle(rid)
101
113
 
102
114
  if bundle:
103
- logger.debug("Network hit")
115
+ log.debug("Network hit")
104
116
  return bundle, source
105
117
  else:
106
- logger.debug("Network miss")
118
+ log.debug("Network miss")
107
119
  return None
108
120
 
109
121
 
@@ -127,7 +139,7 @@ class Effector:
127
139
  handle_result: handles resulting bundle with knowledge pipeline when `True`
128
140
  """
129
141
 
130
- logger.debug(f"Dereferencing {rid!r}")
142
+ log.debug(f"Dereferencing {rid!r}")
131
143
 
132
144
  bundle, source = (
133
145
  # if `refresh_cache`, skip try cache
@@ -1,11 +1,11 @@
1
- from logging import getLogger
1
+ import structlog
2
2
  from rid_lib.ext import Cache
3
3
  from rid_lib.types import KoiNetNode
4
4
  from koi_net.identity import NodeIdentity
5
5
  from koi_net.network.event_queue import EventQueue
6
6
  from .protocol.event import Event, EventType
7
7
 
8
- logger = getLogger(__name__)
8
+ log = structlog.stdlib.get_logger()
9
9
 
10
10
 
11
11
  class Handshaker:
@@ -24,7 +24,7 @@ class Handshaker:
24
24
  Pushes successive `FORGET` and `NEW` events to target node to
25
25
  reset the target's cache in case it already knew this node.
26
26
  """
27
- logger.debug(f"Initiating handshake with {target}")
27
+ log.debug(f"Initiating handshake with {target}")
28
28
  self.event_queue.push_event_to(
29
29
  Event.from_rid(
30
30
  event_type=EventType.FORGET,
@@ -1,10 +1,9 @@
1
- import logging
1
+ import structlog
2
2
  from rid_lib.types.koi_net_node import KoiNetNode
3
3
  from .config import NodeConfig
4
4
  from .protocol.node import NodeProfile
5
5
 
6
-
7
- logger = logging.getLogger(__name__)
6
+ log = structlog.stdlib.get_logger()
8
7
 
9
8
 
10
9
  class NodeIdentity:
@@ -0,0 +1,5 @@
1
+ from typing import Protocol
2
+
3
+
4
+ class EntryPoint(Protocol):
5
+ def run(self): ...
@@ -1,5 +1,12 @@
1
1
  import threading
2
2
 
3
+
4
+ class End:
5
+ """Class for a sentinel value by knowledge handlers."""
6
+ pass
7
+
8
+ STOP_WORKER = End()
9
+
3
10
  class ThreadWorker:
4
11
  thread: threading.Thread
5
12
 
@@ -1,22 +1,22 @@
1
- import logging
1
+ import structlog
2
2
  from contextlib import contextmanager, asynccontextmanager
3
3
 
4
4
  from rid_lib.ext import Bundle, Cache
5
5
  from rid_lib.types import KoiNetNode
6
6
 
7
- from koi_net.behaviors import Behaviors
8
- from koi_net.handshaker import Handshaker
9
- from koi_net.kobj_worker import KnowledgeProcessingWorker
10
- from koi_net.models import END
11
- from koi_net.network.event_queue import EventQueue
12
- from koi_net.processor.event_worker import EventProcessingWorker
13
-
7
+ from .handshaker import Handshaker
8
+ from .network.request_handler import RequestHandler
9
+ from .processor.kobj_worker import KnowledgeProcessingWorker
10
+ from .network.event_queue import EventQueue
11
+ from .processor.event_worker import EventProcessingWorker
12
+ from .protocol.api_models import ErrorResponse
13
+ from .interfaces.worker import STOP_WORKER
14
14
  from .config import NodeConfig
15
15
  from .processor.kobj_queue import KobjQueue
16
16
  from .network.graph import NetworkGraph
17
17
  from .identity import NodeIdentity
18
18
 
19
- logger = logging.getLogger(__name__)
19
+ log = structlog.stdlib.get_logger()
20
20
 
21
21
 
22
22
  class NodeLifecycle:
@@ -31,7 +31,7 @@ class NodeLifecycle:
31
31
  event_worker: EventProcessingWorker
32
32
  cache: Cache
33
33
  handshaker: Handshaker
34
- behaviors: Behaviors
34
+ request_handler: RequestHandler
35
35
 
36
36
  def __init__(
37
37
  self,
@@ -44,7 +44,7 @@ class NodeLifecycle:
44
44
  event_worker: EventProcessingWorker,
45
45
  cache: Cache,
46
46
  handshaker: Handshaker,
47
- behaviors: Behaviors
47
+ request_handler: RequestHandler
48
48
  ):
49
49
  self.config = config
50
50
  self.identity = identity
@@ -55,32 +55,32 @@ class NodeLifecycle:
55
55
  self.event_worker = event_worker
56
56
  self.cache = cache
57
57
  self.handshaker = handshaker
58
- self.behaviors = behaviors
58
+ self.request_handler = request_handler
59
59
 
60
60
  @contextmanager
61
61
  def run(self):
62
62
  """Synchronous context manager for node startup and shutdown."""
63
63
  try:
64
- logger.info("Starting node lifecycle...")
64
+ log.info("Starting node lifecycle...")
65
65
  self.start()
66
66
  yield
67
67
  except KeyboardInterrupt:
68
- logger.info("Keyboard interrupt!")
68
+ log.info("Keyboard interrupt!")
69
69
  finally:
70
- logger.info("Stopping node lifecycle...")
70
+ log.info("Stopping node lifecycle...")
71
71
  self.stop()
72
72
 
73
73
  @asynccontextmanager
74
74
  async def async_run(self):
75
75
  """Asynchronous context manager for node startup and shutdown."""
76
76
  try:
77
- logger.info("Starting async node lifecycle...")
77
+ log.info("Starting async node lifecycle...")
78
78
  self.start()
79
79
  yield
80
80
  except KeyboardInterrupt:
81
- logger.info("Keyboard interrupt!")
81
+ log.info("Keyboard interrupt!")
82
82
  finally:
83
- logger.info("Stopping async node lifecycle...")
83
+ log.info("Stopping async node lifecycle...")
84
84
  self.stop()
85
85
 
86
86
  def start(self):
@@ -91,7 +91,7 @@ class NodeLifecycle:
91
91
  of node bundle. Initiates handshake with first contact if node
92
92
  doesn't have any neighbors. Catches up with coordinator state.
93
93
  """
94
- logger.info("Starting processor worker thread")
94
+ log.info("Starting processor worker thread")
95
95
 
96
96
  self.kobj_worker.thread.start()
97
97
  self.event_worker.thread.start()
@@ -104,30 +104,38 @@ class NodeLifecycle:
104
104
  contents=self.identity.profile.model_dump()
105
105
  ))
106
106
 
107
- logger.debug("Waiting for kobj queue to empty")
107
+ log.debug("Waiting for kobj queue to empty")
108
108
  self.kobj_queue.q.join()
109
109
 
110
- # TODO: FACTOR OUT BEHAVIOR
111
-
112
110
  coordinators = self.graph.get_neighbors(direction="in", allowed_type=KoiNetNode)
113
111
 
114
- if len(coordinators) == 0 and self.config.koi_net.first_contact.rid:
115
- logger.debug(f"I don't have any edges with coordinators, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
112
+ if len(coordinators) > 0:
113
+ for coordinator in coordinators:
114
+ payload = self.request_handler.fetch_manifests(
115
+ node=coordinator,
116
+ rid_types=[KoiNetNode]
117
+ )
118
+ if type(payload) is ErrorResponse:
119
+ continue
120
+
121
+ for manifest in payload.manifests:
122
+ self.kobj_queue.put_kobj(
123
+ manifest=manifest,
124
+ source=coordinator
125
+ )
126
+
127
+ elif self.config.koi_net.first_contact.rid:
128
+ log.debug(f"I don't have any edges with coordinators, reaching out to first contact {self.config.koi_net.first_contact.rid!r}")
116
129
 
117
130
  self.handshaker.handshake_with(self.config.koi_net.first_contact.rid)
118
131
 
119
-
120
-
121
- for coordinator in self.behaviors.identify_coordinators():
122
- self.behaviors.catch_up_with(coordinator, rid_types=[KoiNetNode])
123
-
124
132
 
125
133
  def stop(self):
126
134
  """Stops a node.
127
135
 
128
136
  Finishes processing knowledge object queue.
129
137
  """
130
- logger.info(f"Waiting for kobj queue to empty ({self.kobj_queue.q.unfinished_tasks} tasks remaining)")
138
+ log.info(f"Waiting for kobj queue to empty ({self.kobj_queue.q.unfinished_tasks} tasks remaining)")
131
139
 
132
- self.kobj_queue.q.put(END)
133
- self.event_queue.q.put(END)
140
+ self.kobj_queue.q.put(STOP_WORKER)
141
+ self.event_queue.q.put(STOP_WORKER)