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.
Files changed (131) hide show
  1. base_server/__init__.py +1 -0
  2. base_server/mock/__init__.py +5 -0
  3. base_server/mock/mock_pb2.py +39 -0
  4. base_server/mock/mock_pb2_grpc.py +102 -0
  5. base_server/server_async_insecure.py +125 -0
  6. base_server/server_async_secure.py +143 -0
  7. base_server/server_sync_insecure.py +103 -0
  8. base_server/server_sync_secure.py +122 -0
  9. digitalkin/__init__.py +8 -0
  10. digitalkin/__version__.py +8 -0
  11. digitalkin/core/__init__.py +1 -0
  12. digitalkin/core/common/__init__.py +9 -0
  13. digitalkin/core/common/factories.py +156 -0
  14. digitalkin/core/job_manager/__init__.py +1 -0
  15. digitalkin/core/job_manager/base_job_manager.py +288 -0
  16. digitalkin/core/job_manager/single_job_manager.py +354 -0
  17. digitalkin/core/job_manager/taskiq_broker.py +311 -0
  18. digitalkin/core/job_manager/taskiq_job_manager.py +541 -0
  19. digitalkin/core/task_manager/__init__.py +1 -0
  20. digitalkin/core/task_manager/base_task_manager.py +539 -0
  21. digitalkin/core/task_manager/local_task_manager.py +108 -0
  22. digitalkin/core/task_manager/remote_task_manager.py +87 -0
  23. digitalkin/core/task_manager/surrealdb_repository.py +266 -0
  24. digitalkin/core/task_manager/task_executor.py +249 -0
  25. digitalkin/core/task_manager/task_session.py +406 -0
  26. digitalkin/grpc_servers/__init__.py +1 -0
  27. digitalkin/grpc_servers/_base_server.py +486 -0
  28. digitalkin/grpc_servers/module_server.py +208 -0
  29. digitalkin/grpc_servers/module_servicer.py +516 -0
  30. digitalkin/grpc_servers/utils/__init__.py +1 -0
  31. digitalkin/grpc_servers/utils/exceptions.py +29 -0
  32. digitalkin/grpc_servers/utils/grpc_client_wrapper.py +88 -0
  33. digitalkin/grpc_servers/utils/grpc_error_handler.py +53 -0
  34. digitalkin/grpc_servers/utils/utility_schema_extender.py +97 -0
  35. digitalkin/logger.py +157 -0
  36. digitalkin/mixins/__init__.py +19 -0
  37. digitalkin/mixins/base_mixin.py +10 -0
  38. digitalkin/mixins/callback_mixin.py +24 -0
  39. digitalkin/mixins/chat_history_mixin.py +110 -0
  40. digitalkin/mixins/cost_mixin.py +76 -0
  41. digitalkin/mixins/file_history_mixin.py +93 -0
  42. digitalkin/mixins/filesystem_mixin.py +46 -0
  43. digitalkin/mixins/logger_mixin.py +51 -0
  44. digitalkin/mixins/storage_mixin.py +79 -0
  45. digitalkin/models/__init__.py +8 -0
  46. digitalkin/models/core/__init__.py +1 -0
  47. digitalkin/models/core/job_manager_models.py +36 -0
  48. digitalkin/models/core/task_monitor.py +70 -0
  49. digitalkin/models/grpc_servers/__init__.py +1 -0
  50. digitalkin/models/grpc_servers/models.py +275 -0
  51. digitalkin/models/grpc_servers/types.py +24 -0
  52. digitalkin/models/module/__init__.py +25 -0
  53. digitalkin/models/module/module.py +40 -0
  54. digitalkin/models/module/module_context.py +149 -0
  55. digitalkin/models/module/module_types.py +393 -0
  56. digitalkin/models/module/utility.py +146 -0
  57. digitalkin/models/services/__init__.py +10 -0
  58. digitalkin/models/services/cost.py +54 -0
  59. digitalkin/models/services/registry.py +42 -0
  60. digitalkin/models/services/storage.py +44 -0
  61. digitalkin/modules/__init__.py +11 -0
  62. digitalkin/modules/_base_module.py +517 -0
  63. digitalkin/modules/archetype_module.py +23 -0
  64. digitalkin/modules/tool_module.py +23 -0
  65. digitalkin/modules/trigger_handler.py +48 -0
  66. digitalkin/modules/triggers/__init__.py +12 -0
  67. digitalkin/modules/triggers/healthcheck_ping_trigger.py +45 -0
  68. digitalkin/modules/triggers/healthcheck_services_trigger.py +63 -0
  69. digitalkin/modules/triggers/healthcheck_status_trigger.py +52 -0
  70. digitalkin/py.typed +0 -0
  71. digitalkin/services/__init__.py +30 -0
  72. digitalkin/services/agent/__init__.py +6 -0
  73. digitalkin/services/agent/agent_strategy.py +19 -0
  74. digitalkin/services/agent/default_agent.py +13 -0
  75. digitalkin/services/base_strategy.py +22 -0
  76. digitalkin/services/communication/__init__.py +7 -0
  77. digitalkin/services/communication/communication_strategy.py +76 -0
  78. digitalkin/services/communication/default_communication.py +101 -0
  79. digitalkin/services/communication/grpc_communication.py +223 -0
  80. digitalkin/services/cost/__init__.py +14 -0
  81. digitalkin/services/cost/cost_strategy.py +100 -0
  82. digitalkin/services/cost/default_cost.py +114 -0
  83. digitalkin/services/cost/grpc_cost.py +138 -0
  84. digitalkin/services/filesystem/__init__.py +7 -0
  85. digitalkin/services/filesystem/default_filesystem.py +417 -0
  86. digitalkin/services/filesystem/filesystem_strategy.py +252 -0
  87. digitalkin/services/filesystem/grpc_filesystem.py +317 -0
  88. digitalkin/services/identity/__init__.py +6 -0
  89. digitalkin/services/identity/default_identity.py +15 -0
  90. digitalkin/services/identity/identity_strategy.py +14 -0
  91. digitalkin/services/registry/__init__.py +27 -0
  92. digitalkin/services/registry/default_registry.py +141 -0
  93. digitalkin/services/registry/exceptions.py +47 -0
  94. digitalkin/services/registry/grpc_registry.py +306 -0
  95. digitalkin/services/registry/registry_models.py +43 -0
  96. digitalkin/services/registry/registry_strategy.py +98 -0
  97. digitalkin/services/services_config.py +200 -0
  98. digitalkin/services/services_models.py +65 -0
  99. digitalkin/services/setup/__init__.py +1 -0
  100. digitalkin/services/setup/default_setup.py +219 -0
  101. digitalkin/services/setup/grpc_setup.py +343 -0
  102. digitalkin/services/setup/setup_strategy.py +145 -0
  103. digitalkin/services/snapshot/__init__.py +6 -0
  104. digitalkin/services/snapshot/default_snapshot.py +39 -0
  105. digitalkin/services/snapshot/snapshot_strategy.py +30 -0
  106. digitalkin/services/storage/__init__.py +7 -0
  107. digitalkin/services/storage/default_storage.py +228 -0
  108. digitalkin/services/storage/grpc_storage.py +214 -0
  109. digitalkin/services/storage/storage_strategy.py +273 -0
  110. digitalkin/services/user_profile/__init__.py +12 -0
  111. digitalkin/services/user_profile/default_user_profile.py +55 -0
  112. digitalkin/services/user_profile/grpc_user_profile.py +69 -0
  113. digitalkin/services/user_profile/user_profile_strategy.py +40 -0
  114. digitalkin/utils/__init__.py +29 -0
  115. digitalkin/utils/arg_parser.py +92 -0
  116. digitalkin/utils/development_mode_action.py +51 -0
  117. digitalkin/utils/dynamic_schema.py +483 -0
  118. digitalkin/utils/llm_ready_schema.py +75 -0
  119. digitalkin/utils/package_discover.py +357 -0
  120. digitalkin-0.3.2.dev2.dist-info/METADATA +602 -0
  121. digitalkin-0.3.2.dev2.dist-info/RECORD +131 -0
  122. digitalkin-0.3.2.dev2.dist-info/WHEEL +5 -0
  123. digitalkin-0.3.2.dev2.dist-info/licenses/LICENSE +430 -0
  124. digitalkin-0.3.2.dev2.dist-info/top_level.txt +4 -0
  125. modules/__init__.py +0 -0
  126. modules/cpu_intensive_module.py +280 -0
  127. modules/dynamic_setup_module.py +338 -0
  128. modules/minimal_llm_module.py +347 -0
  129. modules/text_transform_module.py +203 -0
  130. services/filesystem_module.py +200 -0
  131. services/storage_module.py +206 -0
@@ -0,0 +1 @@
1
+ """Example of a (a)synchronous (in)secure gRPC server using DigitalKin's BaseServer."""
@@ -0,0 +1,5 @@
1
+ """Basic greeting service.
2
+
3
+ This module contains the definition of the basic greeting service
4
+ implemented using Protocol Buffers and gRPC.
5
+ """
@@ -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,8 @@
1
+ """DigitalKin SDK!
2
+
3
+ This package implements the DigitalKin agentic mesh standards.
4
+ """
5
+
6
+ from digitalkin.__version__ import __version__
7
+
8
+ __all__ = ["__version__"]
@@ -0,0 +1,8 @@
1
+ """Version information."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("digitalkin")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.3.2.dev2"
@@ -0,0 +1 @@
1
+ """Core of Digitlakin defining the task management and sub-modules."""
@@ -0,0 +1,9 @@
1
+ """Common utilities for the core module."""
2
+
3
+ from digitalkin.core.common.factories import ConnectionFactory, ModuleFactory, QueueFactory
4
+
5
+ __all__ = [
6
+ "ConnectionFactory",
7
+ "ModuleFactory",
8
+ "QueueFactory",
9
+ ]