koi-net 1.1.0b7__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.

Files changed (41) hide show
  1. koi_net/__init__.py +1 -1
  2. koi_net/behaviors.py +51 -0
  3. koi_net/cli/__init__.py +1 -0
  4. koi_net/cli/commands.py +99 -0
  5. koi_net/cli/models.py +41 -0
  6. koi_net/config.py +34 -0
  7. koi_net/context.py +16 -20
  8. koi_net/core.py +208 -171
  9. koi_net/default_actions.py +10 -1
  10. koi_net/effector.py +39 -24
  11. koi_net/{network/behavior.py → handshaker.py} +17 -20
  12. koi_net/kobj_worker.py +45 -0
  13. koi_net/lifecycle.py +71 -41
  14. koi_net/models.py +14 -0
  15. koi_net/network/error_handler.py +12 -10
  16. koi_net/network/event_queue.py +14 -184
  17. koi_net/network/graph.py +14 -9
  18. koi_net/network/request_handler.py +31 -19
  19. koi_net/network/resolver.py +6 -9
  20. koi_net/network/response_handler.py +5 -6
  21. koi_net/poll_event_buffer.py +17 -0
  22. koi_net/poller.py +12 -5
  23. koi_net/processor/default_handlers.py +84 -35
  24. koi_net/processor/event_worker.py +121 -0
  25. koi_net/processor/handler.py +4 -2
  26. koi_net/processor/knowledge_object.py +19 -7
  27. koi_net/processor/knowledge_pipeline.py +7 -26
  28. koi_net/processor/kobj_queue.py +51 -0
  29. koi_net/protocol/api_models.py +3 -2
  30. koi_net/protocol/node.py +3 -3
  31. koi_net/secure.py +28 -8
  32. koi_net/server.py +25 -9
  33. koi_net/utils.py +18 -0
  34. koi_net/worker.py +10 -0
  35. {koi_net-1.1.0b7.dist-info → koi_net-1.2.0b1.dist-info}/METADATA +7 -3
  36. koi_net-1.2.0b1.dist-info/RECORD +49 -0
  37. koi_net-1.2.0b1.dist-info/entry_points.txt +2 -0
  38. koi_net/processor/interface.py +0 -101
  39. koi_net-1.1.0b7.dist-info/RECORD +0 -38
  40. {koi_net-1.1.0b7.dist-info → koi_net-1.2.0b1.dist-info}/WHEEL +0 -0
  41. {koi_net-1.1.0b7.dist-info → koi_net-1.2.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,9 @@
1
1
  import logging
2
- from typing import Callable
3
- from rid_lib.core import RIDType
4
2
  from rid_lib.types import KoiNetEdge, KoiNetNode
5
3
  from rid_lib.ext import Cache
6
4
  from ..protocol.event import EventType
7
5
  from ..network.request_handler import RequestHandler
8
- from ..network.event_queue import NetworkEventQueue
6
+ from ..network.event_queue import EventQueue
9
7
  from ..network.graph import NetworkGraph
10
8
  from ..identity import NodeIdentity
11
9
  from .handler import (
@@ -28,42 +26,26 @@ class KnowledgePipeline:
28
26
  cache: Cache
29
27
  identity: NodeIdentity
30
28
  request_handler: RequestHandler
31
- event_queue: NetworkEventQueue
29
+ event_queue: EventQueue
32
30
  graph: NetworkGraph
33
- handlers: list[KnowledgeHandler]
31
+ knowledge_handlers: list[KnowledgeHandler]
34
32
 
35
33
  def __init__(
36
34
  self,
37
35
  handler_context: "HandlerContext",
38
36
  cache: Cache,
39
37
  request_handler: RequestHandler,
40
- event_queue: NetworkEventQueue,
38
+ event_queue: EventQueue,
41
39
  graph: NetworkGraph,
42
- default_handlers: list[KnowledgeHandler] = []
40
+ knowledge_handlers: list[KnowledgeHandler] = []
43
41
  ):
44
42
  self.handler_context = handler_context
45
43
  self.cache = cache
46
44
  self.request_handler = request_handler
47
45
  self.event_queue = event_queue
48
46
  self.graph = graph
49
- self.handlers = default_handlers
47
+ self.knowledge_handlers = knowledge_handlers
50
48
 
51
- def add_handler(self, handler: KnowledgeHandler):
52
- self.handlers.append(handler)
53
-
54
- def register_handler(
55
- self,
56
- handler_type: HandlerType,
57
- rid_types: list[RIDType] | None = None,
58
- event_types: list[EventType | None] | None = None
59
- ):
60
- """Assigns decorated function as handler for this processor."""
61
- def decorator(func: Callable) -> Callable:
62
- handler = KnowledgeHandler(func, handler_type, rid_types, event_types)
63
- self.add_handler(handler)
64
- return func
65
- return decorator
66
-
67
49
  def call_handler_chain(
68
50
  self,
69
51
  handler_type: HandlerType,
@@ -79,7 +61,7 @@ class KnowledgePipeline:
79
61
  Handlers will only be called in the chain if their handler and RID type match that of the inputted knowledge object.
80
62
  """
81
63
 
82
- for handler in self.handlers:
64
+ for handler in self.knowledge_handlers:
83
65
  if handler_type != handler.handler_type:
84
66
  continue
85
67
 
@@ -215,6 +197,5 @@ class KnowledgePipeline:
215
197
 
216
198
  for node in kobj.network_targets:
217
199
  self.event_queue.push_event_to(kobj.normalized_event, node)
218
- self.event_queue.flush_webhook_queue(node)
219
200
 
220
201
  kobj = self.call_handler_chain(HandlerType.Final, kobj)
@@ -0,0 +1,51 @@
1
+ import logging
2
+ from queue import Queue
3
+ from rid_lib.core import RID
4
+ from rid_lib.ext import Bundle, Manifest
5
+ from rid_lib.types import KoiNetNode
6
+ from ..protocol.event import Event, EventType
7
+ from .knowledge_object import KnowledgeObject
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ class KobjQueue:
13
+ """Provides access to this node's knowledge processing pipeline."""
14
+ q: Queue[KnowledgeObject]
15
+
16
+ def __init__(self):
17
+ self.q = Queue()
18
+
19
+ def put_kobj(
20
+ self,
21
+ rid: RID | None = None,
22
+ manifest: Manifest | None = None,
23
+ bundle: Bundle | None = None,
24
+ event: Event | None = None,
25
+ kobj: KnowledgeObject | None = None,
26
+ event_type: EventType | None = None,
27
+ source: KoiNetNode | None = None
28
+ ):
29
+ """Queues knowledge object to be handled by processing pipeline.
30
+
31
+ Knowledge may take the form of an RID, manifest, bundle, event,
32
+ or knowledge object (with an optional event type for RID,
33
+ manifest, or bundle objects). All objects will be normalized
34
+ to knowledge objects and queued. If `flush` is `True`, the queue
35
+ will be flushed immediately after adding the new knowledge.
36
+ """
37
+ if rid:
38
+ _kobj = KnowledgeObject.from_rid(rid, event_type, source)
39
+ elif manifest:
40
+ _kobj = KnowledgeObject.from_manifest(manifest, event_type, source)
41
+ elif bundle:
42
+ _kobj = KnowledgeObject.from_bundle(bundle, event_type, source)
43
+ elif event:
44
+ _kobj = KnowledgeObject.from_event(event, source)
45
+ elif kobj:
46
+ _kobj = kobj
47
+ else:
48
+ raise ValueError("One of 'rid', 'manifest', 'bundle', 'event', or 'kobj' must be provided")
49
+
50
+ self.q.put(_kobj)
51
+ logger.debug(f"Queued {_kobj!r}")
@@ -1,4 +1,4 @@
1
- """Pydantic models for request and response/payload objects in the KOI-net API."""
1
+ """Pydantic models for request and response objects in the KOI-net API."""
2
2
 
3
3
  from typing import Literal
4
4
  from pydantic import BaseModel, Field
@@ -59,4 +59,5 @@ class ErrorResponse(BaseModel):
59
59
  # TYPES
60
60
 
61
61
  type RequestModels = EventsPayload | PollEvents | FetchRids | FetchManifests | FetchBundles
62
- type ResponseModels = RidsPayload | ManifestsPayload | BundlesPayload | EventsPayload | ErrorResponse
62
+ type ResponseModels = RidsPayload | ManifestsPayload | BundlesPayload | EventsPayload | ErrorResponse
63
+ type ApiModels = RequestModels | ResponseModels
koi_net/protocol/node.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from enum import StrEnum
2
- from pydantic import BaseModel
2
+ from pydantic import BaseModel, Field
3
3
  from rid_lib import RIDType
4
4
 
5
5
 
@@ -8,8 +8,8 @@ class NodeType(StrEnum):
8
8
  PARTIAL = "PARTIAL"
9
9
 
10
10
  class NodeProvides(BaseModel):
11
- event: list[RIDType] = []
12
- state: list[RIDType] = []
11
+ event: list[RIDType] = Field(default_factory=list)
12
+ state: list[RIDType] = Field(default_factory=list)
13
13
 
14
14
  class NodeProfile(BaseModel):
15
15
  base_url: str | None = None
koi_net/secure.py CHANGED
@@ -2,12 +2,13 @@ import logging
2
2
  from functools import wraps
3
3
 
4
4
  import cryptography.exceptions
5
- from rid_lib.ext import Bundle
5
+ from rid_lib.ext import Bundle, Cache
6
6
  from rid_lib.ext.utils import sha256_hash
7
+ from rid_lib.types import KoiNetNode
7
8
  from .identity import NodeIdentity
8
9
  from .protocol.envelope import UnsignedEnvelope, SignedEnvelope
9
10
  from .protocol.secure import PublicKey
10
- from .protocol.api_models import EventsPayload
11
+ from .protocol.api_models import ApiModels, EventsPayload
11
12
  from .protocol.event import EventType
12
13
  from .protocol.node import NodeProfile
13
14
  from .protocol.secure import PrivateKey
@@ -17,31 +18,32 @@ from .protocol.errors import (
17
18
  InvalidSignatureError,
18
19
  InvalidTargetError
19
20
  )
20
- from .effector import Effector
21
21
  from .config import NodeConfig
22
22
 
23
23
  logger = logging.getLogger(__name__)
24
24
 
25
25
 
26
26
  class Secure:
27
+ """Subsystem handling secure protocol logic."""
27
28
  identity: NodeIdentity
28
- effector: Effector
29
+ cache: Cache
29
30
  config: NodeConfig
30
31
  priv_key: PrivateKey
31
32
 
32
33
  def __init__(
33
34
  self,
34
35
  identity: NodeIdentity,
35
- effector: Effector,
36
+ cache: Cache,
36
37
  config: NodeConfig
37
38
  ):
38
39
  self.identity = identity
39
- self.effector = effector
40
+ self.cache = cache
40
41
  self.config = config
41
42
 
42
43
  self.priv_key = self._load_priv_key()
43
44
 
44
45
  def _load_priv_key(self) -> PrivateKey:
46
+ """Loads private key from PEM file path in config."""
45
47
  with open(self.config.koi_net.private_key_pem_path, "r") as f:
46
48
  priv_key_pem = f.read()
47
49
 
@@ -51,6 +53,14 @@ class Secure:
51
53
  )
52
54
 
53
55
  def _handle_unknown_node(self, envelope: SignedEnvelope) -> Bundle | None:
56
+ """Attempts to find node profile in proided envelope.
57
+
58
+ If an unknown node sends an envelope, it may still be able to be
59
+ validated if that envelope contains their node profile. This is
60
+ essential for allowing unknown nodes to handshake and introduce
61
+ themselves. Only an `EventsPayload` contain a `NEW` event for a
62
+ node profile for the source node is permissible.
63
+ """
54
64
  if type(envelope.payload) != EventsPayload:
55
65
  return None
56
66
 
@@ -64,7 +74,10 @@ class Secure:
64
74
  return event.bundle
65
75
  return None
66
76
 
67
- def create_envelope(self, payload, target) -> SignedEnvelope:
77
+ def create_envelope(
78
+ self, payload: ApiModels, target: KoiNetNode
79
+ ) -> SignedEnvelope:
80
+ """Returns signed envelope to target from provided payload."""
68
81
  return UnsignedEnvelope(
69
82
  payload=payload,
70
83
  source_node=self.identity.rid,
@@ -72,8 +85,10 @@ class Secure:
72
85
  ).sign_with(self.priv_key)
73
86
 
74
87
  def validate_envelope(self, envelope: SignedEnvelope):
88
+ """Validates signed envelope from another node."""
89
+
75
90
  node_bundle = (
76
- self.effector.deref(envelope.source_node) or
91
+ self.cache.read(envelope.source_node) or
77
92
  self._handle_unknown_node(envelope)
78
93
  )
79
94
 
@@ -98,6 +113,11 @@ class Secure:
98
113
  raise InvalidTargetError(f"Envelope target {envelope.target_node!r} is not me")
99
114
 
100
115
  def envelope_handler(self, func):
116
+ """Wrapper function validates envelopes for server endpoints.
117
+
118
+ Validates incoming envelope and passes payload to endpoint
119
+ handler. Resulting payload is returned as a signed envelope.
120
+ """
101
121
  @wraps(func)
102
122
  async def wrapper(req: SignedEnvelope, *args, **kwargs) -> SignedEnvelope | None:
103
123
  logger.info("Validating envelope")
koi_net/server.py CHANGED
@@ -3,9 +3,10 @@ import uvicorn
3
3
  from contextlib import asynccontextmanager
4
4
  from fastapi import FastAPI, APIRouter
5
5
  from fastapi.responses import JSONResponse
6
- from .network.event_queue import NetworkEventQueue
6
+
7
+ from koi_net.poll_event_buffer import PollEventBuffer
7
8
  from .network.response_handler import ResponseHandler
8
- from .processor.interface import ProcessorInterface
9
+ from .processor.kobj_queue import KobjQueue
9
10
  from .protocol.api_models import (
10
11
  PollEvents,
11
12
  FetchRids,
@@ -34,27 +35,35 @@ logger = logging.getLogger(__name__)
34
35
 
35
36
 
36
37
  class NodeServer:
38
+ """Manages FastAPI server and event handling for full nodes."""
39
+ config: NodeConfig
37
40
  lifecycle: NodeLifecycle
41
+ secure: Secure
42
+ kobj_queue: KobjQueue
43
+ poll_event_buf: PollEventBuffer
44
+ response_handler: ResponseHandler
45
+ app: FastAPI
46
+ router: APIRouter
38
47
 
39
48
  def __init__(
40
49
  self,
41
50
  config: NodeConfig,
42
51
  lifecycle: NodeLifecycle,
43
52
  secure: Secure,
44
- processor: ProcessorInterface,
45
- event_queue: NetworkEventQueue,
53
+ kobj_queue: KobjQueue,
54
+ poll_event_buf: PollEventBuffer,
46
55
  response_handler: ResponseHandler
47
56
  ):
48
57
  self.config = config
49
58
  self.lifecycle = lifecycle
50
59
  self.secure = secure
51
- self.processor = processor
52
- self.event_queue = event_queue
60
+ self.kobj_queue = kobj_queue
61
+ self.poll_event_buf = poll_event_buf
53
62
  self.response_handler = response_handler
54
63
  self._build_app()
55
64
 
56
65
  def _build_app(self):
57
-
66
+ """Builds FastAPI app and adds endpoints."""
58
67
  @asynccontextmanager
59
68
  async def lifespan(*args, **kwargs):
60
69
  async with self.lifecycle.async_run():
@@ -86,6 +95,7 @@ class NodeServer:
86
95
  self.app.include_router(self.router)
87
96
 
88
97
  def run(self):
98
+ """Starts FastAPI server and event handler."""
89
99
  uvicorn.run(
90
100
  app=self.app,
91
101
  host=self.config.server.host,
@@ -93,6 +103,7 @@ class NodeServer:
93
103
  )
94
104
 
95
105
  def protocol_error_handler(self, request, exc: ProtocolError):
106
+ """Catches `ProtocolError` and returns as `ErrorResponse`."""
96
107
  logger.info(f"caught protocol error: {exc}")
97
108
  resp = ErrorResponse(error=exc.error_type)
98
109
  logger.info(f"returning error response: {resp}")
@@ -102,28 +113,33 @@ class NodeServer:
102
113
  )
103
114
 
104
115
  async def broadcast_events(self, req: SignedEnvelope[EventsPayload]):
116
+ """Handles events broadcast endpoint."""
105
117
  logger.info(f"Request to {BROADCAST_EVENTS_PATH}, received {len(req.payload.events)} event(s)")
106
118
  for event in req.payload.events:
107
- self.processor.handle(event=event, source=req.source_node)
119
+ self.kobj_queue.put_kobj(event=event, source=req.source_node)
108
120
 
109
121
  async def poll_events(
110
122
  self, req: SignedEnvelope[PollEvents]
111
123
  ) -> SignedEnvelope[EventsPayload] | ErrorResponse:
124
+ """Handles poll events endpoint."""
112
125
  logger.info(f"Request to {POLL_EVENTS_PATH}")
113
- events = self.event_queue.flush_poll_queue(req.source_node)
126
+ events = self.poll_event_buf.flush(req.source_node)
114
127
  return EventsPayload(events=events)
115
128
 
116
129
  async def fetch_rids(
117
130
  self, req: SignedEnvelope[FetchRids]
118
131
  ) -> SignedEnvelope[RidsPayload] | ErrorResponse:
132
+ """Handles fetch RIDs endpoint."""
119
133
  return self.response_handler.fetch_rids(req.payload, req.source_node)
120
134
 
121
135
  async def fetch_manifests(
122
136
  self, req: SignedEnvelope[FetchManifests]
123
137
  ) -> SignedEnvelope[ManifestsPayload] | ErrorResponse:
138
+ """Handles fetch manifests endpoint."""
124
139
  return self.response_handler.fetch_manifests(req.payload, req.source_node)
125
140
 
126
141
  async def fetch_bundles(
127
142
  self, req: SignedEnvelope[FetchBundles]
128
143
  ) -> SignedEnvelope[BundlesPayload] | ErrorResponse:
144
+ """Handles fetch bundles endpoint."""
129
145
  return self.response_handler.fetch_bundles(req.payload, req.source_node)
koi_net/utils.py ADDED
@@ -0,0 +1,18 @@
1
+ from typing import Callable
2
+
3
+ from rid_lib import RID
4
+ from rid_lib.ext import Bundle, Cache
5
+
6
+ cache = Cache()
7
+
8
+ def build_dereferencer(
9
+ *funcs: list[Callable[[RID], Bundle | None]]
10
+ ) -> Callable[[RID], Bundle | None]:
11
+ def any_of(rid: RID):
12
+ return any(
13
+ f(rid) for f in funcs
14
+ )
15
+ return any_of
16
+
17
+ deref = build_dereferencer(cache.read)
18
+ deref(RID.from_string("string:hello_world"))
koi_net/worker.py ADDED
@@ -0,0 +1,10 @@
1
+ import threading
2
+
3
+ class ThreadWorker:
4
+ thread: threading.Thread
5
+
6
+ def __init__(self):
7
+ self.thread = threading.Thread(target=self.run)
8
+
9
+ def run(self):
10
+ ...
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: koi-net
3
- Version: 1.1.0b7
3
+ Version: 1.2.0b1
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>
@@ -33,14 +33,18 @@ Requires-Dist: httpx>=0.28.1
33
33
  Requires-Dist: networkx>=3.4.2
34
34
  Requires-Dist: pydantic>=2.10.6
35
35
  Requires-Dist: python-dotenv>=1.1.0
36
+ Requires-Dist: rich>=14.1.0
36
37
  Requires-Dist: rid-lib>=3.2.7
37
38
  Requires-Dist: ruamel-yaml>=0.18.10
38
39
  Requires-Dist: uvicorn>=0.34.2
39
40
  Provides-Extra: dev
40
41
  Requires-Dist: build; extra == 'dev'
41
42
  Requires-Dist: twine>=6.0; extra == 'dev'
42
- Provides-Extra: examples
43
- Requires-Dist: rich; extra == 'examples'
43
+ Provides-Extra: docs
44
+ Requires-Dist: sphinx; extra == 'docs'
45
+ Requires-Dist: sphinx-autoapi>=3.6.0; extra == 'docs'
46
+ Requires-Dist: sphinx-autodoc-typehints>=3.0.1; extra == 'docs'
47
+ Requires-Dist: sphinx-rtd-theme>=3.0.2; extra == 'docs'
44
48
  Description-Content-Type: text/markdown
45
49
 
46
50
  # KOI-net
@@ -0,0 +1,49 @@
1
+ koi_net/__init__.py,sha256=S6Ew8CDhwaysGFJGX19tgkXLcJiJR2R5fwz449gaXFY,31
2
+ koi_net/behaviors.py,sha256=_2yeX3IdDQWiOsXoehiFjr2jPj58oZTnBX28M32LM4w,1955
3
+ koi_net/config.py,sha256=yV53GFQrJchyKKRs3sOhj27pyM6rwiUhIgDZ81rvZ48,5238
4
+ koi_net/context.py,sha256=z24-pwYE3YDIznqNkRBIZuqT-JpksHnkuUfDOH034j0,1429
5
+ koi_net/core.py,sha256=qUq87KfL-DKUu0NVfJPFBiNYpD35ODzvP6bV7F5ijd4,7252
6
+ koi_net/default_actions.py,sha256=y9oaDeb3jJGSvVbuonmyGWkKWlzpEX3V32MjgEuYWc8,622
7
+ koi_net/effector.py,sha256=iFX34Tq9Byev22CUbGnQU1MUccyNH5ebmQzoovwY_aE,4738
8
+ koi_net/handshaker.py,sha256=e5bzWGWvU7w5gQ4kBEiU6ShCXwc-XXgfvx3c1RxMguA,1238
9
+ koi_net/identity.py,sha256=FvIWksGTqwM7HCevIwmo_6l-t-2tnYkaaR4CanZatL4,569
10
+ koi_net/kobj_worker.py,sha256=p_1QJDDoz-cIDoTQ3qCptPTbuvqou5BAPEQqZjH8niE,1306
11
+ koi_net/lifecycle.py,sha256=WP3XiiCtSG8ZGa4oBnNBxwsmQ35QRQEgeYqwnGRxcsI,4379
12
+ koi_net/models.py,sha256=_hL-ohrlH-kZRV9B0PWjYyZbb_Ef1z14HOLI5RK_Dto,274
13
+ koi_net/poll_event_buffer.py,sha256=DMuEnaoH7nZccXY0LwHaGEyecrWfW1z0s7AbVHMGM88,441
14
+ koi_net/poller.py,sha256=zBHWPXvca-Ia9ggE5emu8rJx5c8Jdi1ZAYB7lTyezv8,1395
15
+ koi_net/secure.py,sha256=1t1WtHeqmoHfi4siW1jFCmIhKFsQGactITkEmVYKqnc,4919
16
+ koi_net/server.py,sha256=iP9agexIjqmNlCOXs50cLkyEWHth7cGlQRN593rBOhU,4955
17
+ koi_net/utils.py,sha256=4K6BqtifExDUfPP8zJAICgPV1mcabz_MaFB0P0fSnic,411
18
+ koi_net/worker.py,sha256=G3JLlILN0cyY7Fv3eA5y8uf9rImy4e21OQ1mEKFoVVo,191
19
+ koi_net/cli/__init__.py,sha256=Vpq0yLN0ROyF0qQYsyiuWFbjs-zsa90G1DpAv13qaiw,25
20
+ koi_net/cli/commands.py,sha256=5eVsvJAnHjwJdt8MNYrk5zSzWWm1ooiP35bJeQxYIq4,2592
21
+ koi_net/cli/models.py,sha256=ON-rqMgHDng3J2hbBbGYzkynaWc9Tq4QfOJIu_6pFuA,1190
22
+ koi_net/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ koi_net/network/error_handler.py,sha256=8XgOljFBJ4lzXdEO5I-spND9iRln9309lwgpPV1pMnM,1802
24
+ koi_net/network/event_queue.py,sha256=rvTuMuG-QicKHGSfB2FOyFMngmnESovec1TOPIowvss,780
25
+ koi_net/network/graph.py,sha256=8hweEivCwxnagFoUrefE7Hnz06MWScF3XJHeXU2Qqnw,4178
26
+ koi_net/network/request_handler.py,sha256=1Sp0-OCqcQXM-fAE5sFH4_M9FP1EbJh69P8gB56BSKg,7102
27
+ koi_net/network/resolver.py,sha256=MqUMwG5snZp4QLV7d-gZ7tRJDQ0uJZ_IhssozTc9JkU,5309
28
+ koi_net/network/response_handler.py,sha256=tbTqHmwXUEZIqiYqVL2vi4q6eDH0bPRrAO6G4XpbMic,2123
29
+ koi_net/processor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ koi_net/processor/default_handlers.py,sha256=siRZl_Ww7QA9MhJR4_nmrSHOG8ejfS0NjiF6braA338,11020
31
+ koi_net/processor/event_worker.py,sha256=bS_Xgkg4s1e_5dG0qtHY3d79JsYEyGzbI6F7JSZbSws,4297
32
+ koi_net/processor/handler.py,sha256=PJlQCPfKm3zYsJrwmGIw5xZmc6vArKz_UoeJuXBj16Y,2356
33
+ koi_net/processor/knowledge_object.py,sha256=0VyBOqkCoINMK3fqYhLlXU-Ri7gKEpJloDHfhaGITxg,4172
34
+ koi_net/processor/knowledge_pipeline.py,sha256=sz8T12nAcJ1tbF2jHf0Wmi6ffnQPMrIl64xif4WP7fc,8784
35
+ koi_net/processor/kobj_queue.py,sha256=LIh9Sz3EaEZj9qJBd4iLPlzYBO6aH-6qt_JHCxAzAJU,1839
36
+ koi_net/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ koi_net/protocol/api_models.py,sha256=AIFT49fyNAnOsHep9bgjga2M0S38-PouBaqfQI2zcKY,1795
38
+ koi_net/protocol/consts.py,sha256=bisbVEojPIHlLhkLafBzfIhH25TjNfvTORF1g6YXzIM,243
39
+ koi_net/protocol/edge.py,sha256=PzdEhC43T1KO5iMSEu7I4tiz-7sZxtz41dJfWf-oHA0,1034
40
+ koi_net/protocol/envelope.py,sha256=UVHlO2BDyDiP5eixqx9xD6xUsCfFRi0kZyzC4BC-DOw,1886
41
+ koi_net/protocol/errors.py,sha256=uKPQ-TGLouZuK0xd2pXuCQoRTyu_JFsydSCLml13Cz8,595
42
+ koi_net/protocol/event.py,sha256=HxzLN-iCXPyr2YzrswMIkgZYeUdFbBpa5v98dAB06lQ,1328
43
+ koi_net/protocol/node.py,sha256=KH_SjHDzW-V4pn2tqpTDhIzfuCgKgM1YqCafyofLN3k,466
44
+ koi_net/protocol/secure.py,sha256=6sRLWxG5EDF0QLBj29gk3hPmZnPXATrTTFdwx39wQfY,5127
45
+ koi_net-1.2.0b1.dist-info/METADATA,sha256=reMf5147lyI17Gu5Ca98sQFZsoMk9pL5mV2BHZKtpV0,37314
46
+ koi_net-1.2.0b1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
47
+ koi_net-1.2.0b1.dist-info/entry_points.txt,sha256=l7He_JTyXrfKIHkttnPWXHI717v8WpLLfCduL5QcEWA,40
48
+ koi_net-1.2.0b1.dist-info/licenses/LICENSE,sha256=03mgCL5qth2aD9C3F3qNVs4sFJSpK9kjtYCyOwdSp7s,1069
49
+ koi_net-1.2.0b1.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ koi = koi_net.cli:app
@@ -1,101 +0,0 @@
1
- import logging
2
- import queue
3
- import threading
4
- from rid_lib.core import RID
5
- from rid_lib.ext import Bundle, Manifest
6
- from rid_lib.types import KoiNetNode
7
- from ..protocol.event import Event, EventType
8
- from .knowledge_object import KnowledgeObject
9
- from .knowledge_pipeline import KnowledgePipeline
10
-
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
-
15
- class ProcessorInterface:
16
- """Provides access to this node's knowledge processing pipeline."""
17
- pipeline: KnowledgePipeline
18
- kobj_queue: queue.Queue[KnowledgeObject]
19
- use_kobj_processor_thread: bool
20
- worker_thread: threading.Thread | None = None
21
-
22
- def __init__(
23
- self,
24
- pipeline: KnowledgePipeline,
25
- use_kobj_processor_thread: bool,
26
- ):
27
- self.pipeline = pipeline
28
- self.use_kobj_processor_thread = use_kobj_processor_thread
29
- self.kobj_queue = queue.Queue()
30
-
31
- if self.use_kobj_processor_thread:
32
- self.worker_thread = threading.Thread(
33
- target=self.kobj_processor_worker,
34
- daemon=True
35
- )
36
-
37
- def flush_kobj_queue(self):
38
- """Flushes all knowledge objects from queue and processes them.
39
-
40
- NOTE: ONLY CALL THIS METHOD IN SINGLE THREADED NODES, OTHERWISE THIS WILL CAUSE RACE CONDITIONS.
41
- """
42
- if self.use_kobj_processor_thread:
43
- logger.warning("You are using a worker thread, calling this method can cause race conditions!")
44
-
45
- while not self.kobj_queue.empty():
46
- kobj = self.kobj_queue.get()
47
- logger.debug(f"Dequeued {kobj!r}")
48
-
49
- try:
50
- self.pipeline.process(kobj)
51
- finally:
52
- self.kobj_queue.task_done()
53
- logger.debug("Done")
54
-
55
- def kobj_processor_worker(self, timeout=0.1):
56
- while True:
57
- try:
58
- kobj = self.kobj_queue.get(timeout=timeout)
59
- logger.debug(f"Dequeued {kobj!r}")
60
-
61
- try:
62
- self.pipeline.process(kobj)
63
- finally:
64
- self.kobj_queue.task_done()
65
- logger.debug("Done")
66
-
67
- except queue.Empty:
68
- pass
69
-
70
- except Exception as e:
71
- logger.warning(f"Error processing kobj: {e}")
72
-
73
- def handle(
74
- self,
75
- rid: RID | None = None,
76
- manifest: Manifest | None = None,
77
- bundle: Bundle | None = None,
78
- event: Event | None = None,
79
- kobj: KnowledgeObject | None = None,
80
- event_type: EventType | None = None,
81
- source: KoiNetNode | None = None
82
- ):
83
- """Queues provided knowledge to be handled by processing pipeline.
84
-
85
- Knowledge may take the form of an RID, manifest, bundle, event, or knowledge object (with an optional event type for RID, manifest, or bundle objects). All objects will be normalized into knowledge objects and queued. If `flush` is `True`, the queue will be flushed immediately after adding the new knowledge.
86
- """
87
- if rid:
88
- _kobj = KnowledgeObject.from_rid(rid, event_type, source)
89
- elif manifest:
90
- _kobj = KnowledgeObject.from_manifest(manifest, event_type, source)
91
- elif bundle:
92
- _kobj = KnowledgeObject.from_bundle(bundle, event_type, source)
93
- elif event:
94
- _kobj = KnowledgeObject.from_event(event, source)
95
- elif kobj:
96
- _kobj = kobj
97
- else:
98
- raise ValueError("One of 'rid', 'manifest', 'bundle', 'event', or 'kobj' must be provided")
99
-
100
- self.kobj_queue.put(_kobj)
101
- logger.debug(f"Queued {_kobj!r}")
@@ -1,38 +0,0 @@
1
- koi_net/__init__.py,sha256=b0Ze0pZmJAuygpWUFHM6Kvqo3DkU_uzmkptv1EpAArw,31
2
- koi_net/config.py,sha256=47XbQ59GRYFi4rlsoWKlnzMQATcnK70i3qmKTZAGOQk,4087
3
- koi_net/context.py,sha256=FwkzjmsyNqUeOeGCuZXtONqs5_MSl1R8-e3IxHuyzTI,1531
4
- koi_net/core.py,sha256=aO9caoS8dLafRGheJWzhbp_ym7o7bi_wxds683bly48,7150
5
- koi_net/default_actions.py,sha256=TkQR9oj9CpO37Gb5bZLmFNl-Q8n3OxGiX4dvxQR7SaA,421
6
- koi_net/effector.py,sha256=gSyZgRxQ91X04UL261e2pXWUfBHnQTGtjSHpc2JufxA,4097
7
- koi_net/identity.py,sha256=FvIWksGTqwM7HCevIwmo_6l-t-2tnYkaaR4CanZatL4,569
8
- koi_net/lifecycle.py,sha256=GL2zltmh-aeBuNVg_rbIgsXMch672w7xkWAXCTjxst4,3550
9
- koi_net/poller.py,sha256=bIrlqdac5vLQYAid35xiQJLDMR85GnOSPCXSTQ07-Mc,1173
10
- koi_net/secure.py,sha256=cGNF2assqCaYq0i0fhQBm7aREoAdpY-XVypDsE1ALaU,3970
11
- koi_net/server.py,sha256=PrR_cXQV5YMKa6FXwiJXwMZJ52VQVzLPYYPVl-Miuw8,4315
12
- koi_net/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- koi_net/network/behavior.py,sha256=NZLvWlrxR0uWriE3ZzCXmocUVccQthy7Xx8E_8KBwsg,1208
14
- koi_net/network/error_handler.py,sha256=_dAl2ovpUZEVhPc8_dcLA7I-FoMNqQNwFy0QLa4uTSY,1617
15
- koi_net/network/event_queue.py,sha256=DWs26C235iYkP4koKcdbhmIOHGZRJ48d072BoNWyiHo,7325
16
- koi_net/network/graph.py,sha256=60SLiR3aNXIOGe-URzMGgx8abmMEJtz37EJs6jeImEM,4143
17
- koi_net/network/request_handler.py,sha256=_SM5MuYkS636wGJeFkesapQsW5x_kt_1o9KTXB0wksU,6869
18
- koi_net/network/resolver.py,sha256=coIp4M6k0-8sUfAy4h2NMx_7zCNroWlCHKOj3AXZVhc,5412
19
- koi_net/network/response_handler.py,sha256=__R_EvEpjaMz3PCDvkNgWF_EAHe2nePGk-zK_cT4C4g,2077
20
- koi_net/processor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
- koi_net/processor/default_handlers.py,sha256=1OTC4p0luTadNm90q6Fr_dbvysFzgRCbltp-YP6cRXo,9562
22
- koi_net/processor/handler.py,sha256=_loaHjgVGVUxtCQdvAY9dQ0iqiq5co7wB2tK-usuv3Y,2355
23
- koi_net/processor/interface.py,sha256=ebDwqggznFRfp2PT8-UJPUAvCwX8nZaaQ68FUeWQvmw,3682
24
- koi_net/processor/knowledge_object.py,sha256=avQnsaeqqiJxy40P1VGljuQMtAGmJB-TBa4pmBXTaIs,3863
25
- koi_net/processor/knowledge_pipeline.py,sha256=i7FpCFl0UIOwCI5zhP1i8M4PX4A48VN28iV9jruvN5k,9486
26
- koi_net/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- koi_net/protocol/api_models.py,sha256=jzRZWW_ZB5YsBAiwCom882-WIbr0rPyelJxExRgHZGc,1755
28
- koi_net/protocol/consts.py,sha256=bisbVEojPIHlLhkLafBzfIhH25TjNfvTORF1g6YXzIM,243
29
- koi_net/protocol/edge.py,sha256=PzdEhC43T1KO5iMSEu7I4tiz-7sZxtz41dJfWf-oHA0,1034
30
- koi_net/protocol/envelope.py,sha256=UVHlO2BDyDiP5eixqx9xD6xUsCfFRi0kZyzC4BC-DOw,1886
31
- koi_net/protocol/errors.py,sha256=uKPQ-TGLouZuK0xd2pXuCQoRTyu_JFsydSCLml13Cz8,595
32
- koi_net/protocol/event.py,sha256=HxzLN-iCXPyr2YzrswMIkgZYeUdFbBpa5v98dAB06lQ,1328
33
- koi_net/protocol/node.py,sha256=7GQzHORFr9cP4BqJgir6EGSWCskL-yqmvJksIiLfcWU,409
34
- koi_net/protocol/secure.py,sha256=6sRLWxG5EDF0QLBj29gk3hPmZnPXATrTTFdwx39wQfY,5127
35
- koi_net-1.1.0b7.dist-info/METADATA,sha256=nHdoaFbZodVbTgjEuZeMxi8as2JOWP0Fyhoj05tn7e8,37118
36
- koi_net-1.1.0b7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
37
- koi_net-1.1.0b7.dist-info/licenses/LICENSE,sha256=03mgCL5qth2aD9C3F3qNVs4sFJSpK9kjtYCyOwdSp7s,1069
38
- koi_net-1.1.0b7.dist-info/RECORD,,