digitalkin 0.3.2.dev2__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.
- base_server/__init__.py +1 -0
- base_server/mock/__init__.py +5 -0
- base_server/mock/mock_pb2.py +39 -0
- base_server/mock/mock_pb2_grpc.py +102 -0
- base_server/server_async_insecure.py +125 -0
- base_server/server_async_secure.py +143 -0
- base_server/server_sync_insecure.py +103 -0
- base_server/server_sync_secure.py +122 -0
- digitalkin/__init__.py +8 -0
- digitalkin/__version__.py +8 -0
- digitalkin/core/__init__.py +1 -0
- digitalkin/core/common/__init__.py +9 -0
- digitalkin/core/common/factories.py +156 -0
- digitalkin/core/job_manager/__init__.py +1 -0
- digitalkin/core/job_manager/base_job_manager.py +288 -0
- digitalkin/core/job_manager/single_job_manager.py +354 -0
- digitalkin/core/job_manager/taskiq_broker.py +311 -0
- digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
- digitalkin/core/task_manager/__init__.py +1 -0
- digitalkin/core/task_manager/base_task_manager.py +539 -0
- digitalkin/core/task_manager/local_task_manager.py +108 -0
- digitalkin/core/task_manager/remote_task_manager.py +87 -0
- digitalkin/core/task_manager/surrealdb_repository.py +266 -0
- digitalkin/core/task_manager/task_executor.py +249 -0
- digitalkin/core/task_manager/task_session.py +406 -0
- digitalkin/grpc_servers/__init__.py +1 -0
- digitalkin/grpc_servers/_base_server.py +486 -0
- digitalkin/grpc_servers/module_server.py +208 -0
- digitalkin/grpc_servers/module_servicer.py +516 -0
- digitalkin/grpc_servers/utils/__init__.py +1 -0
- digitalkin/grpc_servers/utils/exceptions.py +29 -0
- digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
- digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
- digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
- digitalkin/logger.py +157 -0
- digitalkin/mixins/__init__.py +19 -0
- digitalkin/mixins/base_mixin.py +10 -0
- digitalkin/mixins/callback_mixin.py +24 -0
- digitalkin/mixins/chat_history_mixin.py +110 -0
- digitalkin/mixins/cost_mixin.py +76 -0
- digitalkin/mixins/file_history_mixin.py +93 -0
- digitalkin/mixins/filesystem_mixin.py +46 -0
- digitalkin/mixins/logger_mixin.py +51 -0
- digitalkin/mixins/storage_mixin.py +79 -0
- digitalkin/models/__init__.py +8 -0
- digitalkin/models/core/__init__.py +1 -0
- digitalkin/models/core/job_manager_models.py +36 -0
- digitalkin/models/core/task_monitor.py +70 -0
- digitalkin/models/grpc_servers/__init__.py +1 -0
- digitalkin/models/grpc_servers/models.py +275 -0
- digitalkin/models/grpc_servers/types.py +24 -0
- digitalkin/models/module/__init__.py +25 -0
- digitalkin/models/module/module.py +40 -0
- digitalkin/models/module/module_context.py +149 -0
- digitalkin/models/module/module_types.py +393 -0
- digitalkin/models/module/utility.py +146 -0
- digitalkin/models/services/__init__.py +10 -0
- digitalkin/models/services/cost.py +54 -0
- digitalkin/models/services/registry.py +42 -0
- digitalkin/models/services/storage.py +44 -0
- digitalkin/modules/__init__.py +11 -0
- digitalkin/modules/_base_module.py +517 -0
- digitalkin/modules/archetype_module.py +23 -0
- digitalkin/modules/tool_module.py +23 -0
- digitalkin/modules/trigger_handler.py +48 -0
- digitalkin/modules/triggers/__init__.py +12 -0
- digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
- digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
- digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
- digitalkin/py.typed +0 -0
- digitalkin/services/__init__.py +30 -0
- digitalkin/services/agent/__init__.py +6 -0
- digitalkin/services/agent/agent_strategy.py +19 -0
- digitalkin/services/agent/default_agent.py +13 -0
- digitalkin/services/base_strategy.py +22 -0
- digitalkin/services/communication/__init__.py +7 -0
- digitalkin/services/communication/communication_strategy.py +76 -0
- digitalkin/services/communication/default_communication.py +101 -0
- digitalkin/services/communication/grpc_communication.py +223 -0
- digitalkin/services/cost/__init__.py +14 -0
- digitalkin/services/cost/cost_strategy.py +100 -0
- digitalkin/services/cost/default_cost.py +114 -0
- digitalkin/services/cost/grpc_cost.py +138 -0
- digitalkin/services/filesystem/__init__.py +7 -0
- digitalkin/services/filesystem/default_filesystem.py +417 -0
- digitalkin/services/filesystem/filesystem_strategy.py +252 -0
- digitalkin/services/filesystem/grpc_filesystem.py +317 -0
- digitalkin/services/identity/__init__.py +6 -0
- digitalkin/services/identity/default_identity.py +15 -0
- digitalkin/services/identity/identity_strategy.py +14 -0
- digitalkin/services/registry/__init__.py +27 -0
- digitalkin/services/registry/default_registry.py +141 -0
- digitalkin/services/registry/exceptions.py +47 -0
- digitalkin/services/registry/grpc_registry.py +306 -0
- digitalkin/services/registry/registry_models.py +43 -0
- digitalkin/services/registry/registry_strategy.py +98 -0
- digitalkin/services/services_config.py +200 -0
- digitalkin/services/services_models.py +65 -0
- digitalkin/services/setup/__init__.py +1 -0
- digitalkin/services/setup/default_setup.py +219 -0
- digitalkin/services/setup/grpc_setup.py +343 -0
- digitalkin/services/setup/setup_strategy.py +145 -0
- digitalkin/services/snapshot/__init__.py +6 -0
- digitalkin/services/snapshot/default_snapshot.py +39 -0
- digitalkin/services/snapshot/snapshot_strategy.py +30 -0
- digitalkin/services/storage/__init__.py +7 -0
- digitalkin/services/storage/default_storage.py +228 -0
- digitalkin/services/storage/grpc_storage.py +214 -0
- digitalkin/services/storage/storage_strategy.py +273 -0
- digitalkin/services/user_profile/__init__.py +12 -0
- digitalkin/services/user_profile/default_user_profile.py +55 -0
- digitalkin/services/user_profile/grpc_user_profile.py +69 -0
- digitalkin/services/user_profile/user_profile_strategy.py +40 -0
- digitalkin/utils/__init__.py +29 -0
- digitalkin/utils/arg_parser.py +92 -0
- digitalkin/utils/development_mode_action.py +51 -0
- digitalkin/utils/dynamic_schema.py +483 -0
- digitalkin/utils/llm_ready_schema.py +75 -0
- digitalkin/utils/package_discover.py +357 -0
- digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
- digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
- digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
- digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
- digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
- modules/__init__.py +0 -0
- modules/cpu_intensive_module.py +280 -0
- modules/dynamic_setup_module.py +338 -0
- modules/minimal_llm_module.py +347 -0
- modules/text_transform_module.py +203 -0
- services/filesystem_module.py +200 -0
- services/storage_module.py +206 -0
base_server/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Example of a (a)synchronous (in)secure gRPC server using DigitalKin's BaseServer."""
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ruff: noqa
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
4
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
5
|
+
# source: mock.proto
|
|
6
|
+
# Protobuf Python Version: 5.29.0
|
|
7
|
+
"""Generated protocol buffer code."""
|
|
8
|
+
|
|
9
|
+
from google.protobuf import descriptor as _descriptor
|
|
10
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
11
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
12
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
13
|
+
from google.protobuf.internal import builder as _builder
|
|
14
|
+
|
|
15
|
+
_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 0, "", "mock.proto")
|
|
16
|
+
# @@protoc_insertion_point(imports)
|
|
17
|
+
|
|
18
|
+
_sym_db = _symbol_database.Default()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
|
22
|
+
b'\n\nmock.proto\x12\nhelloworld"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2I\n\x07Greeter\x12>\n\x08SayHello\x12\x18.helloworld.HelloRequest\x1a\x16.helloworld.HelloReply"\x00\x42\x36\n\x1bio.grpc.examples.helloworldB\x0fHelloWorldProtoP\x01\xa2\x02\x03HLWb\x06proto3'
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
_globals = globals()
|
|
26
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
27
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "mock_pb2", _globals)
|
|
28
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
29
|
+
_globals["DESCRIPTOR"]._loaded_options = None
|
|
30
|
+
_globals[
|
|
31
|
+
"DESCRIPTOR"
|
|
32
|
+
]._serialized_options = b"\n\033io.grpc.examples.helloworldB\017HelloWorldProtoP\001\242\002\003HLW"
|
|
33
|
+
_globals["_HELLOREQUEST"]._serialized_start = 26
|
|
34
|
+
_globals["_HELLOREQUEST"]._serialized_end = 54
|
|
35
|
+
_globals["_HELLOREPLY"]._serialized_start = 56
|
|
36
|
+
_globals["_HELLOREPLY"]._serialized_end = 85
|
|
37
|
+
_globals["_GREETER"]._serialized_start = 87
|
|
38
|
+
_globals["_GREETER"]._serialized_end = 160
|
|
39
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ruff: noqa
|
|
2
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
3
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
4
|
+
|
|
5
|
+
import grpc
|
|
6
|
+
|
|
7
|
+
import examples.base_server.mock.mock_pb2 as mock__pb2
|
|
8
|
+
|
|
9
|
+
GRPC_GENERATED_VERSION = "1.70.0"
|
|
10
|
+
GRPC_VERSION = grpc.__version__
|
|
11
|
+
_version_not_supported = False
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from grpc._utilities import first_version_is_lower
|
|
15
|
+
|
|
16
|
+
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
|
17
|
+
except ImportError:
|
|
18
|
+
_version_not_supported = True
|
|
19
|
+
|
|
20
|
+
if _version_not_supported:
|
|
21
|
+
raise RuntimeError(
|
|
22
|
+
f"The grpc package installed is at version {GRPC_VERSION},"
|
|
23
|
+
+ f" but the generated code in mock_pb2_grpc.py depends on"
|
|
24
|
+
+ f" grpcio>={GRPC_GENERATED_VERSION}."
|
|
25
|
+
+ f" Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}"
|
|
26
|
+
+ f" or downgrade your generated code using grpcio-tools<={GRPC_VERSION}."
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GreeterStub(object):
|
|
31
|
+
"""The greeting service definition."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, channel):
|
|
34
|
+
"""Constructor.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
channel: A grpc.Channel.
|
|
38
|
+
"""
|
|
39
|
+
self.SayHello = channel.unary_unary(
|
|
40
|
+
"/helloworld.Greeter/SayHello",
|
|
41
|
+
request_serializer=mock__pb2.HelloRequest.SerializeToString,
|
|
42
|
+
response_deserializer=mock__pb2.HelloReply.FromString,
|
|
43
|
+
_registered_method=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class GreeterServicer(object):
|
|
48
|
+
"""The greeting service definition."""
|
|
49
|
+
|
|
50
|
+
def SayHello(self, request, context):
|
|
51
|
+
"""Sends a greeting"""
|
|
52
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
53
|
+
context.set_details("Method not implemented!")
|
|
54
|
+
raise NotImplementedError("Method not implemented!")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def add_GreeterServicer_to_server(servicer, server):
|
|
58
|
+
rpc_method_handlers = {
|
|
59
|
+
"SayHello": grpc.unary_unary_rpc_method_handler(
|
|
60
|
+
servicer.SayHello,
|
|
61
|
+
request_deserializer=mock__pb2.HelloRequest.FromString,
|
|
62
|
+
response_serializer=mock__pb2.HelloReply.SerializeToString,
|
|
63
|
+
),
|
|
64
|
+
}
|
|
65
|
+
generic_handler = grpc.method_handlers_generic_handler("helloworld.Greeter", rpc_method_handlers)
|
|
66
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
67
|
+
server.add_registered_method_handlers("helloworld.Greeter", rpc_method_handlers)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# This class is part of an EXPERIMENTAL API.
|
|
71
|
+
class Greeter(object):
|
|
72
|
+
"""The greeting service definition."""
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def SayHello(
|
|
76
|
+
request,
|
|
77
|
+
target,
|
|
78
|
+
options=(),
|
|
79
|
+
channel_credentials=None,
|
|
80
|
+
call_credentials=None,
|
|
81
|
+
insecure=False,
|
|
82
|
+
compression=None,
|
|
83
|
+
wait_for_ready=None,
|
|
84
|
+
timeout=None,
|
|
85
|
+
metadata=None,
|
|
86
|
+
):
|
|
87
|
+
return grpc.experimental.unary_unary(
|
|
88
|
+
request,
|
|
89
|
+
target,
|
|
90
|
+
"/helloworld.Greeter/SayHello",
|
|
91
|
+
mock__pb2.HelloRequest.SerializeToString,
|
|
92
|
+
mock__pb2.HelloReply.FromString,
|
|
93
|
+
options,
|
|
94
|
+
channel_credentials,
|
|
95
|
+
insecure,
|
|
96
|
+
call_credentials,
|
|
97
|
+
compression,
|
|
98
|
+
wait_for_ready,
|
|
99
|
+
timeout,
|
|
100
|
+
metadata,
|
|
101
|
+
_registered_method=True,
|
|
102
|
+
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Example of an asynchronous insecure gRPC server using BaseServer."""
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Add parent directory to path to enable imports
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
11
|
+
|
|
12
|
+
from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
|
|
13
|
+
|
|
14
|
+
from digitalkin.grpc_servers._base_server import BaseServer
|
|
15
|
+
from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
|
|
16
|
+
from examples.base_server.mock.mock_pb2_grpc import (
|
|
17
|
+
Greeter,
|
|
18
|
+
add_GreeterServicer_to_server,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Configure logging
|
|
22
|
+
logging.basicConfig(
|
|
23
|
+
level=logging.INFO,
|
|
24
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
25
|
+
)
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class AsyncGreeterImpl(Greeter):
|
|
30
|
+
"""Asynchronous implementation of Greeter service."""
|
|
31
|
+
|
|
32
|
+
async def SayHello(self, request, context): # noqa: N802
|
|
33
|
+
"""Asynchronous implementation of SayHello method."""
|
|
34
|
+
logger.info("Received request object: %s", request)
|
|
35
|
+
logger.info(f"Request attributes: {vars(request)}")
|
|
36
|
+
logger.info(f"Received request with name: {request.name}")
|
|
37
|
+
|
|
38
|
+
# If the name is still empty, try to get metadata from the context
|
|
39
|
+
name = request.name
|
|
40
|
+
if not name:
|
|
41
|
+
name = "unknown"
|
|
42
|
+
# Check context metadata
|
|
43
|
+
for key, value in context.invocation_metadata():
|
|
44
|
+
logger.info("Metadata: %s=%s", key, value)
|
|
45
|
+
if key.lower() == "name":
|
|
46
|
+
name = value
|
|
47
|
+
|
|
48
|
+
# Simulate some async work
|
|
49
|
+
await asyncio.sleep(0.1)
|
|
50
|
+
|
|
51
|
+
return HelloReply(message=f"Hello async, {name}!")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AsyncInsecureServer(BaseServer):
|
|
55
|
+
"""Asynchronous insecure gRPC server implementation."""
|
|
56
|
+
|
|
57
|
+
def _register_servicers(self) -> None:
|
|
58
|
+
"""Register servicers with the gRPC server."""
|
|
59
|
+
if self.server is None:
|
|
60
|
+
msg = "Server must be created before registering servicers"
|
|
61
|
+
raise RuntimeError(msg)
|
|
62
|
+
|
|
63
|
+
# Create and register the servicer
|
|
64
|
+
servicer = AsyncGreeterImpl()
|
|
65
|
+
self.register_servicer(
|
|
66
|
+
servicer,
|
|
67
|
+
add_GreeterServicer_to_server,
|
|
68
|
+
service_descriptor=DESCRIPTOR,
|
|
69
|
+
)
|
|
70
|
+
logger.info("Registered Async Greeter servicer")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
async def main_async() -> int:
|
|
74
|
+
"""Run the asynchronous insecure server."""
|
|
75
|
+
server = None
|
|
76
|
+
try:
|
|
77
|
+
# Create server configuration
|
|
78
|
+
config = ServerConfig(
|
|
79
|
+
host="localhost",
|
|
80
|
+
port=50051,
|
|
81
|
+
mode=ServerMode.ASYNC,
|
|
82
|
+
security=SecurityMode.INSECURE,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Create the server
|
|
86
|
+
server = AsyncInsecureServer(config)
|
|
87
|
+
|
|
88
|
+
# Use the async-specific start method
|
|
89
|
+
await server.start_async()
|
|
90
|
+
|
|
91
|
+
logger.info("Server started. Press Ctrl+C to stop.")
|
|
92
|
+
|
|
93
|
+
# Keep the server running until interrupted
|
|
94
|
+
await server.await_termination()
|
|
95
|
+
|
|
96
|
+
except KeyboardInterrupt:
|
|
97
|
+
# This inner handler will rarely be reached,
|
|
98
|
+
# as the KeyboardInterrupt usually breaks out of asyncio.run()
|
|
99
|
+
logger.info("Server stopping due to keyboard interrupt...")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.exception("Error running server: %s", e)
|
|
102
|
+
return 1
|
|
103
|
+
finally:
|
|
104
|
+
# Clean up resources if server was started
|
|
105
|
+
if server is not None and server.server is not None:
|
|
106
|
+
await server.stop_async()
|
|
107
|
+
|
|
108
|
+
return 0
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def main():
|
|
112
|
+
"""Run the async main function."""
|
|
113
|
+
try:
|
|
114
|
+
return asyncio.run(main_async())
|
|
115
|
+
except KeyboardInterrupt:
|
|
116
|
+
# This is the primary KeyboardInterrupt handler
|
|
117
|
+
logger.info("Server stopped by keyboard interrupt")
|
|
118
|
+
return 0 # Clean exit
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.exception("Fatal error: %s", e)
|
|
121
|
+
return 1
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
sys.exit(main())
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Example of an asynchronous secure gRPC server using BaseServer with TLS."""
|
|
3
|
+
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
# Add parent directory to path to enable imports
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
11
|
+
|
|
12
|
+
from digitalkin.grpc_servers.utils.models import (
|
|
13
|
+
SecurityMode,
|
|
14
|
+
ServerConfig,
|
|
15
|
+
ServerCredentials,
|
|
16
|
+
ServerMode,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from digitalkin.grpc_servers._base_server import BaseServer
|
|
20
|
+
from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
|
|
21
|
+
from examples.base_server.mock.mock_pb2_grpc import (
|
|
22
|
+
Greeter,
|
|
23
|
+
add_GreeterServicer_to_server,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Configure logging
|
|
27
|
+
logging.basicConfig(
|
|
28
|
+
level=logging.INFO,
|
|
29
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
30
|
+
)
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AsyncGreeterImpl(Greeter):
|
|
35
|
+
"""Asynchronous implementation of Greeter service."""
|
|
36
|
+
|
|
37
|
+
async def SayHello(self, request, context): # noqa: N802
|
|
38
|
+
"""Asynchronous implementation of SayHello method."""
|
|
39
|
+
logger.info("Received request object: %s", request)
|
|
40
|
+
logger.info(f"Request attributes: {vars(request)}")
|
|
41
|
+
logger.info(f"Received request with name: {request.name}")
|
|
42
|
+
|
|
43
|
+
# If the name is still empty, try to get metadata from the context
|
|
44
|
+
name = request.name
|
|
45
|
+
if not name:
|
|
46
|
+
name = "unknown"
|
|
47
|
+
# Check context metadata
|
|
48
|
+
for key, value in context.invocation_metadata():
|
|
49
|
+
logger.info("Metadata: %s=%s", key, value)
|
|
50
|
+
if key.lower() == "name":
|
|
51
|
+
name = value
|
|
52
|
+
|
|
53
|
+
# Simulate some async work
|
|
54
|
+
await asyncio.sleep(0.1)
|
|
55
|
+
|
|
56
|
+
return HelloReply(message=f"Hello secure async, {name}!")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AsyncSecureServer(BaseServer):
|
|
60
|
+
"""Asynchronous secure gRPC server implementation."""
|
|
61
|
+
|
|
62
|
+
def _register_servicers(self) -> None:
|
|
63
|
+
"""Register servicers with the gRPC server."""
|
|
64
|
+
if self.server is None:
|
|
65
|
+
msg = "Server must be created before registering servicers"
|
|
66
|
+
raise RuntimeError(msg)
|
|
67
|
+
|
|
68
|
+
# Create and register the servicer
|
|
69
|
+
servicer = AsyncGreeterImpl()
|
|
70
|
+
self.register_servicer(
|
|
71
|
+
servicer,
|
|
72
|
+
add_GreeterServicer_to_server,
|
|
73
|
+
service_descriptor=DESCRIPTOR,
|
|
74
|
+
)
|
|
75
|
+
logger.info("Registered Async Greeter servicer")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def main_async() -> int:
|
|
79
|
+
"""Run the asynchronous secure server."""
|
|
80
|
+
try:
|
|
81
|
+
# Path to certificate files
|
|
82
|
+
cert_dir = Path(__file__).parent.parent.parent / "certs"
|
|
83
|
+
|
|
84
|
+
# Check if certificates exist
|
|
85
|
+
if not cert_dir.exists() or not (cert_dir / "server.key").exists():
|
|
86
|
+
logger.error("Certificate files not found. Please generate them first.")
|
|
87
|
+
logger.info("Run the generate_certificates.py script to create certificates.")
|
|
88
|
+
return 1
|
|
89
|
+
|
|
90
|
+
# Create server configuration with security credentials
|
|
91
|
+
config = ServerConfig(
|
|
92
|
+
host="localhost",
|
|
93
|
+
port=50051,
|
|
94
|
+
mode=ServerMode.ASYNC,
|
|
95
|
+
security=SecurityMode.SECURE,
|
|
96
|
+
credentials=ServerCredentials(
|
|
97
|
+
server_key_path=cert_dir / "server.key",
|
|
98
|
+
server_cert_path=cert_dir / "server.crt",
|
|
99
|
+
# For mTLS (mutual TLS with client authentication), uncomment:
|
|
100
|
+
# root_cert_path=cert_dir / "ca.crt", # noqa: ERA001
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Create and start the server
|
|
105
|
+
server = AsyncSecureServer(config)
|
|
106
|
+
# Use the async-specific start method
|
|
107
|
+
await server.start_async()
|
|
108
|
+
|
|
109
|
+
logger.info("Server started. Press Ctrl+C to stop.")
|
|
110
|
+
|
|
111
|
+
# Keep the server running until interrupted
|
|
112
|
+
await server.await_termination()
|
|
113
|
+
|
|
114
|
+
except KeyboardInterrupt:
|
|
115
|
+
# This inner handler will rarely be reached,
|
|
116
|
+
# as the KeyboardInterrupt usually breaks out of asyncio.run()
|
|
117
|
+
logger.info("Server stopping due to keyboard interrupt...")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.exception("Error running server: %s", e)
|
|
120
|
+
return 1
|
|
121
|
+
finally:
|
|
122
|
+
# Clean up resources if server was started
|
|
123
|
+
if server is not None and server.server is not None:
|
|
124
|
+
await server.stop_async()
|
|
125
|
+
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def main():
|
|
130
|
+
"""Run the async main function."""
|
|
131
|
+
try:
|
|
132
|
+
return asyncio.run(main_async())
|
|
133
|
+
except KeyboardInterrupt:
|
|
134
|
+
# This is the primary KeyboardInterrupt handler
|
|
135
|
+
logger.info("Server stopped by keyboard interrupt")
|
|
136
|
+
return 0 # Clean exit
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.exception("Fatal error: %s", e)
|
|
139
|
+
return 1
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
sys.exit(main())
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Example of a synchronous insecure gRPC server using BaseServer."""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Add parent directory to path to enable imports
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
10
|
+
|
|
11
|
+
from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
|
|
12
|
+
|
|
13
|
+
from digitalkin.grpc_servers._base_server import BaseServer
|
|
14
|
+
from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
|
|
15
|
+
from examples.base_server.mock.mock_pb2_grpc import (
|
|
16
|
+
Greeter,
|
|
17
|
+
add_GreeterServicer_to_server,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
# Configure logging
|
|
21
|
+
logging.basicConfig(
|
|
22
|
+
level=logging.INFO,
|
|
23
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
24
|
+
)
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SyncGreeterServicer(Greeter):
|
|
29
|
+
"""Synchronous implementation of Greeter service."""
|
|
30
|
+
|
|
31
|
+
def SayHello(self, request, context): # noqa: N802
|
|
32
|
+
"""Implementation of SayHello method."""
|
|
33
|
+
logger.info("Received request object: %s", request)
|
|
34
|
+
logger.info(f"Request attributes: {vars(request)}")
|
|
35
|
+
logger.info(f"Received request with name: {request.name}")
|
|
36
|
+
|
|
37
|
+
# If the name is still empty, try to get metadata from the context
|
|
38
|
+
name = request.name
|
|
39
|
+
if not name:
|
|
40
|
+
name = "unknown"
|
|
41
|
+
# Check context metadata
|
|
42
|
+
for key, value in context.invocation_metadata():
|
|
43
|
+
logger.info("Metadata: %s=%s", key, value)
|
|
44
|
+
if key.lower() == "name":
|
|
45
|
+
name = value
|
|
46
|
+
|
|
47
|
+
return HelloReply(message=f"Hello, {name}!")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SyncInsecureServer(BaseServer):
|
|
51
|
+
"""Synchronous insecure gRPC server implementation."""
|
|
52
|
+
|
|
53
|
+
def _register_servicers(self) -> None:
|
|
54
|
+
"""Register servicers with the gRPC server."""
|
|
55
|
+
if self.server is None:
|
|
56
|
+
msg = "Server must be created before registering servicers"
|
|
57
|
+
raise RuntimeError(msg)
|
|
58
|
+
|
|
59
|
+
# Create and register the servicer
|
|
60
|
+
servicer = SyncGreeterServicer()
|
|
61
|
+
self.register_servicer(
|
|
62
|
+
servicer,
|
|
63
|
+
add_GreeterServicer_to_server,
|
|
64
|
+
service_descriptor=DESCRIPTOR,
|
|
65
|
+
)
|
|
66
|
+
logger.info("Registered Greeter servicer")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main() -> int:
|
|
70
|
+
"""Run the synchronous insecure server."""
|
|
71
|
+
try:
|
|
72
|
+
# Create server configuration
|
|
73
|
+
config = ServerConfig(
|
|
74
|
+
host="localhost",
|
|
75
|
+
port=50051,
|
|
76
|
+
mode=ServerMode.SYNC,
|
|
77
|
+
security=SecurityMode.INSECURE,
|
|
78
|
+
max_workers=10,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Create and start the server
|
|
82
|
+
server = SyncInsecureServer(config)
|
|
83
|
+
server.start()
|
|
84
|
+
|
|
85
|
+
logger.info("Server started. Press Ctrl+C to stop.")
|
|
86
|
+
|
|
87
|
+
# Keep the server running until interrupted
|
|
88
|
+
try:
|
|
89
|
+
server.wait_for_termination()
|
|
90
|
+
except KeyboardInterrupt:
|
|
91
|
+
logger.info("Server stopping due to keyboard interrupt...")
|
|
92
|
+
finally:
|
|
93
|
+
server.stop()
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.exception("Error running server: %s", e)
|
|
97
|
+
return 1
|
|
98
|
+
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
sys.exit(main())
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Example of a synchronous secure gRPC server using BaseServer with TLS."""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Add parent directory to path to enable imports
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
10
|
+
|
|
11
|
+
from digitalkin.grpc_servers.utils.models import (
|
|
12
|
+
SecurityMode,
|
|
13
|
+
ServerConfig,
|
|
14
|
+
ServerCredentials,
|
|
15
|
+
ServerMode,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from digitalkin.grpc_servers._base_server import BaseServer
|
|
19
|
+
from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
|
|
20
|
+
from examples.base_server.mock.mock_pb2_grpc import (
|
|
21
|
+
Greeter,
|
|
22
|
+
add_GreeterServicer_to_server,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Configure logging
|
|
26
|
+
logging.basicConfig(
|
|
27
|
+
level=logging.INFO,
|
|
28
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
29
|
+
)
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SyncGreeterServicer(Greeter):
|
|
34
|
+
"""Synchronous implementation of Greeter service."""
|
|
35
|
+
|
|
36
|
+
def SayHello(self, request, context): # noqa: N802
|
|
37
|
+
"""Implementation of SayHello method."""
|
|
38
|
+
logger.info("Received request object: %s", request)
|
|
39
|
+
logger.info(f"Request attributes: {vars(request)}")
|
|
40
|
+
logger.info(f"Received request with name: {request.name}")
|
|
41
|
+
|
|
42
|
+
# If the name is still empty, try to get metadata from the context
|
|
43
|
+
name = request.name
|
|
44
|
+
if not name:
|
|
45
|
+
name = "unknown"
|
|
46
|
+
# Check context metadata
|
|
47
|
+
for key, value in context.invocation_metadata():
|
|
48
|
+
logger.info("Metadata: %s=%s", key, value)
|
|
49
|
+
if key.lower() == "name":
|
|
50
|
+
name = value
|
|
51
|
+
|
|
52
|
+
return HelloReply(message=f"Hello secure, {name}!")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SyncSecureServer(BaseServer):
|
|
56
|
+
"""Synchronous secure gRPC server implementation."""
|
|
57
|
+
|
|
58
|
+
def _register_servicers(self) -> None:
|
|
59
|
+
"""Register servicers with the gRPC server."""
|
|
60
|
+
if self.server is None:
|
|
61
|
+
msg = "Server must be created before registering servicers"
|
|
62
|
+
raise RuntimeError(msg)
|
|
63
|
+
|
|
64
|
+
# Create and register the servicer
|
|
65
|
+
servicer = SyncGreeterServicer()
|
|
66
|
+
self.register_servicer(
|
|
67
|
+
servicer,
|
|
68
|
+
add_GreeterServicer_to_server,
|
|
69
|
+
service_descriptor=DESCRIPTOR,
|
|
70
|
+
)
|
|
71
|
+
logger.info("Registered Greeter servicer")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main() -> int:
|
|
75
|
+
"""Run the synchronous secure server."""
|
|
76
|
+
try:
|
|
77
|
+
# Path to certificate files
|
|
78
|
+
cert_dir = Path(__file__).parent.parent.parent / "certs"
|
|
79
|
+
# Check if certificates exist
|
|
80
|
+
if not cert_dir.exists() or not (cert_dir / "server.key").exists():
|
|
81
|
+
logger.error("Certificate files not found. Please generate them first.")
|
|
82
|
+
logger.info("Run the generate_certificates.py script to create certificates.")
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
# Create server configuration with security credentials
|
|
86
|
+
config = ServerConfig(
|
|
87
|
+
host="localhost",
|
|
88
|
+
port=50051,
|
|
89
|
+
mode=ServerMode.SYNC,
|
|
90
|
+
security=SecurityMode.SECURE,
|
|
91
|
+
credentials=ServerCredentials(
|
|
92
|
+
server_key_path=cert_dir / "server.key",
|
|
93
|
+
server_cert_path=cert_dir / "server.crt",
|
|
94
|
+
# For mTLS (mutual TLS with client authentication), uncomment:
|
|
95
|
+
# root_cert_path=cert_dir / "ca.crt", # noqa: ERA001
|
|
96
|
+
),
|
|
97
|
+
max_workers=10,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Create and start the server
|
|
101
|
+
server = SyncSecureServer(config)
|
|
102
|
+
server.start()
|
|
103
|
+
|
|
104
|
+
logger.info("Secure server started. Press Ctrl+C to stop.")
|
|
105
|
+
|
|
106
|
+
# Keep the server running until interrupted
|
|
107
|
+
try:
|
|
108
|
+
server.wait_for_termination()
|
|
109
|
+
except KeyboardInterrupt:
|
|
110
|
+
logger.info("Server stopping due to keyboard interrupt...")
|
|
111
|
+
finally:
|
|
112
|
+
server.stop()
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.exception("Error running server: %s", e)
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
return 0
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
sys.exit(main())
|
digitalkin/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core of Digitlakin defining the task management and sub-modules."""
|