Skip to content

Commit e1eb4f7

Browse files
authored
Merge branch 'main' into dev
2 parents 7c2408f + 7a22b73 commit e1eb4f7

File tree

2 files changed

+2
-277
lines changed

2 files changed

+2
-277
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ prototypes
1010
dist/
1111
docs/
1212
tests/
13-
.rid_cache/
13+
.rid_cache/
14+
*.ndjson

README.md

Lines changed: 0 additions & 276 deletions
Original file line numberDiff line numberDiff line change
@@ -401,282 +401,6 @@ node = NodeInterface(
401401

402402
Take a look at `src/koi_net/processor/default_handlers.py` to see some more in depth examples and better understand the default node behavior.
403403

404-
# Implementation Reference
405-
This section provides high level explanations of the Python implementation. More detailed explanations of methods can be found in the docstrings within the codebase itself.
406-
407-
## Node Interface
408-
The node class mostly acts as a container for other classes with more specialized behavior, with special functions that should be called to start up and shut down a node. We'll take a look at each of these components in turn, but here is the class stub:
409-
```python
410-
class NodeInterface:
411-
config: ConfigType
412-
cache: Cache
413-
identity: NodeIdentity
414-
network: NetworkInterface
415-
processor: ProcessorInterface
416-
417-
use_kobj_processor_thread: bool
418-
419-
def __init__(
420-
self,
421-
config: ConfigType,
422-
use_kobj_processor_thread: bool = False,
423-
424-
handlers: list[KnowledgeHandler] | None = None,
425-
426-
cache: Cache | None = None,
427-
network: NetworkInterface | None = None,
428-
processor: ProcessorInterface | None = None
429-
): ...
430-
431-
def start(self): ...
432-
def stop(self): ...
433-
```
434-
As you can see, only a node config is required, all other fields are optional.
435-
436-
## Node Config
437-
438-
The node config class is a mix of configuration groups providing basic shared behavior across nodes through a standard interface. The class is implemented as Pydantic model, but provides functions to load from and save to a YAML file, the expected format within a node repo.
439-
440-
```python
441-
class ServerConfig(BaseModel):
442-
host: str | None = "127.0.0.1"
443-
port: int | None = 8000
444-
path: str | None = "/koi-net"
445-
446-
@property
447-
def url(self) -> str: ...
448-
449-
class KoiNetConfig(BaseModel):
450-
node_name: str
451-
node_rid: KoiNetNode | None = None
452-
node_profile: NodeProfile
453-
454-
cache_directory_path: str | None = ".rid_cache"
455-
event_queues_path: str | None = "event_queues.json"
456-
457-
first_contact: str | None = None
458-
459-
class EnvConfig(BaseModel):
460-
...
461-
462-
class NodeConfig(BaseModel):
463-
server: ServerConfig | None = Field(default_factory=ServerConfig)
464-
koi_net: KoiNetConfig
465-
466-
@classmethod
467-
def load_from_yaml(
468-
cls,
469-
file_path: str = "config.yaml",
470-
generate_missing: bool = True
471-
): ...
472-
473-
def save_to_yaml(self): ...
474-
```
475-
476-
Nodes are expected to create new node config classes inheriting from `NodeConfig`. You may want to set a default KoiNetConfig (see examples) to allow for a default config to be generated if not provided by the user. Environment variables can be handled by inheriting from the `EnvConfig` class and adding new string fields. The value of this field should be equivalent to the environment variable name, for example:
477-
478-
```python
479-
class SlackEnvConfig(EnvConfig):
480-
slack_bot_token: str | None = "SLACK_BOT_TOKEN"
481-
slack_signing_secret: str | None = "SLACK_SIGNING_SECRET"
482-
slack_app_token: str | None = "SLACK_APP_TOKEN"
483-
```
484-
485-
This special config class will automatically load in the variables from the current environment, or local `.env` file. Beyond these base config classes, you are free to add your own config groups. See `config.py` in the [koi-net-slack-sensor-node](https://github.com/BlockScience/koi-net-slack-sensor-node/blob/main/slack_sensor_node/config.py) repo for a more complete example.
486-
487-
## Node Identity
488-
The `NodeIdentity` class provides easy access to a node's own RID, profile, and bundle. It provides access to the following properties after initialization, accessed with `node.identity`.
489-
```python
490-
class NodeIdentity:
491-
rid: KoiNetNode # an RID type
492-
profile: NodeProfile
493-
bundle: Bundle
494-
```
495-
This it what is initialized from the required `name` and `profile` fields in the `NodeConfig` class. Node RIDs take the form of `orn:koi-net.node:<name>+<uuid>`, and are generated on first use to the identity JSON file along with a the node profile.
496-
497-
## Network Interface
498-
The `NetworkInterface` class provides access to high level network actions, and contains several other network related classes. It is accessed with `node.network`.
499-
```python
500-
class NetworkInterface:
501-
graph: NetworkGraph
502-
request_handler: RequestHandler
503-
response_handler: ResponseHandler
504-
505-
def __init__(
506-
self,
507-
file_path: str,
508-
first_contact: str | None,
509-
cache: Cache,
510-
identity: NodeIdentity
511-
): ...
512-
513-
def push_event_to(self, event: Event, node: KoiNetNode, flush=False): ...
514-
515-
def flush_poll_queue(self, node: KoiNetNode) -> list[Event]: ...
516-
def flush_webhook_queue(self, node: RID): ...
517-
518-
def fetch_remote_bundle(self, rid: RID): ...
519-
def fetch_remote_manifest(self, rid: RID): ...
520-
521-
def get_state_providers(self, rid_type: RIDType): ...
522-
def poll_neighbors(self) -> list[Event]: ...
523-
```
524-
525-
Most of the provided functions are abstractions for KOI-net protocol actions. It also contains three lower level classes: `NetworkGraph`, `RequestHandler`, and `ResponseHandler`.
526-
527-
### Network Graph
528-
The `NetworkGraph` class provides access to a graph view of the node's KOI network: all of the KOI-net node and edge objects it knows about (stored in local cache). This view allows us to query nodes that we have edges with to make networking decisions.
529-
```python
530-
class NetworkGraph:
531-
dg: nx.DiGraph
532-
533-
def __init__(
534-
self,
535-
cache: Cache,
536-
identity: NodeIdentity
537-
): ...
538-
539-
def generate(self): ...
540-
541-
def get_edges(
542-
self,
543-
direction: Literal["in", "out"] | None = None
544-
) -> list[KoiNetEdge]: ...
545-
546-
def get_neighbors(
547-
self,
548-
direction: Literal["in", "out"] | None = None,
549-
status: EdgeStatus | None = None,
550-
allowed_type: RIDType | None = None
551-
) -> list[KoiNetNode]: ...
552-
553-
def get_node_profile(self, rid: KoiNetNode) -> NodeProfile | None: ...
554-
def get_edge_profile(
555-
self,
556-
rid: KoiNetEdge | None = None,
557-
source: KoiNetNode | None = None,
558-
target: KoiNetNode | None = None
559-
) -> EdgeProfile | None: ...
560-
```
561-
562-
### Request Handler
563-
Handles raw API requests to other nodes through the KOI-net protocol. Accepts a node RID or direct URL as the target. Each method requires either a valid request model, or `kwargs` which will be converted to the correct model in `koi_net.protocol.api_models`.
564-
```python
565-
class RequestHandler:
566-
def __init__(self, cache: Cache, graph: NetworkGraph): ...
567-
568-
def broadcast_events(
569-
self,
570-
node: RID = None,
571-
url: str = None,
572-
req: EventsPayload | None = None,
573-
**kwargs
574-
) -> None: ...
575-
576-
def poll_events(
577-
self,
578-
node: RID = None,
579-
url: str = None,
580-
req: PollEvents | None = None,
581-
**kwargs
582-
) -> EventsPayload: ...
583-
584-
def fetch_rids(
585-
self,
586-
node: RID = None,
587-
url: str = None,
588-
req: FetchRids | None = None,
589-
**kwargs
590-
) -> RidsPayload: ...
591-
592-
def fetch_manifests(
593-
self,
594-
node: RID = None,
595-
url: str = None,
596-
req: FetchManifests | None = None,
597-
**kwargs
598-
) -> ManifestsPayload: ...
599-
600-
def fetch_bundles(
601-
self,
602-
node: RID = None,
603-
url: str = None,
604-
req: FetchBundles | None = None,
605-
**kwargs
606-
) -> BundlesPayload: ...
607-
```
608-
609-
### Response Handler
610-
Handles raw API responses to requests from other nodes through the KOI-net protocol.
611-
```python
612-
class ResponseHandler:
613-
def __init__(self, cache: Cache): ...
614-
615-
def fetch_rids(self, req: FetchRids) -> RidsPayload:
616-
def fetch_manifests(self, req: FetchManifests) -> ManifestsPayload:
617-
def fetch_bundles(self, req: FetchBundles) -> BundlesPayload:
618-
```
619-
Only fetch methods are provided right now, event polling and broadcasting can be handled like this:
620-
```python
621-
def broadcast_events(req: EventsPayload) -> None:
622-
for event in req.events:
623-
node.processor.handle(event=event, source=KnowledgeSource.External)
624-
node.processor.flush_kobj_queue()
625-
626-
def poll_events(req: PollEvents) -> EventsPayload:
627-
events = node.network.flush_poll_queue(req.rid)
628-
return EventsPayload(events=events)
629-
```
630-
631-
## Processor Interface
632-
The `ProcessorInterface` class provides access to a node's internal knowledge processing pipeline.
633-
```python
634-
class ProcessorInterface:
635-
worker_thread: threading.Thread | None = None
636-
637-
def __init__(
638-
self,
639-
cache: Cache,
640-
network: NetworkInterface,
641-
identity: NodeIdentity,
642-
use_kobj_processor_thread: bool,
643-
default_handlers: list[KnowledgeHandler] = []
644-
): ...
645-
646-
def add_handler(self, handler: KnowledgeHandler): ...
647-
648-
def register_handler(
649-
self,
650-
handler_type: HandlerType,
651-
rid_types: list[RIDType] | None = None
652-
): ...
653-
654-
def process_kobj(self, kobj: KnowledgeObject) -> None:
655-
def flush_kobj_queue(self): ...
656-
657-
def handle(
658-
self,
659-
rid: RID | None = None,
660-
manifest: Manifest | None = None,
661-
bundle: Bundle | None = None,
662-
event: Event | None = None,
663-
kobj: KnowledgeObject | None = None,
664-
event_type: KnowledgeEventType = None,
665-
source: KnowledgeSource = KnowledgeSource.Internal
666-
): ...
667-
```
668-
669-
The `register_handler` method is a decorator which can wrap a function to create a new `KnowledgeHandler` and add it to the processing pipeline in a single step. The `add_handler` method adds an existing `KnowledgeHandler` to the processining pipeline.
670-
671-
The most commonly used functions in this class are `handle` and `flush_kobj_queue`. The `handle` method can be called on RIDs, manifests, bundles, and events to convert them to normalized to `KnowledgeObject` instances which are then added to the processing queue. If you have enabled `use_kobj_processor_thread` then the queue will be automatically processed, otherwise you will need to regularly call `flush_kobj_queue` to process queued knolwedge objects. When calling the `handle` method, knowledge objects are marked as internally source by default. If you are handling RIDs, manifests, bundles, or events sourced from other nodes, `source` should be set to `KnowledgeSource.External`.
672-
673-
Here is an example of how an event polling loop would be implemented using the knowledge processing pipeline:
674-
```python
675-
for event in node.network.poll_neighbors():
676-
node.processor.handle(event=event, source=KnowledgeSource.External)
677-
node.processor.flush_kobj_queue()
678-
```
679-
680404
# Development
681405
## Setup
682406
Clone this repository:

0 commit comments

Comments
 (0)