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.
- 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 +16 -20
- koi_net/core.py +208 -171
- koi_net/default_actions.py +10 -1
- koi_net/effector.py +39 -24
- koi_net/{network/behavior.py → handshaker.py} +17 -20
- koi_net/kobj_worker.py +45 -0
- koi_net/lifecycle.py +71 -41
- 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 +14 -9
- koi_net/network/request_handler.py +31 -19
- koi_net/network/resolver.py +6 -9
- 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.0b7.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/processor/interface.py +0 -101
- koi_net-1.1.0b7.dist-info/RECORD +0 -38
- {koi_net-1.1.0b7.dist-info → koi_net-1.2.0b1.dist-info}/WHEEL +0 -0
- {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
|
|
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:
|
|
29
|
+
event_queue: EventQueue
|
|
32
30
|
graph: NetworkGraph
|
|
33
|
-
|
|
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:
|
|
38
|
+
event_queue: EventQueue,
|
|
41
39
|
graph: NetworkGraph,
|
|
42
|
-
|
|
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.
|
|
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.
|
|
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}")
|
koi_net/protocol/api_models.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Pydantic models for request and response
|
|
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
|
-
|
|
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
|
-
|
|
36
|
+
cache: Cache,
|
|
36
37
|
config: NodeConfig
|
|
37
38
|
):
|
|
38
39
|
self.identity = identity
|
|
39
|
-
self.
|
|
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(
|
|
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.
|
|
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
|
-
|
|
6
|
+
|
|
7
|
+
from koi_net.poll_event_buffer import PollEventBuffer
|
|
7
8
|
from .network.response_handler import ResponseHandler
|
|
8
|
-
from .processor.
|
|
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
|
-
|
|
45
|
-
|
|
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.
|
|
52
|
-
self.
|
|
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.
|
|
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.
|
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: koi-net
|
|
3
|
-
Version: 1.
|
|
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:
|
|
43
|
-
Requires-Dist:
|
|
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,,
|
koi_net/processor/interface.py
DELETED
|
@@ -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}")
|
koi_net-1.1.0b7.dist-info/RECORD
DELETED
|
@@ -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,,
|
|
File without changes
|
|
File without changes
|