koi-net 1.2.0b1__py3-none-any.whl → 1.2.0b2__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.

Files changed (40) hide show
  1. koi_net/__init__.py +2 -1
  2. koi_net/assembler.py +82 -0
  3. koi_net/context.py +5 -13
  4. koi_net/core.py +51 -206
  5. koi_net/effector.py +26 -14
  6. koi_net/handshaker.py +3 -3
  7. koi_net/identity.py +2 -3
  8. koi_net/interfaces/entrypoint.py +5 -0
  9. koi_net/{worker.py → interfaces/worker.py} +7 -0
  10. koi_net/lifecycle.py +40 -32
  11. koi_net/logger.py +176 -0
  12. koi_net/network/error_handler.py +6 -6
  13. koi_net/network/event_queue.py +8 -6
  14. koi_net/network/graph.py +8 -8
  15. koi_net/network/poll_event_buffer.py +26 -0
  16. koi_net/network/request_handler.py +23 -28
  17. koi_net/network/resolver.py +13 -13
  18. koi_net/network/response_handler.py +74 -9
  19. koi_net/poller.py +7 -5
  20. koi_net/processor/event_worker.py +14 -18
  21. koi_net/processor/{default_handlers.py → handlers.py} +26 -25
  22. koi_net/processor/kobj_queue.py +3 -3
  23. koi_net/{kobj_worker.py → processor/kobj_worker.py} +12 -13
  24. koi_net/processor/{knowledge_pipeline.py → pipeline.py} +24 -27
  25. koi_net/protocol/api_models.py +5 -2
  26. koi_net/protocol/envelope.py +5 -6
  27. koi_net/protocol/model_map.py +61 -0
  28. koi_net/protocol/secure.py +8 -8
  29. koi_net/secure.py +5 -5
  30. koi_net/sentry.py +13 -0
  31. koi_net/server.py +36 -86
  32. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/METADATA +2 -1
  33. koi_net-1.2.0b2.dist-info/RECORD +52 -0
  34. koi_net/behaviors.py +0 -51
  35. koi_net/models.py +0 -14
  36. koi_net/poll_event_buffer.py +0 -17
  37. koi_net-1.2.0b1.dist-info/RECORD +0 -49
  38. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/WHEEL +0 -0
  39. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/entry_points.txt +0 -0
  40. {koi_net-1.2.0b1.dist-info → koi_net-1.2.0b2.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- import logging
1
+ import structlog
2
2
  from rid_lib.types import KoiNetEdge, KoiNetNode
3
3
  from rid_lib.ext import Cache
4
4
  from ..protocol.event import EventType
@@ -13,16 +13,13 @@ from .handler import (
13
13
  StopChain
14
14
  )
15
15
  from .knowledge_object import KnowledgeObject
16
+ from ..context import HandlerContext
16
17
 
17
- from typing import TYPE_CHECKING
18
- if TYPE_CHECKING:
19
- from ..context import HandlerContext
20
-
21
- logger = logging.getLogger(__name__)
18
+ log = structlog.stdlib.get_logger()
22
19
 
23
20
 
24
21
  class KnowledgePipeline:
25
- handler_context: "HandlerContext"
22
+ handler_context: HandlerContext
26
23
  cache: Cache
27
24
  identity: NodeIdentity
28
25
  request_handler: RequestHandler
@@ -32,7 +29,7 @@ class KnowledgePipeline:
32
29
 
33
30
  def __init__(
34
31
  self,
35
- handler_context: "HandlerContext",
32
+ handler_context: HandlerContext,
36
33
  cache: Cache,
37
34
  request_handler: RequestHandler,
38
35
  event_queue: EventQueue,
@@ -71,7 +68,7 @@ class KnowledgePipeline:
71
68
  if handler.event_types and kobj.event_type not in handler.event_types:
72
69
  continue
73
70
 
74
- logger.debug(f"Calling {handler_type} handler '{handler.func.__name__}'")
71
+ log.debug(f"Calling {handler_type} handler '{handler.func.__name__}'")
75
72
 
76
73
  resp = handler.func(
77
74
  ctx=self.handler_context,
@@ -80,7 +77,7 @@ class KnowledgePipeline:
80
77
 
81
78
  # stops handler chain execution
82
79
  if resp is STOP_CHAIN:
83
- logger.debug(f"Handler chain stopped by {handler.func.__name__}")
80
+ log.debug(f"Handler chain stopped by {handler.func.__name__}")
84
81
  return STOP_CHAIN
85
82
  # kobj unmodified
86
83
  elif resp is None:
@@ -88,7 +85,7 @@ class KnowledgePipeline:
88
85
  # kobj modified by handler
89
86
  elif isinstance(resp, KnowledgeObject):
90
87
  kobj = resp
91
- logger.debug(f"Knowledge object modified by {handler.func.__name__}")
88
+ log.debug(f"Knowledge object modified by {handler.func.__name__}")
92
89
  else:
93
90
  raise ValueError(f"Handler {handler.func.__name__} returned invalid response '{resp}'")
94
91
 
@@ -107,36 +104,36 @@ class KnowledgePipeline:
107
104
  The pipeline may be stopped by any point by a single handler returning the `STOP_CHAIN` sentinel. In that case, the process will exit immediately. Further handlers of that type and later handler chains will not be called.
108
105
  """
109
106
 
110
- logger.debug(f"Handling {kobj!r}")
107
+ log.debug(f"Handling {kobj!r}")
111
108
  kobj = self.call_handler_chain(HandlerType.RID, kobj)
112
109
  if kobj is STOP_CHAIN: return
113
110
 
114
111
  if kobj.event_type == EventType.FORGET:
115
112
  bundle = self.cache.read(kobj.rid)
116
113
  if not bundle:
117
- logger.debug("Local bundle not found")
114
+ log.debug("Local bundle not found")
118
115
  return
119
116
 
120
117
  # the bundle (to be deleted) attached to kobj for downstream analysis
121
- logger.debug("Adding local bundle (to be deleted) to knowledge object")
118
+ log.debug("Adding local bundle (to be deleted) to knowledge object")
122
119
  kobj.manifest = bundle.manifest
123
120
  kobj.contents = bundle.contents
124
121
 
125
122
  else:
126
123
  # attempt to retrieve manifest
127
124
  if not kobj.manifest:
128
- logger.debug("Manifest not found")
125
+ log.debug("Manifest not found")
129
126
  if not kobj.source:
130
127
  return
131
128
 
132
- logger.debug("Attempting to fetch remote manifest from source")
129
+ log.debug("Attempting to fetch remote manifest from source")
133
130
  payload = self.request_handler.fetch_manifests(
134
131
  node=kobj.source,
135
132
  rids=[kobj.rid]
136
133
  )
137
134
 
138
135
  if not payload.manifests:
139
- logger.debug("Failed to find manifest")
136
+ log.debug("Failed to find manifest")
140
137
  return
141
138
 
142
139
  kobj.manifest = payload.manifests[0]
@@ -146,24 +143,24 @@ class KnowledgePipeline:
146
143
 
147
144
  # attempt to retrieve bundle
148
145
  if not kobj.bundle:
149
- logger.debug("Bundle not found")
146
+ log.debug("Bundle not found")
150
147
  if kobj.source is None:
151
148
  return
152
149
 
153
- logger.debug("Attempting to fetch remote bundle from source")
150
+ log.debug("Attempting to fetch remote bundle from source")
154
151
  payload = self.request_handler.fetch_bundles(
155
152
  node=kobj.source,
156
153
  rids=[kobj.rid]
157
154
  )
158
155
 
159
156
  if not payload.bundles:
160
- logger.debug("Failed to find bundle")
157
+ log.debug("Failed to find bundle")
161
158
  return
162
159
 
163
160
  bundle = payload.bundles[0]
164
161
 
165
162
  if kobj.manifest != bundle.manifest:
166
- logger.warning("Retrieved bundle contains a different manifest")
163
+ log.warning("Retrieved bundle contains a different manifest")
167
164
 
168
165
  kobj.manifest = bundle.manifest
169
166
  kobj.contents = bundle.contents
@@ -172,28 +169,28 @@ class KnowledgePipeline:
172
169
  if kobj is STOP_CHAIN: return
173
170
 
174
171
  if kobj.normalized_event_type in (EventType.UPDATE, EventType.NEW):
175
- logger.info(f"Writing to cache: {kobj!r}")
172
+ log.info(f"Writing to cache: {kobj!r}")
176
173
  self.cache.write(kobj.bundle)
177
174
 
178
175
  elif kobj.normalized_event_type == EventType.FORGET:
179
- logger.info(f"Deleting from cache: {kobj!r}")
176
+ log.info(f"Deleting from cache: {kobj!r}")
180
177
  self.cache.delete(kobj.rid)
181
178
 
182
179
  else:
183
- logger.debug("Normalized event type was never set, no cache or network operations will occur")
180
+ log.debug("Normalized event type was never set, no cache or network operations will occur")
184
181
  return
185
182
 
186
183
  if type(kobj.rid) in (KoiNetNode, KoiNetEdge):
187
- logger.debug("Change to node or edge, regenerating network graph")
184
+ log.debug("Change to node or edge, regenerating network graph")
188
185
  self.graph.generate()
189
186
 
190
187
  kobj = self.call_handler_chain(HandlerType.Network, kobj)
191
188
  if kobj is STOP_CHAIN: return
192
189
 
193
190
  if kobj.network_targets:
194
- logger.debug(f"Broadcasting event to {len(kobj.network_targets)} network target(s)")
191
+ log.debug(f"Broadcasting event to {len(kobj.network_targets)} network target(s)")
195
192
  else:
196
- logger.debug("No network targets set")
193
+ log.debug("No network targets set")
197
194
 
198
195
  for node in kobj.network_targets:
199
196
  self.event_queue.push_event_to(kobj.normalized_event, node)
@@ -1,6 +1,6 @@
1
1
  """Pydantic models for request and response objects in the KOI-net API."""
2
2
 
3
- from typing import Literal
3
+ from typing import Annotated, Literal
4
4
  from pydantic import BaseModel, Field
5
5
  from rid_lib import RID, RIDType
6
6
  from rid_lib.ext import Bundle, Manifest
@@ -60,4 +60,7 @@ class ErrorResponse(BaseModel):
60
60
 
61
61
  type RequestModels = EventsPayload | PollEvents | FetchRids | FetchManifests | FetchBundles
62
62
  type ResponseModels = RidsPayload | ManifestsPayload | BundlesPayload | EventsPayload | ErrorResponse
63
- type ApiModels = RequestModels | ResponseModels
63
+ type ApiModels = Annotated[
64
+ RequestModels | ResponseModels,
65
+ Field(discriminator="type")
66
+ ]
@@ -1,4 +1,4 @@
1
- import logging
1
+ import structlog
2
2
  from typing import Generic, TypeVar
3
3
  from pydantic import BaseModel, ConfigDict
4
4
  from rid_lib.types import KoiNetNode
@@ -6,8 +6,7 @@ from rid_lib.types import KoiNetNode
6
6
  from .secure import PrivateKey, PublicKey
7
7
  from .api_models import RequestModels, ResponseModels
8
8
 
9
-
10
- logger = logging.getLogger(__name__)
9
+ log = structlog.stdlib.get_logger()
11
10
 
12
11
 
13
12
  T = TypeVar("T", bound=RequestModels | ResponseModels)
@@ -28,7 +27,7 @@ class SignedEnvelope(BaseModel, Generic[T]):
28
27
  target_node=self.target_node
29
28
  )
30
29
 
31
- logger.debug(f"Verifying envelope: {unsigned_envelope.model_dump_json(exclude_none=True)}")
30
+ log.debug(f"Verifying envelope: {unsigned_envelope.model_dump_json(exclude_none=True)}")
32
31
 
33
32
  pub_key.verify(
34
33
  self.signature,
@@ -43,8 +42,8 @@ class UnsignedEnvelope(BaseModel, Generic[T]):
43
42
  target_node: KoiNetNode
44
43
 
45
44
  def sign_with(self, priv_key: PrivateKey) -> SignedEnvelope[T]:
46
- logger.debug(f"Signing envelope: {self.model_dump_json(exclude_none=True)}")
47
- logger.debug(f"Type: [{type(self.payload)}]")
45
+ log.debug(f"Signing envelope: {self.model_dump_json(exclude_none=True)}")
46
+ log.debug(f"Type: [{type(self.payload)}]")
48
47
 
49
48
  signature = priv_key.sign(
50
49
  self.model_dump_json(exclude_none=True).encode()
@@ -0,0 +1,61 @@
1
+ from typing import NamedTuple
2
+ from pydantic import BaseModel
3
+ from .envelope import SignedEnvelope
4
+ from .consts import (
5
+ BROADCAST_EVENTS_PATH,
6
+ POLL_EVENTS_PATH,
7
+ FETCH_BUNDLES_PATH,
8
+ FETCH_MANIFESTS_PATH,
9
+ FETCH_RIDS_PATH
10
+ )
11
+ from .api_models import (
12
+ EventsPayload,
13
+ PollEvents,
14
+ FetchBundles,
15
+ BundlesPayload,
16
+ FetchManifests,
17
+ ManifestsPayload,
18
+ FetchRids,
19
+ RidsPayload
20
+ )
21
+
22
+
23
+ class Models(NamedTuple):
24
+ request: type[BaseModel]
25
+ response: type[BaseModel] | None
26
+ request_envelope: type[SignedEnvelope]
27
+ response_envelope: type[SignedEnvelope] | None
28
+
29
+
30
+ API_MODEL_MAP: dict[str, Models] = {
31
+ BROADCAST_EVENTS_PATH: Models(
32
+ request=EventsPayload,
33
+ response=None,
34
+ request_envelope=SignedEnvelope[EventsPayload],
35
+ response_envelope=None
36
+ ),
37
+ POLL_EVENTS_PATH: Models(
38
+ request=PollEvents,
39
+ response=EventsPayload,
40
+ request_envelope=SignedEnvelope[PollEvents],
41
+ response_envelope=SignedEnvelope[EventsPayload]
42
+ ),
43
+ FETCH_BUNDLES_PATH: Models(
44
+ request=FetchBundles,
45
+ response=BundlesPayload,
46
+ request_envelope=SignedEnvelope[FetchBundles],
47
+ response_envelope=SignedEnvelope[BundlesPayload]
48
+ ),
49
+ FETCH_MANIFESTS_PATH: Models(
50
+ request=FetchManifests,
51
+ response=ManifestsPayload,
52
+ request_envelope=SignedEnvelope[FetchManifests],
53
+ response_envelope=SignedEnvelope[ManifestsPayload]
54
+ ),
55
+ FETCH_RIDS_PATH: Models(
56
+ request=FetchRids,
57
+ response=RidsPayload,
58
+ request_envelope=SignedEnvelope[FetchRids],
59
+ response_envelope=SignedEnvelope[RidsPayload]
60
+ )
61
+ }
@@ -1,4 +1,4 @@
1
- import logging
1
+ import structlog
2
2
  from base64 import b64decode, b64encode
3
3
  from cryptography.hazmat.primitives import hashes
4
4
  from cryptography.hazmat.primitives.asymmetric import ec
@@ -9,7 +9,7 @@ from cryptography.hazmat.primitives.asymmetric.utils import (
9
9
  encode_dss_signature
10
10
  )
11
11
 
12
- logger = logging.getLogger(__name__)
12
+ log = structlog.stdlib.get_logger()
13
13
 
14
14
 
15
15
  def der_to_raw_signature(der_signature: bytes, curve=ec.SECP256R1()) -> bytes:
@@ -91,9 +91,9 @@ class PrivateKey:
91
91
 
92
92
  signature = b64encode(raw_signature_bytes).decode()
93
93
 
94
- logger.debug(f"Signing message with [{self.public_key().to_der()}]")
95
- logger.debug(f"hash: {hashed_message}")
96
- logger.debug(f"signature: {signature}")
94
+ log.debug(f"Signing message with [{self.public_key().to_der()}]")
95
+ log.debug(f"hash: {hashed_message}")
96
+ log.debug(f"signature: {signature}")
97
97
 
98
98
  return signature
99
99
 
@@ -144,9 +144,9 @@ class PublicKey:
144
144
  # print()
145
145
  # print(message.decode())
146
146
 
147
- # logger.debug(f"Verifying message with [{self.to_der()}]")
148
- # logger.debug(f"hash: {hashed_message}")
149
- # logger.debug(f"signature: {signature}")
147
+ # log.debug(f"Verifying message with [{self.to_der()}]")
148
+ # log.debug(f"hash: {hashed_message}")
149
+ # log.debug(f"signature: {signature}")
150
150
 
151
151
  raw_signature_bytes = b64decode(signature)
152
152
  der_signature_bytes = raw_to_der_signature(raw_signature_bytes)
koi_net/secure.py CHANGED
@@ -1,4 +1,4 @@
1
- import logging
1
+ import structlog
2
2
  from functools import wraps
3
3
 
4
4
  import cryptography.exceptions
@@ -20,7 +20,7 @@ from .protocol.errors import (
20
20
  )
21
21
  from .config import NodeConfig
22
22
 
23
- logger = logging.getLogger(__name__)
23
+ log = structlog.stdlib.get_logger()
24
24
 
25
25
 
26
26
  class Secure:
@@ -120,15 +120,15 @@ class Secure:
120
120
  """
121
121
  @wraps(func)
122
122
  async def wrapper(req: SignedEnvelope, *args, **kwargs) -> SignedEnvelope | None:
123
- logger.info("Validating envelope")
123
+ log.info("Validating envelope")
124
124
 
125
125
  self.validate_envelope(req)
126
- logger.info("Calling endpoint handler")
126
+ log.info("Calling endpoint handler")
127
127
 
128
128
  result = await func(req, *args, **kwargs)
129
129
 
130
130
  if result is not None:
131
- logger.info("Creating response envelope")
131
+ log.info("Creating response envelope")
132
132
  return self.create_envelope(
133
133
  payload=result,
134
134
  target=req.source_node
koi_net/sentry.py ADDED
@@ -0,0 +1,13 @@
1
+ # import sentry_sdk
2
+ # from sentry_sdk.integrations.logging import LoggingIntegration
3
+
4
+ # sentry_sdk.init(
5
+ # dsn="https://7bbafef3c7dbd652506db3cb2aca9f98@o4510149352357888.ingest.us.sentry.io/4510149355765760",
6
+ # # Add data like request headers and IP for users,
7
+ # # see https://docs.sentry.io/platforms/python/data-management/data-collected/ for more info
8
+ # send_default_pii=True,
9
+ # enable_logs=True,
10
+ # integrations=[
11
+ # LoggingIntegration(sentry_logs_level=None)
12
+ # ]
13
+ # )
koi_net/server.py CHANGED
@@ -1,46 +1,25 @@
1
- import logging
1
+ import structlog
2
2
  import uvicorn
3
3
  from contextlib import asynccontextmanager
4
4
  from fastapi import FastAPI, APIRouter
5
5
  from fastapi.responses import JSONResponse
6
6
 
7
- from koi_net.poll_event_buffer import PollEventBuffer
7
+ from koi_net.interfaces.entrypoint import EntryPoint
8
+
8
9
  from .network.response_handler import ResponseHandler
9
- from .processor.kobj_queue import KobjQueue
10
- from .protocol.api_models import (
11
- PollEvents,
12
- FetchRids,
13
- FetchManifests,
14
- FetchBundles,
15
- EventsPayload,
16
- RidsPayload,
17
- ManifestsPayload,
18
- BundlesPayload,
19
- ErrorResponse
20
- )
10
+ from .protocol.model_map import API_MODEL_MAP
11
+ from .protocol.api_models import ErrorResponse
21
12
  from .protocol.errors import ProtocolError
22
- from .protocol.envelope import SignedEnvelope
23
- from .protocol.consts import (
24
- BROADCAST_EVENTS_PATH,
25
- POLL_EVENTS_PATH,
26
- FETCH_RIDS_PATH,
27
- FETCH_MANIFESTS_PATH,
28
- FETCH_BUNDLES_PATH
29
- )
30
- from .secure import Secure
31
13
  from .lifecycle import NodeLifecycle
32
14
  from .config import NodeConfig
33
15
 
34
- logger = logging.getLogger(__name__)
16
+ log = structlog.stdlib.get_logger()
35
17
 
36
18
 
37
- class NodeServer:
19
+ class NodeServer(EntryPoint):
38
20
  """Manages FastAPI server and event handling for full nodes."""
39
21
  config: NodeConfig
40
22
  lifecycle: NodeLifecycle
41
- secure: Secure
42
- kobj_queue: KobjQueue
43
- poll_event_buf: PollEventBuffer
44
23
  response_handler: ResponseHandler
45
24
  app: FastAPI
46
25
  router: APIRouter
@@ -49,16 +28,10 @@ class NodeServer:
49
28
  self,
50
29
  config: NodeConfig,
51
30
  lifecycle: NodeLifecycle,
52
- secure: Secure,
53
- kobj_queue: KobjQueue,
54
- poll_event_buf: PollEventBuffer,
55
- response_handler: ResponseHandler
31
+ response_handler: ResponseHandler,
56
32
  ):
57
33
  self.config = config
58
34
  self.lifecycle = lifecycle
59
- self.secure = secure
60
- self.kobj_queue = kobj_queue
61
- self.poll_event_buf = poll_event_buf
62
35
  self.response_handler = response_handler
63
36
  self._build_app()
64
37
 
@@ -75,71 +48,48 @@ class NodeServer:
75
48
  version="1.0.0"
76
49
  )
77
50
 
78
- self.router = APIRouter(prefix="/koi-net")
79
51
  self.app.add_exception_handler(ProtocolError, self.protocol_error_handler)
80
52
 
81
- def _add_endpoint(path, func):
53
+ self.router = APIRouter(prefix="/koi-net")
54
+
55
+ for path, models in API_MODEL_MAP.items():
56
+ def create_endpoint(path: str):
57
+ async def endpoint(req):
58
+ return self.response_handler.handle_response(path, req)
59
+
60
+ # programmatically setting type hint annotations for FastAPI's model validation
61
+ endpoint.__annotations__ = {
62
+ "req": models.request_envelope,
63
+ "return": models.response_envelope
64
+ }
65
+
66
+ return endpoint
67
+
82
68
  self.router.add_api_route(
83
69
  path=path,
84
- endpoint=self.secure.envelope_handler(func),
70
+ endpoint=create_endpoint(path),
85
71
  methods=["POST"],
86
72
  response_model_exclude_none=True
87
73
  )
88
74
 
89
- _add_endpoint(BROADCAST_EVENTS_PATH, self.broadcast_events)
90
- _add_endpoint(POLL_EVENTS_PATH, self.poll_events)
91
- _add_endpoint(FETCH_RIDS_PATH, self.fetch_rids)
92
- _add_endpoint(FETCH_MANIFESTS_PATH, self.fetch_manifests)
93
- _add_endpoint(FETCH_BUNDLES_PATH, self.fetch_bundles)
94
-
95
75
  self.app.include_router(self.router)
96
-
97
- def run(self):
98
- """Starts FastAPI server and event handler."""
99
- uvicorn.run(
100
- app=self.app,
101
- host=self.config.server.host,
102
- port=self.config.server.port
103
- )
104
76
 
105
77
  def protocol_error_handler(self, request, exc: ProtocolError):
106
78
  """Catches `ProtocolError` and returns as `ErrorResponse`."""
107
- logger.info(f"caught protocol error: {exc}")
79
+ log.info(f"caught protocol error: {exc}")
108
80
  resp = ErrorResponse(error=exc.error_type)
109
- logger.info(f"returning error response: {resp}")
81
+ log.info(f"returning error response: {resp}")
110
82
  return JSONResponse(
111
83
  status_code=400,
112
84
  content=resp.model_dump(mode="json")
113
85
  )
114
-
115
- async def broadcast_events(self, req: SignedEnvelope[EventsPayload]):
116
- """Handles events broadcast endpoint."""
117
- logger.info(f"Request to {BROADCAST_EVENTS_PATH}, received {len(req.payload.events)} event(s)")
118
- for event in req.payload.events:
119
- self.kobj_queue.put_kobj(event=event, source=req.source_node)
120
-
121
- async def poll_events(
122
- self, req: SignedEnvelope[PollEvents]
123
- ) -> SignedEnvelope[EventsPayload] | ErrorResponse:
124
- """Handles poll events endpoint."""
125
- logger.info(f"Request to {POLL_EVENTS_PATH}")
126
- events = self.poll_event_buf.flush(req.source_node)
127
- return EventsPayload(events=events)
128
-
129
- async def fetch_rids(
130
- self, req: SignedEnvelope[FetchRids]
131
- ) -> SignedEnvelope[RidsPayload] | ErrorResponse:
132
- """Handles fetch RIDs endpoint."""
133
- return self.response_handler.fetch_rids(req.payload, req.source_node)
134
-
135
- async def fetch_manifests(
136
- self, req: SignedEnvelope[FetchManifests]
137
- ) -> SignedEnvelope[ManifestsPayload] | ErrorResponse:
138
- """Handles fetch manifests endpoint."""
139
- return self.response_handler.fetch_manifests(req.payload, req.source_node)
140
-
141
- async def fetch_bundles(
142
- self, req: SignedEnvelope[FetchBundles]
143
- ) -> SignedEnvelope[BundlesPayload] | ErrorResponse:
144
- """Handles fetch bundles endpoint."""
145
- return self.response_handler.fetch_bundles(req.payload, req.source_node)
86
+
87
+ def run(self):
88
+ """Starts FastAPI server and event handler."""
89
+ uvicorn.run(
90
+ app=self.app,
91
+ host=self.config.server.host,
92
+ port=self.config.server.port,
93
+ log_config=None,
94
+ access_log=False
95
+ )
@@ -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'
@@ -0,0 +1,52 @@
1
+ koi_net/__init__.py,sha256=ZJQHfYaMtu3Qv6ZtKxq49N0T1GbVd3dvrjXZdeNYnQw,41
2
+ koi_net/assembler.py,sha256=kV7qB1xnPvAVUrunaNgx4f9lb2qQlvlA4zZs1xKq62Q,2831
3
+ koi_net/config.py,sha256=yV53GFQrJchyKKRs3sOhj27pyM6rwiUhIgDZ81rvZ48,5238
4
+ koi_net/context.py,sha256=mBKMR1o6Tqy_cyhj7_niGkMSMOQeEMPhP9cpNDlML1s,1323
5
+ koi_net/core.py,sha256=owbJYN_kCMwKz3ecWXt_tQXOEECL_q1uq4CcFAuR6EA,2428
6
+ koi_net/default_actions.py,sha256=y9oaDeb3jJGSvVbuonmyGWkKWlzpEX3V32MjgEuYWc8,622
7
+ koi_net/effector.py,sha256=0hW2NnHWjVZyRPXWh_X_0EBxqpCJBBhJCHFvB6uC3SM,4941
8
+ koi_net/handshaker.py,sha256=sd2bYe7Fh5hCTq5q18_knKWAMzvH5a6zHzCcBKbkhWQ,1229
9
+ koi_net/identity.py,sha256=jZjTSlgO8aJhfOx6-ppQVveHih6h0dP2Gtbsw6GGdTI,569
10
+ koi_net/lifecycle.py,sha256=AhJCkXHuBhUHMXXAzI3akz0pTRE77aNEHwjsnbe98TE,4775
11
+ koi_net/logger.py,sha256=EzpQNvNrbi5LmMCobyuN1_wOz5VuMDeaPbLcyMrLKGI,6172
12
+ koi_net/poller.py,sha256=OZLYAMHxXxM75ENetHd_nnNaiiZTSpMgeP3xxAphVEg,1463
13
+ koi_net/secure.py,sha256=qayAzO7xWF8wa33rmNCTupVVaYXwNuimlXpPFWJfQT8,4911
14
+ koi_net/sentry.py,sha256=9UCVMPoEzua3KMUHe4inciqEnqTF_gNSQO2zhpUN8ts,506
15
+ koi_net/server.py,sha256=GJbkHp2BmDhSHWdF6PObLus7f0BiMZuhcZALj7kNiF4,3087
16
+ koi_net/utils.py,sha256=4K6BqtifExDUfPP8zJAICgPV1mcabz_MaFB0P0fSnic,411
17
+ koi_net/cli/__init__.py,sha256=Vpq0yLN0ROyF0qQYsyiuWFbjs-zsa90G1DpAv13qaiw,25
18
+ koi_net/cli/commands.py,sha256=5eVsvJAnHjwJdt8MNYrk5zSzWWm1ooiP35bJeQxYIq4,2592
19
+ koi_net/cli/models.py,sha256=ON-rqMgHDng3J2hbBbGYzkynaWc9Tq4QfOJIu_6pFuA,1190
20
+ koi_net/interfaces/entrypoint.py,sha256=8FEG1r0YIws_1AojTtIPWB-Th3GOS3U2qi89RlyBJYs,80
21
+ koi_net/interfaces/worker.py,sha256=IvtXBTVmthUrURmB9Cn9wffeVv9_Cphsk97_GCLb5NQ,294
22
+ koi_net/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ koi_net/network/error_handler.py,sha256=4i_r7EWBW52_bhdmyU-1A_Cnu0Q-jHO9O1EXgO1NGws,1784
24
+ koi_net/network/event_queue.py,sha256=JqSMRk_PvBn5hlnUKF2jl9uxaOCX793fwZsblx-pEKs,743
25
+ koi_net/network/graph.py,sha256=ZJ1CY-SnYhnZAMm3e9YCbBflk80ZDETnoQKaBk5kLgo,4161
26
+ koi_net/network/poll_event_buffer.py,sha256=PO9HnmlhQ0V-uPGOf5b15GhbJtjAzEfMDf6TtsQLtPA,734
27
+ koi_net/network/request_handler.py,sha256=XSX-JAqRua42YOakmia--9NnKcT32Fpoc-7aYfjDpwg,6752
28
+ koi_net/network/resolver.py,sha256=i8YsdIYVo9--YYW5AK9PO-f_lpXIVKZONImJ9wYMySY,5277
29
+ koi_net/network/response_handler.py,sha256=OyEd_5ltQf-sdGDI4QalPuHVLqrSMpAjRQCDtlNR8WU,4216
30
+ koi_net/processor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ koi_net/processor/event_worker.py,sha256=z6Zef5TFDokwywSNzaMh6oDmEaAoss6m--dRHHmU3YY,4190
32
+ koi_net/processor/handler.py,sha256=PJlQCPfKm3zYsJrwmGIw5xZmc6vArKz_UoeJuXBj16Y,2356
33
+ koi_net/processor/handlers.py,sha256=rGvjrud72szKBM8rhZRrQ2qO8Ng97oGLIfUG9Af-oxw,10934
34
+ koi_net/processor/knowledge_object.py,sha256=0VyBOqkCoINMK3fqYhLlXU-Ri7gKEpJloDHfhaGITxg,4172
35
+ koi_net/processor/kobj_queue.py,sha256=A4jdS0WCrL-fg3Kl8bHPbzIjSfYNGOBBqzCbSJhfMZ4,1837
36
+ koi_net/processor/kobj_worker.py,sha256=26DG8fU3hLbKZmS6mcuOEnUWcVLkq-1fl8AGNLrYFY0,1237
37
+ koi_net/processor/pipeline.py,sha256=FpU3nORIeJxJuX8QHTSqIji8iMU69GuTR4UMeThOzUo,8668
38
+ koi_net/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ koi_net/protocol/api_models.py,sha256=lPd_w-sncyrQlTQ__iae9Li1lFARu8nKiMMlXu7-Hls,1856
40
+ koi_net/protocol/consts.py,sha256=bisbVEojPIHlLhkLafBzfIhH25TjNfvTORF1g6YXzIM,243
41
+ koi_net/protocol/edge.py,sha256=PzdEhC43T1KO5iMSEu7I4tiz-7sZxtz41dJfWf-oHA0,1034
42
+ koi_net/protocol/envelope.py,sha256=4SKF4tP1P1ex7UEmYOJt7IP9_8fCWsa_o6ZBOu8SOKk,1877
43
+ koi_net/protocol/errors.py,sha256=uKPQ-TGLouZuK0xd2pXuCQoRTyu_JFsydSCLml13Cz8,595
44
+ koi_net/protocol/event.py,sha256=HxzLN-iCXPyr2YzrswMIkgZYeUdFbBpa5v98dAB06lQ,1328
45
+ koi_net/protocol/model_map.py,sha256=W0quo25HqVkzp8HQ7l_FNTtN4ZX0u7MIqp68Ir81fuk,1671
46
+ koi_net/protocol/node.py,sha256=KH_SjHDzW-V4pn2tqpTDhIzfuCgKgM1YqCafyofLN3k,466
47
+ koi_net/protocol/secure.py,sha256=y3-l0ldQ4oQ6ReEmunDy0ncF836kZvLdGFeEgmuJw44,5110
48
+ koi_net-1.2.0b2.dist-info/METADATA,sha256=t4T3T0cGyG24XvptQg6vy10B8_gHCWGW-s0PntA9aX8,37347
49
+ koi_net-1.2.0b2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
50
+ koi_net-1.2.0b2.dist-info/entry_points.txt,sha256=l7He_JTyXrfKIHkttnPWXHI717v8WpLLfCduL5QcEWA,40
51
+ koi_net-1.2.0b2.dist-info/licenses/LICENSE,sha256=03mgCL5qth2aD9C3F3qNVs4sFJSpK9kjtYCyOwdSp7s,1069
52
+ koi_net-1.2.0b2.dist-info/RECORD,,
koi_net/behaviors.py DELETED
@@ -1,51 +0,0 @@
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
- )