digitalkin 0.1.1__tar.gz → 0.2.0__tar.gz

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 (111) hide show
  1. {digitalkin-0.1.1 → digitalkin-0.2.0}/PKG-INFO +8 -7
  2. digitalkin-0.2.0/examples/base_server/__init__.py +1 -0
  3. digitalkin-0.2.0/examples/base_server/mock/__init__.py +5 -0
  4. digitalkin-0.2.0/examples/base_server/mock/mock_pb2.py +39 -0
  5. digitalkin-0.2.0/examples/base_server/mock/mock_pb2_grpc.py +102 -0
  6. digitalkin-0.2.0/examples/base_server/server_async_insecure.py +124 -0
  7. digitalkin-0.2.0/examples/base_server/server_async_secure.py +142 -0
  8. digitalkin-0.2.0/examples/base_server/server_sync_insecure.py +102 -0
  9. digitalkin-0.2.0/examples/base_server/server_sync_secure.py +121 -0
  10. digitalkin-0.2.0/examples/modules/minimal_llm_module.py +162 -0
  11. digitalkin-0.2.0/examples/modules/storage_module.py +187 -0
  12. digitalkin-0.2.0/examples/modules/text_transform_module.py +201 -0
  13. digitalkin-0.2.0/pyproject.toml +200 -0
  14. digitalkin-0.2.0/src/digitalkin/__init__.py +8 -0
  15. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/__version__.py +1 -4
  16. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/__init__.py +1 -13
  17. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/_base_server.py +3 -3
  18. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/module_server.py +30 -12
  19. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/module_servicer.py +30 -14
  20. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/registry_server.py +6 -4
  21. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/registry_servicer.py +8 -2
  22. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/utils/factory.py +6 -4
  23. digitalkin-0.2.0/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +68 -0
  24. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/utils/models.py +1 -1
  25. digitalkin-0.2.0/src/digitalkin/models/__init__.py +8 -0
  26. digitalkin-0.2.0/src/digitalkin/models/module/__init__.py +11 -0
  27. digitalkin-0.2.0/src/digitalkin/models/module/module_types.py +10 -0
  28. digitalkin-0.2.0/src/digitalkin/models/services/__init__.py +1 -0
  29. digitalkin-0.2.0/src/digitalkin/modules/__init__.py +7 -0
  30. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/modules/_base_module.py +64 -27
  31. digitalkin-0.2.0/src/digitalkin/modules/archetype_module.py +10 -0
  32. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/modules/job_manager.py +46 -28
  33. digitalkin-0.2.0/src/digitalkin/modules/tool_module.py +10 -0
  34. digitalkin-0.2.0/src/digitalkin/modules/trigger_module.py +9 -0
  35. digitalkin-0.2.0/src/digitalkin/py.typed +0 -0
  36. digitalkin-0.2.0/src/digitalkin/services/__init__.py +26 -0
  37. digitalkin-0.2.0/src/digitalkin/services/agent/__init__.py +6 -0
  38. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/agent/agent_strategy.py +3 -6
  39. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/agent/default_agent.py +1 -4
  40. digitalkin-0.2.0/src/digitalkin/services/base_strategy.py +18 -0
  41. digitalkin-0.2.0/src/digitalkin/services/cost/__init__.py +7 -0
  42. digitalkin-0.2.0/src/digitalkin/services/cost/cost_strategy.py +45 -0
  43. digitalkin-0.2.0/src/digitalkin/services/cost/default_cost.py +30 -0
  44. digitalkin-0.2.0/src/digitalkin/services/cost/grpc_cost.py +81 -0
  45. digitalkin-0.2.0/src/digitalkin/services/filesystem/__init__.py +7 -0
  46. digitalkin-0.2.0/src/digitalkin/services/filesystem/default_filesystem.py +209 -0
  47. digitalkin-0.2.0/src/digitalkin/services/filesystem/filesystem_strategy.py +70 -0
  48. digitalkin-0.2.0/src/digitalkin/services/filesystem/grpc_filesystem.py +209 -0
  49. digitalkin-0.2.0/src/digitalkin/services/identity/__init__.py +6 -0
  50. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/identity/default_identity.py +1 -1
  51. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/identity/identity_strategy.py +3 -1
  52. digitalkin-0.2.0/src/digitalkin/services/registry/__init__.py +6 -0
  53. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/registry/default_registry.py +1 -4
  54. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/registry/registry_strategy.py +3 -6
  55. digitalkin-0.2.0/src/digitalkin/services/services_config.py +176 -0
  56. digitalkin-0.2.0/src/digitalkin/services/services_models.py +61 -0
  57. digitalkin-0.2.0/src/digitalkin/services/setup/default_setup.py +222 -0
  58. digitalkin-0.2.0/src/digitalkin/services/setup/grpc_setup.py +307 -0
  59. digitalkin-0.2.0/src/digitalkin/services/setup/setup_strategy.py +145 -0
  60. digitalkin-0.2.0/src/digitalkin/services/snapshot/__init__.py +6 -0
  61. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/snapshot/default_snapshot.py +1 -1
  62. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/services/snapshot/snapshot_strategy.py +3 -4
  63. digitalkin-0.2.0/src/digitalkin/services/storage/__init__.py +7 -0
  64. digitalkin-0.2.0/src/digitalkin/services/storage/default_storage.py +218 -0
  65. digitalkin-0.2.0/src/digitalkin/services/storage/grpc_storage.py +113 -0
  66. digitalkin-0.2.0/src/digitalkin/services/storage/storage_strategy.py +213 -0
  67. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/utils/arg_parser.py +16 -17
  68. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin.egg-info/PKG-INFO +8 -7
  69. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin.egg-info/SOURCES.txt +32 -13
  70. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin.egg-info/requires.txt +7 -6
  71. digitalkin-0.2.0/src/digitalkin.egg-info/top_level.txt +3 -0
  72. digitalkin-0.1.1/pyproject.toml +0 -198
  73. digitalkin-0.1.1/src/digitalkin/__init__.py +0 -18
  74. digitalkin-0.1.1/src/digitalkin/models/__init__.py +0 -11
  75. digitalkin-0.1.1/src/digitalkin/models/module/__init__.py +0 -5
  76. digitalkin-0.1.1/src/digitalkin/models/services/__init__.py +0 -6
  77. digitalkin-0.1.1/src/digitalkin/modules/__init__.py +0 -7
  78. digitalkin-0.1.1/src/digitalkin/modules/archetype_module.py +0 -14
  79. digitalkin-0.1.1/src/digitalkin/modules/tool_module.py +0 -14
  80. digitalkin-0.1.1/src/digitalkin/modules/trigger_module.py +0 -14
  81. digitalkin-0.1.1/src/digitalkin/services/__init__.py +0 -28
  82. digitalkin-0.1.1/src/digitalkin/services/agent/__init__.py +0 -6
  83. digitalkin-0.1.1/src/digitalkin/services/cost/__init__.py +0 -6
  84. digitalkin-0.1.1/src/digitalkin/services/cost/cost_strategy.py +0 -15
  85. digitalkin-0.1.1/src/digitalkin/services/cost/default_cost.py +0 -13
  86. digitalkin-0.1.1/src/digitalkin/services/default_service.py +0 -13
  87. digitalkin-0.1.1/src/digitalkin/services/development_service.py +0 -10
  88. digitalkin-0.1.1/src/digitalkin/services/filesystem/__init__.py +0 -6
  89. digitalkin-0.1.1/src/digitalkin/services/filesystem/default_filesystem.py +0 -29
  90. digitalkin-0.1.1/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -31
  91. digitalkin-0.1.1/src/digitalkin/services/identity/__init__.py +0 -6
  92. digitalkin-0.1.1/src/digitalkin/services/registry/__init__.py +0 -6
  93. digitalkin-0.1.1/src/digitalkin/services/service_provider.py +0 -27
  94. digitalkin-0.1.1/src/digitalkin/services/snapshot/__init__.py +0 -6
  95. digitalkin-0.1.1/src/digitalkin/services/storage/__init__.py +0 -6
  96. digitalkin-0.1.1/src/digitalkin/services/storage/default_storage.py +0 -91
  97. digitalkin-0.1.1/src/digitalkin/services/storage/grpc_storage.py +0 -207
  98. digitalkin-0.1.1/src/digitalkin/services/storage/storage_strategy.py +0 -42
  99. digitalkin-0.1.1/src/digitalkin.egg-info/top_level.txt +0 -1
  100. {digitalkin-0.1.1 → digitalkin-0.2.0}/LICENSE +0 -0
  101. {digitalkin-0.1.1 → digitalkin-0.2.0}/README.md +0 -0
  102. /digitalkin-0.1.1/src/digitalkin/py.typed → /digitalkin-0.2.0/examples/modules/__init__.py +0 -0
  103. {digitalkin-0.1.1 → digitalkin-0.2.0}/setup.cfg +0 -0
  104. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/utils/exceptions.py +0 -0
  105. {digitalkin-0.1.1/src/digitalkin/grpc → digitalkin-0.2.0/src/digitalkin/grpc_servers}/utils/types.py +0 -0
  106. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/logger.py +0 -0
  107. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/models/module/module.py +0 -0
  108. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/models/services/cost.py +0 -0
  109. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/models/services/storage.py +0 -0
  110. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin/utils/__init__.py +0 -0
  111. {digitalkin-0.1.1 → digitalkin-0.2.0}/src/digitalkin.egg-info/dependency_links.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: SDK to build kin used in DigitalKin
5
5
  Author-email: "DigitalKin.ai" <contact@digitalkin.ai>
6
6
  License: Attribution-NonCommercial-ShareAlike 4.0 International
@@ -452,25 +452,26 @@ Classifier: License :: Other/Proprietary License
452
452
  Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
- Requires-Dist: digitalkin-proto>=0.0.8
455
+ Requires-Dist: digitalkin-proto>=0.1.5
456
456
  Requires-Dist: grpcio-health-checking>=1.71.0
457
457
  Requires-Dist: grpcio-reflection>=1.71.0
458
458
  Requires-Dist: grpcio-status>=1.71.0
459
- Requires-Dist: pydantic>=2.10.6
459
+ Requires-Dist: pydantic>=2.11.3
460
460
  Provides-Extra: dev
461
461
  Requires-Dist: pytest>=8.3.4; extra == "dev"
462
- Requires-Dist: pytest-asyncio>=0.25.3; extra == "dev"
463
- Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
464
- Requires-Dist: typos>=1.30.2; extra == "dev"
462
+ Requires-Dist: pytest-asyncio>=0.26.0; extra == "dev"
463
+ Requires-Dist: pytest-cov>=6.1.0; extra == "dev"
464
+ Requires-Dist: typos>=1.31.1; extra == "dev"
465
465
  Requires-Dist: ruff>=0.11.2; extra == "dev"
466
466
  Requires-Dist: mypy>=1.15.0; extra == "dev"
467
- Requires-Dist: pyright>=1.1.397; extra == "dev"
467
+ Requires-Dist: pyright>=1.1.398; extra == "dev"
468
468
  Requires-Dist: pre-commit>=4.2.0; extra == "dev"
469
469
  Requires-Dist: bump2version>=1.0.1; extra == "dev"
470
470
  Requires-Dist: build>=1.2.2; extra == "dev"
471
471
  Requires-Dist: twine>=6.1.0; extra == "dev"
472
472
  Requires-Dist: cryptography>=44.0.2; extra == "dev"
473
473
  Requires-Dist: grpcio-testing>=1.71.0; extra == "dev"
474
+ Requires-Dist: freezegun>=1.5.1; extra == "dev"
474
475
  Provides-Extra: examples
475
476
  Requires-Dist: openai>=1.66.3; extra == "examples"
476
477
  Dynamic: license-file
@@ -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,124 @@
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._base_server import BaseServer
13
+ from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
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 AsyncGreeterImpl(Greeter):
29
+ """Asynchronous implementation of Greeter service."""
30
+
31
+ async def SayHello(self, request, context): # noqa: N802
32
+ """Asynchronous implementation of SayHello method."""
33
+ logger.info(f"Received request object: {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(f"Metadata: {key}={value}")
44
+ if key.lower() == "name":
45
+ name = value
46
+
47
+ # Simulate some async work
48
+ await asyncio.sleep(0.1)
49
+
50
+ return HelloReply(message=f"Hello async, {name}!")
51
+
52
+
53
+ class AsyncInsecureServer(BaseServer):
54
+ """Asynchronous insecure gRPC server implementation."""
55
+
56
+ def _register_servicers(self) -> None:
57
+ """Register servicers with the gRPC server."""
58
+ if self.server is None:
59
+ msg = "Server must be created before registering servicers"
60
+ raise RuntimeError(msg)
61
+
62
+ # Create and register the servicer
63
+ servicer = AsyncGreeterImpl()
64
+ self.register_servicer(
65
+ servicer,
66
+ add_GreeterServicer_to_server,
67
+ service_descriptor=DESCRIPTOR,
68
+ )
69
+ logger.info("Registered Async Greeter servicer")
70
+
71
+
72
+ async def main_async() -> int:
73
+ """Run the asynchronous insecure server."""
74
+ server = None
75
+ try:
76
+ # Create server configuration
77
+ config = ServerConfig(
78
+ host="localhost",
79
+ port=50051,
80
+ mode=ServerMode.ASYNC,
81
+ security=SecurityMode.INSECURE,
82
+ )
83
+
84
+ # Create the server
85
+ server = AsyncInsecureServer(config)
86
+
87
+ # Use the async-specific start method
88
+ await server.start_async()
89
+
90
+ logger.info("Server started. Press Ctrl+C to stop.")
91
+
92
+ # Keep the server running until interrupted
93
+ await server.await_termination()
94
+
95
+ except KeyboardInterrupt:
96
+ # This inner handler will rarely be reached,
97
+ # as the KeyboardInterrupt usually breaks out of asyncio.run()
98
+ logger.info("Server stopping due to keyboard interrupt...")
99
+ except Exception as e:
100
+ logger.exception(f"Error running server: {e}")
101
+ return 1
102
+ finally:
103
+ # Clean up resources if server was started
104
+ if server is not None and server.server is not None:
105
+ await server.stop_async()
106
+
107
+ return 0
108
+
109
+
110
+ def main():
111
+ """Run the async main function."""
112
+ try:
113
+ return asyncio.run(main_async())
114
+ except KeyboardInterrupt:
115
+ # This is the primary KeyboardInterrupt handler
116
+ logger.info("Server stopped by keyboard interrupt")
117
+ return 0 # Clean exit
118
+ except Exception as e:
119
+ logger.exception(f"Fatal error: {e}")
120
+ return 1
121
+
122
+
123
+ if __name__ == "__main__":
124
+ sys.exit(main())
@@ -0,0 +1,142 @@
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._base_server import BaseServer
13
+ from digitalkin.grpc_servers.utils.models import (
14
+ SecurityMode,
15
+ ServerConfig,
16
+ ServerCredentials,
17
+ ServerMode,
18
+ )
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 AsyncGreeterImpl(Greeter):
34
+ """Asynchronous implementation of Greeter service."""
35
+
36
+ async def SayHello(self, request, context): # noqa: N802
37
+ """Asynchronous implementation of SayHello method."""
38
+ logger.info(f"Received request object: {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(f"Metadata: {key}={value}")
49
+ if key.lower() == "name":
50
+ name = value
51
+
52
+ # Simulate some async work
53
+ await asyncio.sleep(0.1)
54
+
55
+ return HelloReply(message=f"Hello secure async, {name}!")
56
+
57
+
58
+ class AsyncSecureServer(BaseServer):
59
+ """Asynchronous secure gRPC server implementation."""
60
+
61
+ def _register_servicers(self) -> None:
62
+ """Register servicers with the gRPC server."""
63
+ if self.server is None:
64
+ msg = "Server must be created before registering servicers"
65
+ raise RuntimeError(msg)
66
+
67
+ # Create and register the servicer
68
+ servicer = AsyncGreeterImpl()
69
+ self.register_servicer(
70
+ servicer,
71
+ add_GreeterServicer_to_server,
72
+ service_descriptor=DESCRIPTOR,
73
+ )
74
+ logger.info("Registered Async Greeter servicer")
75
+
76
+
77
+ async def main_async() -> int:
78
+ """Run the asynchronous secure server."""
79
+ try:
80
+ # Path to certificate files
81
+ cert_dir = Path(__file__).parent.parent.parent / "certs"
82
+
83
+ # Check if certificates exist
84
+ if not cert_dir.exists() or not (cert_dir / "server.key").exists():
85
+ logger.error("Certificate files not found. Please generate them first.")
86
+ logger.info("Run the generate_certificates.py script to create certificates.")
87
+ return 1
88
+
89
+ # Create server configuration with security credentials
90
+ config = ServerConfig(
91
+ host="localhost",
92
+ port=50051,
93
+ mode=ServerMode.ASYNC,
94
+ security=SecurityMode.SECURE,
95
+ credentials=ServerCredentials(
96
+ server_key_path=cert_dir / "server.key",
97
+ server_cert_path=cert_dir / "server.crt",
98
+ # For mTLS (mutual TLS with client authentication), uncomment:
99
+ # root_cert_path=cert_dir / "ca.crt", # noqa: ERA001
100
+ ),
101
+ )
102
+
103
+ # Create and start the server
104
+ server = AsyncSecureServer(config)
105
+ # Use the async-specific start method
106
+ await server.start_async()
107
+
108
+ logger.info("Server started. Press Ctrl+C to stop.")
109
+
110
+ # Keep the server running until interrupted
111
+ await server.await_termination()
112
+
113
+ except KeyboardInterrupt:
114
+ # This inner handler will rarely be reached,
115
+ # as the KeyboardInterrupt usually breaks out of asyncio.run()
116
+ logger.info("Server stopping due to keyboard interrupt...")
117
+ except Exception as e:
118
+ logger.exception(f"Error running server: {e}")
119
+ return 1
120
+ finally:
121
+ # Clean up resources if server was started
122
+ if server is not None and server.server is not None:
123
+ await server.stop_async()
124
+
125
+ return 0
126
+
127
+
128
+ def main():
129
+ """Run the async main function."""
130
+ try:
131
+ return asyncio.run(main_async())
132
+ except KeyboardInterrupt:
133
+ # This is the primary KeyboardInterrupt handler
134
+ logger.info("Server stopped by keyboard interrupt")
135
+ return 0 # Clean exit
136
+ except Exception as e:
137
+ logger.exception(f"Fatal error: {e}")
138
+ return 1
139
+
140
+
141
+ if __name__ == "__main__":
142
+ sys.exit(main())
@@ -0,0 +1,102 @@
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._base_server import BaseServer
12
+ from digitalkin.grpc_servers.utils.models import SecurityMode, ServerConfig, ServerMode
13
+ from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
14
+ from examples.base_server.mock.mock_pb2_grpc import (
15
+ Greeter,
16
+ add_GreeterServicer_to_server,
17
+ )
18
+
19
+ # Configure logging
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
23
+ )
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class SyncGreeterServicer(Greeter):
28
+ """Synchronous implementation of Greeter service."""
29
+
30
+ def SayHello(self, request, context): # noqa: N802
31
+ """Implementation of SayHello method."""
32
+ logger.info(f"Received request object: {request}")
33
+ logger.info(f"Request attributes: {vars(request)}")
34
+ logger.info(f"Received request with name: {request.name}")
35
+
36
+ # If the name is still empty, try to get metadata from the context
37
+ name = request.name
38
+ if not name:
39
+ name = "unknown"
40
+ # Check context metadata
41
+ for key, value in context.invocation_metadata():
42
+ logger.info(f"Metadata: {key}={value}")
43
+ if key.lower() == "name":
44
+ name = value
45
+
46
+ return HelloReply(message=f"Hello, {name}!")
47
+
48
+
49
+ class SyncInsecureServer(BaseServer):
50
+ """Synchronous insecure gRPC server implementation."""
51
+
52
+ def _register_servicers(self) -> None:
53
+ """Register servicers with the gRPC server."""
54
+ if self.server is None:
55
+ msg = "Server must be created before registering servicers"
56
+ raise RuntimeError(msg)
57
+
58
+ # Create and register the servicer
59
+ servicer = SyncGreeterServicer()
60
+ self.register_servicer(
61
+ servicer,
62
+ add_GreeterServicer_to_server,
63
+ service_descriptor=DESCRIPTOR,
64
+ )
65
+ logger.info("Registered Greeter servicer")
66
+
67
+
68
+ def main() -> int:
69
+ """Run the synchronous insecure server."""
70
+ try:
71
+ # Create server configuration
72
+ config = ServerConfig(
73
+ host="localhost",
74
+ port=50051,
75
+ mode=ServerMode.SYNC,
76
+ security=SecurityMode.INSECURE,
77
+ max_workers=10,
78
+ )
79
+
80
+ # Create and start the server
81
+ server = SyncInsecureServer(config)
82
+ server.start()
83
+
84
+ logger.info("Server started. Press Ctrl+C to stop.")
85
+
86
+ # Keep the server running until interrupted
87
+ try:
88
+ server.wait_for_termination()
89
+ except KeyboardInterrupt:
90
+ logger.info("Server stopping due to keyboard interrupt...")
91
+ finally:
92
+ server.stop()
93
+
94
+ except Exception as e:
95
+ logger.exception(f"Error running server: {e}")
96
+ return 1
97
+
98
+ return 0
99
+
100
+
101
+ if __name__ == "__main__":
102
+ sys.exit(main())
@@ -0,0 +1,121 @@
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._base_server import BaseServer
12
+ from digitalkin.grpc_servers.utils.models import (
13
+ SecurityMode,
14
+ ServerConfig,
15
+ ServerCredentials,
16
+ ServerMode,
17
+ )
18
+ from examples.base_server.mock.mock_pb2 import DESCRIPTOR, HelloReply # type: ignore
19
+ from examples.base_server.mock.mock_pb2_grpc import (
20
+ Greeter,
21
+ add_GreeterServicer_to_server,
22
+ )
23
+
24
+ # Configure logging
25
+ logging.basicConfig(
26
+ level=logging.INFO,
27
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
28
+ )
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class SyncGreeterServicer(Greeter):
33
+ """Synchronous implementation of Greeter service."""
34
+
35
+ def SayHello(self, request, context): # noqa: N802
36
+ """Implementation of SayHello method."""
37
+ logger.info(f"Received request object: {request}")
38
+ logger.info(f"Request attributes: {vars(request)}")
39
+ logger.info(f"Received request with name: {request.name}")
40
+
41
+ # If the name is still empty, try to get metadata from the context
42
+ name = request.name
43
+ if not name:
44
+ name = "unknown"
45
+ # Check context metadata
46
+ for key, value in context.invocation_metadata():
47
+ logger.info(f"Metadata: {key}={value}")
48
+ if key.lower() == "name":
49
+ name = value
50
+
51
+ return HelloReply(message=f"Hello secure, {name}!")
52
+
53
+
54
+ class SyncSecureServer(BaseServer):
55
+ """Synchronous secure 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 = SyncGreeterServicer()
65
+ self.register_servicer(
66
+ servicer,
67
+ add_GreeterServicer_to_server,
68
+ service_descriptor=DESCRIPTOR,
69
+ )
70
+ logger.info("Registered Greeter servicer")
71
+
72
+
73
+ def main() -> int:
74
+ """Run the synchronous secure server."""
75
+ try:
76
+ # Path to certificate files
77
+ cert_dir = Path(__file__).parent.parent.parent / "certs"
78
+ # Check if certificates exist
79
+ if not cert_dir.exists() or not (cert_dir / "server.key").exists():
80
+ logger.error("Certificate files not found. Please generate them first.")
81
+ logger.info("Run the generate_certificates.py script to create certificates.")
82
+ return 1
83
+
84
+ # Create server configuration with security credentials
85
+ config = ServerConfig(
86
+ host="localhost",
87
+ port=50051,
88
+ mode=ServerMode.SYNC,
89
+ security=SecurityMode.SECURE,
90
+ credentials=ServerCredentials(
91
+ server_key_path=cert_dir / "server.key",
92
+ server_cert_path=cert_dir / "server.crt",
93
+ # For mTLS (mutual TLS with client authentication), uncomment:
94
+ # root_cert_path=cert_dir / "ca.crt", # noqa: ERA001
95
+ ),
96
+ max_workers=10,
97
+ )
98
+
99
+ # Create and start the server
100
+ server = SyncSecureServer(config)
101
+ server.start()
102
+
103
+ logger.info("Secure server started. Press Ctrl+C to stop.")
104
+
105
+ # Keep the server running until interrupted
106
+ try:
107
+ server.wait_for_termination()
108
+ except KeyboardInterrupt:
109
+ logger.info("Server stopping due to keyboard interrupt...")
110
+ finally:
111
+ server.stop()
112
+
113
+ except Exception as e:
114
+ logger.exception(f"Error running server: {e}")
115
+ return 1
116
+
117
+ return 0
118
+
119
+
120
+ if __name__ == "__main__":
121
+ sys.exit(main())