plexspaces 0.1.1__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 (87) hide show
  1. plexspaces/__init__.py +80 -0
  2. plexspaces/actor_id.py +114 -0
  3. plexspaces/decorators.py +691 -0
  4. plexspaces/generated/__init__.py +0 -0
  5. plexspaces/generated/buf/__init__.py +0 -0
  6. plexspaces/generated/buf/validate/__init__.py +3799 -0
  7. plexspaces/generated/google/__init__.py +0 -0
  8. plexspaces/generated/google/api/__init__.py +379 -0
  9. plexspaces/generated/grpc/__init__.py +0 -0
  10. plexspaces/generated/grpc/gateway/__init__.py +0 -0
  11. plexspaces/generated/grpc/gateway/protoc_gen_openapiv2/__init__.py +0 -0
  12. plexspaces/generated/grpc/gateway/protoc_gen_openapiv2/options/__init__.py +1022 -0
  13. plexspaces/generated/plexspaces/__init__.py +0 -0
  14. plexspaces/generated/plexspaces/actor/__init__.py +0 -0
  15. plexspaces/generated/plexspaces/actor/v1/__init__.py +3538 -0
  16. plexspaces/generated/plexspaces/application/__init__.py +0 -0
  17. plexspaces/generated/plexspaces/application/v1/__init__.py +827 -0
  18. plexspaces/generated/plexspaces/behaviors/__init__.py +0 -0
  19. plexspaces/generated/plexspaces/behaviors/v1/__init__.py +63 -0
  20. plexspaces/generated/plexspaces/channel/__init__.py +0 -0
  21. plexspaces/generated/plexspaces/channel/v1/__init__.py +915 -0
  22. plexspaces/generated/plexspaces/circuitbreaker/__init__.py +0 -0
  23. plexspaces/generated/plexspaces/circuitbreaker/prv/__init__.py +398 -0
  24. plexspaces/generated/plexspaces/common/__init__.py +0 -0
  25. plexspaces/generated/plexspaces/common/v1/__init__.py +691 -0
  26. plexspaces/generated/plexspaces/config/__init__.py +0 -0
  27. plexspaces/generated/plexspaces/config/v1/__init__.py +109 -0
  28. plexspaces/generated/plexspaces/dashboard/__init__.py +0 -0
  29. plexspaces/generated/plexspaces/dashboard/v1/__init__.py +647 -0
  30. plexspaces/generated/plexspaces/facets/__init__.py +0 -0
  31. plexspaces/generated/plexspaces/facets/v1/__init__.py +333 -0
  32. plexspaces/generated/plexspaces/firecracker/__init__.py +0 -0
  33. plexspaces/generated/plexspaces/firecracker/v1/__init__.py +890 -0
  34. plexspaces/generated/plexspaces/grpc/__init__.py +0 -0
  35. plexspaces/generated/plexspaces/grpc/v1/__init__.py +679 -0
  36. plexspaces/generated/plexspaces/journaling/__init__.py +0 -0
  37. plexspaces/generated/plexspaces/journaling/v1/__init__.py +1006 -0
  38. plexspaces/generated/plexspaces/keyvalue/__init__.py +0 -0
  39. plexspaces/generated/plexspaces/keyvalue/v1/__init__.py +1007 -0
  40. plexspaces/generated/plexspaces/locks/__init__.py +0 -0
  41. plexspaces/generated/plexspaces/locks/prv/__init__.py +122 -0
  42. plexspaces/generated/plexspaces/mailbox/__init__.py +0 -0
  43. plexspaces/generated/plexspaces/mailbox/v1/__init__.py +246 -0
  44. plexspaces/generated/plexspaces/metrics/__init__.py +0 -0
  45. plexspaces/generated/plexspaces/metrics/v1/__init__.py +878 -0
  46. plexspaces/generated/plexspaces/node/__init__.py +0 -0
  47. plexspaces/generated/plexspaces/node/v1/__init__.py +2564 -0
  48. plexspaces/generated/plexspaces/object_registry/__init__.py +0 -0
  49. plexspaces/generated/plexspaces/object_registry/v1/__init__.py +759 -0
  50. plexspaces/generated/plexspaces/persistence/__init__.py +0 -0
  51. plexspaces/generated/plexspaces/persistence/prv/__init__.py +539 -0
  52. plexspaces/generated/plexspaces/pool/__init__.py +0 -0
  53. plexspaces/generated/plexspaces/pool/v1/__init__.py +791 -0
  54. plexspaces/generated/plexspaces/processgroups/__init__.py +0 -0
  55. plexspaces/generated/plexspaces/processgroups/v1/__init__.py +622 -0
  56. plexspaces/generated/plexspaces/scheduling/__init__.py +0 -0
  57. plexspaces/generated/plexspaces/scheduling/v1/__init__.py +364 -0
  58. plexspaces/generated/plexspaces/security/__init__.py +0 -0
  59. plexspaces/generated/plexspaces/security/v1/__init__.py +254 -0
  60. plexspaces/generated/plexspaces/services/__init__.py +0 -0
  61. plexspaces/generated/plexspaces/services/prv/__init__.py +112 -0
  62. plexspaces/generated/plexspaces/storage/__init__.py +0 -0
  63. plexspaces/generated/plexspaces/storage/v1/__init__.py +747 -0
  64. plexspaces/generated/plexspaces/supervision/__init__.py +0 -0
  65. plexspaces/generated/plexspaces/supervision/v1/__init__.py +536 -0
  66. plexspaces/generated/plexspaces/system/__init__.py +0 -0
  67. plexspaces/generated/plexspaces/system/v1/__init__.py +1602 -0
  68. plexspaces/generated/plexspaces/timer/__init__.py +0 -0
  69. plexspaces/generated/plexspaces/timer/v1/__init__.py +402 -0
  70. plexspaces/generated/plexspaces/tuplespace/__init__.py +0 -0
  71. plexspaces/generated/plexspaces/tuplespace/v1/__init__.py +869 -0
  72. plexspaces/generated/plexspaces/wasm/__init__.py +0 -0
  73. plexspaces/generated/plexspaces/wasm/v1/__init__.py +920 -0
  74. plexspaces/generated/plexspaces/workflow/__init__.py +0 -0
  75. plexspaces/generated/plexspaces/workflow/v1/__init__.py +870 -0
  76. plexspaces/host.py +2093 -0
  77. plexspaces/leader_worker.py +148 -0
  78. plexspaces/proto_wire.py +1357 -0
  79. plexspaces/runtime.py +284 -0
  80. plexspaces/workflow.py +105 -0
  81. plexspaces-0.1.1.dist-info/METADATA +505 -0
  82. plexspaces-0.1.1.dist-info/RECORD +87 -0
  83. plexspaces-0.1.1.dist-info/WHEEL +5 -0
  84. plexspaces-0.1.1.dist-info/entry_points.txt +2 -0
  85. plexspaces-0.1.1.dist-info/top_level.txt +2 -0
  86. plexspaces_cli/__init__.py +8 -0
  87. plexspaces_cli/build.py +374 -0
@@ -0,0 +1,747 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # sources: plexspaces/v1/storage/blob.proto, plexspaces/v1/storage/storage_config.proto
3
+ # plugin: python-betterproto
4
+ # This file has been @generated
5
+
6
+ from dataclasses import dataclass
7
+ from datetime import (
8
+ datetime,
9
+ timedelta,
10
+ )
11
+ from typing import (
12
+ TYPE_CHECKING,
13
+ AsyncIterator,
14
+ Dict,
15
+ List,
16
+ Optional,
17
+ )
18
+
19
+ import betterproto
20
+ import grpclib
21
+ from betterproto.grpc.grpclib_server import ServiceBase
22
+
23
+ from ...common import v1 as __common_v1__
24
+
25
+ if TYPE_CHECKING:
26
+ import grpclib.server
27
+ from betterproto.grpc.grpclib_client import MetadataLike
28
+ from grpclib.metadata import Deadline
29
+
30
+
31
+ class StorageProvider(betterproto.Enum):
32
+ """
33
+ Storage provider type
34
+
35
+ ## Purpose
36
+ Defines the storage provider type for database backends.
37
+ Follows Kubernetes-style provider pattern for consistency.
38
+ Note: Message queue providers (Kafka, NATS) are defined in channel.proto.
39
+ """
40
+
41
+ UNSPECIFIED = 0
42
+ POSTGRES = 1
43
+ """Relational database providers"""
44
+
45
+ SQLITE = 2
46
+ REDIS = 3
47
+ """NoSQL providers"""
48
+
49
+ DYNAMODB = 4
50
+
51
+
52
+ @dataclass(eq=False, repr=False)
53
+ class BlobMetadata(betterproto.Message):
54
+ """BlobMetadata represents metadata about a stored blob"""
55
+
56
+ blob_id: str = betterproto.string_field(1)
57
+ """Unique blob identifier (ULID or SHA256-based)"""
58
+
59
+ tenant_id: str = betterproto.string_field(2)
60
+ """Tenant identifier for multi-tenancy isolation"""
61
+
62
+ namespace: str = betterproto.string_field(3)
63
+ """Namespace within tenant (e.g., "production", "staging", "dev")"""
64
+
65
+ name: str = betterproto.string_field(4)
66
+ """Display name for the blob"""
67
+
68
+ sha256: str = betterproto.string_field(5)
69
+ """SHA256 hash of blob contents (for deduplication)"""
70
+
71
+ content_type: str = betterproto.string_field(6)
72
+ """Content type (MIME type)"""
73
+
74
+ content_length: int = betterproto.int64_field(7)
75
+ """Content length in bytes"""
76
+
77
+ etag: str = betterproto.string_field(8)
78
+ """ETag from object store"""
79
+
80
+ blob_group: str = betterproto.string_field(9)
81
+ """Blob group/category (optional grouping)"""
82
+
83
+ kind: str = betterproto.string_field(10)
84
+ """Blob kind/type (e.g., "LOGS", "ARTIFACTS", "CACHE", "USER_UPLOADED")"""
85
+
86
+ metadata: Dict[str, str] = betterproto.map_field(
87
+ 11, betterproto.TYPE_STRING, betterproto.TYPE_STRING
88
+ )
89
+ """Custom metadata (key-value pairs)"""
90
+
91
+ tags: Dict[str, str] = betterproto.map_field(
92
+ 12, betterproto.TYPE_STRING, betterproto.TYPE_STRING
93
+ )
94
+ """Custom tags (key-value pairs)"""
95
+
96
+ expires_at: datetime = betterproto.message_field(13)
97
+ """Expiration time (when blob should be deleted)"""
98
+
99
+ created_at: datetime = betterproto.message_field(14)
100
+ """Creation time"""
101
+
102
+ updated_at: datetime = betterproto.message_field(15)
103
+ """Last update time"""
104
+
105
+
106
+ @dataclass(eq=False, repr=False)
107
+ class BlobConfig(betterproto.Message):
108
+ """
109
+ Blob storage configuration
110
+
111
+ ## Purpose
112
+ Configuration for S3-compatible blob storage backend (S3, embedded, GCP, Azure).
113
+ Used for storing large binary objects with metadata management.
114
+
115
+ ## Usage
116
+ This config is typically included in RuntimeConfig for node-level configuration,
117
+ but can also be used standalone for service-specific configuration.
118
+ """
119
+
120
+ backend: str = betterproto.string_field(1)
121
+ """
122
+ Backend type (s3, embedded, gcp, azure)
123
+ "embedded" uses a local S3-compatible subprocess (default for local dev/test)
124
+ """
125
+
126
+ bucket: str = betterproto.string_field(2)
127
+ """Bucket name"""
128
+
129
+ endpoint: str = betterproto.string_field(3)
130
+ """Endpoint URL (for SeaweedFS or custom S3-compatible)"""
131
+
132
+ region: str = betterproto.string_field(4)
133
+ """Region (for S3/GCP/Azure)"""
134
+
135
+ access_key_id: str = betterproto.string_field(5)
136
+ """Access key ID"""
137
+
138
+ secret_access_key: str = betterproto.string_field(6)
139
+ """Secret access key (should be from env var, not config file)"""
140
+
141
+ use_ssl: bool = betterproto.bool_field(7)
142
+ """Use SSL/TLS"""
143
+
144
+ prefix: str = betterproto.string_field(8)
145
+ """Path prefix for all blobs (default: /plexspaces)"""
146
+
147
+ gcp_service_account_json: str = betterproto.string_field(9)
148
+ """GCP-specific: Service account JSON (base64 encoded)"""
149
+
150
+ azure_account_name: str = betterproto.string_field(10)
151
+ """Azure-specific: Account name"""
152
+
153
+ azure_account_key: str = betterproto.string_field(11)
154
+ """Azure-specific: Account key"""
155
+
156
+
157
+ @dataclass(eq=False, repr=False)
158
+ class UploadBlobRequest(betterproto.Message):
159
+ """Upload blob request (HTTP multipart/form-data or raw body)"""
160
+
161
+ tenant_id: str = betterproto.string_field(1)
162
+ """Tenant ID"""
163
+
164
+ namespace: str = betterproto.string_field(2)
165
+ """Namespace"""
166
+
167
+ name: str = betterproto.string_field(3)
168
+ """Blob name"""
169
+
170
+ content_type: str = betterproto.string_field(4)
171
+ """Content type (optional, will be detected if not provided)"""
172
+
173
+ blob_group: str = betterproto.string_field(5)
174
+ """Blob group (optional)"""
175
+
176
+ kind: str = betterproto.string_field(6)
177
+ """Blob kind (optional)"""
178
+
179
+ metadata: Dict[str, str] = betterproto.map_field(
180
+ 7, betterproto.TYPE_STRING, betterproto.TYPE_STRING
181
+ )
182
+ """Custom metadata"""
183
+
184
+ tags: Dict[str, str] = betterproto.map_field(
185
+ 8, betterproto.TYPE_STRING, betterproto.TYPE_STRING
186
+ )
187
+ """Custom tags"""
188
+
189
+ expires_after: timedelta = betterproto.message_field(9)
190
+ """Expiration duration (optional)"""
191
+
192
+ data: bytes = betterproto.bytes_field(10)
193
+ """Blob data (for gRPC streaming or HTTP body)"""
194
+
195
+
196
+ @dataclass(eq=False, repr=False)
197
+ class UploadBlobResponse(betterproto.Message):
198
+ """Upload blob response"""
199
+
200
+ metadata: "BlobMetadata" = betterproto.message_field(1)
201
+
202
+
203
+ @dataclass(eq=False, repr=False)
204
+ class DownloadBlobRequest(betterproto.Message):
205
+ """Download blob request"""
206
+
207
+ blob_id: str = betterproto.string_field(1)
208
+ """Blob ID"""
209
+
210
+ tenant_id: str = betterproto.string_field(2)
211
+ """Tenant ID (for authorization)"""
212
+
213
+ namespace: str = betterproto.string_field(3)
214
+ """Namespace (for authorization)"""
215
+
216
+
217
+ @dataclass(eq=False, repr=False)
218
+ class DownloadBlobResponse(betterproto.Message):
219
+ """Download blob response (streaming)"""
220
+
221
+ metadata: "BlobMetadata" = betterproto.message_field(1)
222
+ """Blob metadata (sent first)"""
223
+
224
+ data: bytes = betterproto.bytes_field(2)
225
+ """Blob data chunks (streamed)"""
226
+
227
+
228
+ @dataclass(eq=False, repr=False)
229
+ class GetBlobMetadataRequest(betterproto.Message):
230
+ """Get blob metadata request"""
231
+
232
+ blob_id: str = betterproto.string_field(1)
233
+ tenant_id: str = betterproto.string_field(2)
234
+ namespace: str = betterproto.string_field(3)
235
+
236
+
237
+ @dataclass(eq=False, repr=False)
238
+ class GetBlobMetadataResponse(betterproto.Message):
239
+ """Get blob metadata response"""
240
+
241
+ metadata: "BlobMetadata" = betterproto.message_field(1)
242
+
243
+
244
+ @dataclass(eq=False, repr=False)
245
+ class ListBlobsRequest(betterproto.Message):
246
+ """List blobs request"""
247
+
248
+ tenant_id: str = betterproto.string_field(1)
249
+ namespace: str = betterproto.string_field(2)
250
+ name_prefix: str = betterproto.string_field(3)
251
+ """Filter by name (prefix match)"""
252
+
253
+ blob_group: str = betterproto.string_field(4)
254
+ """Filter by blob_group"""
255
+
256
+ kind: str = betterproto.string_field(5)
257
+ """Filter by kind"""
258
+
259
+ sha256: str = betterproto.string_field(6)
260
+ """Filter by SHA256"""
261
+
262
+ page: "__common_v1__.PageRequest" = betterproto.message_field(7)
263
+ """Pagination"""
264
+
265
+
266
+ @dataclass(eq=False, repr=False)
267
+ class ListBlobsResponse(betterproto.Message):
268
+ """List blobs response"""
269
+
270
+ blobs: List["BlobMetadata"] = betterproto.message_field(1)
271
+ page: "__common_v1__.PageResponse" = betterproto.message_field(2)
272
+
273
+
274
+ @dataclass(eq=False, repr=False)
275
+ class DeleteBlobRequest(betterproto.Message):
276
+ """Delete blob request"""
277
+
278
+ blob_id: str = betterproto.string_field(1)
279
+ tenant_id: str = betterproto.string_field(2)
280
+ namespace: str = betterproto.string_field(3)
281
+
282
+
283
+ @dataclass(eq=False, repr=False)
284
+ class DeleteBlobResponse(betterproto.Message):
285
+ """Delete blob response"""
286
+
287
+ pass
288
+
289
+
290
+ @dataclass(eq=False, repr=False)
291
+ class GeneratePresignedUrlRequest(betterproto.Message):
292
+ """Generate presigned URL request"""
293
+
294
+ blob_id: str = betterproto.string_field(1)
295
+ operation: str = betterproto.string_field(2)
296
+ """Operation: "GET" or "PUT"""
297
+
298
+ expires_after: timedelta = betterproto.message_field(3)
299
+ """Expiration duration (default: 1 hour)"""
300
+
301
+ tenant_id: str = betterproto.string_field(4)
302
+ namespace: str = betterproto.string_field(5)
303
+
304
+
305
+ @dataclass(eq=False, repr=False)
306
+ class GeneratePresignedUrlResponse(betterproto.Message):
307
+ """Generate presigned URL response"""
308
+
309
+ url: str = betterproto.string_field(1)
310
+ expires_at: datetime = betterproto.message_field(2)
311
+
312
+
313
+ @dataclass(eq=False, repr=False)
314
+ class SharedDbConfig(betterproto.Message):
315
+ """
316
+ Shared database configuration
317
+
318
+ ## Purpose
319
+ Configuration for components that use a shared relational database:
320
+ - scheduler (crates/scheduler/migrations)
321
+ - workflow (crates/workflow/migrations)
322
+ - object-registry (via KeyValueStore SQL backend)
323
+ - journaling (crates/journaling/migrations)
324
+ - blob (crates/blob/migrations)
325
+ - locks (crates/locks/migrations)
326
+ - channel (crates/channel/migrations)
327
+ - keyvalue (crates/keyvalue/migrations)
328
+ - tuplespace (crates/tuplespace/migrations)
329
+
330
+ ## Convention
331
+ All these components share the same database connection by default.
332
+ Can be overridden per-component if needed.
333
+
334
+ ## Environment Variables
335
+ - PLEXSPACES_DATABASE_URL: Connection string (can use ${VAR} syntax)
336
+ - PLEXSPACES_DATABASE_POOL_SIZE: Connection pool size (default: 10)
337
+ - PLEXSPACES_DATABASE_AUTO_MIGRATE: Auto-migrate on startup (default: true)
338
+ """
339
+
340
+ connection_string: str = betterproto.string_field(1)
341
+ """
342
+ Connection string (can use environment variable: ${DATABASE_URL})
343
+ Format: postgres://user:password@host:port/database
344
+ sqlite:///path/to/database.db
345
+ sqlite::memory:
346
+
347
+ Security: Passwords should be in environment variables, not config files
348
+ Example: "postgres://user:${DB_PASSWORD}@localhost:5432/plexspaces"
349
+ """
350
+
351
+ pool_size: int = betterproto.uint32_field(2)
352
+ """Connection pool size (default: 10)"""
353
+
354
+ auto_migrate: bool = betterproto.bool_field(3)
355
+ """
356
+ Auto-migrate on startup (default: true)
357
+ Runs migrations from migration_paths on node startup
358
+ """
359
+
360
+ migration_paths: List[str] = betterproto.string_field(4)
361
+ """
362
+ Migration paths (default: auto-discover from crates/*/migrations)
363
+ Paths are relative to workspace root or absolute
364
+ """
365
+
366
+
367
+ @dataclass(eq=False, repr=False)
368
+ class RedisBackendConfig(betterproto.Message):
369
+ """
370
+ Redis backend configuration
371
+
372
+ ## Purpose
373
+ Configuration for Redis-based backends (locks, channel, keyvalue, tuplespace).
374
+ """
375
+
376
+ url: str = betterproto.string_field(1)
377
+ """
378
+ Redis connection URL
379
+ Format: redis://[password@]host:port[/database]
380
+ redis://localhost:6379/0
381
+ redis://:password@redis-cluster:6379/0
382
+
383
+ Security: Passwords should be in environment variables
384
+ Example: "redis://:${REDIS_PASSWORD}@localhost:6379/0"
385
+ """
386
+
387
+ pool_size: int = betterproto.uint32_field(2)
388
+ """Connection pool size (default: 10)"""
389
+
390
+ key_prefix: str = betterproto.string_field(3)
391
+ """
392
+ Key prefix for namespace isolation (default: "plexspaces:")
393
+ All keys will be prefixed with this value
394
+ """
395
+
396
+ connect_timeout: timedelta = betterproto.message_field(4)
397
+ """Connection timeout (default: 5 seconds)"""
398
+
399
+ cluster_mode: bool = betterproto.bool_field(5)
400
+ """
401
+ Enable cluster mode (default: false)
402
+ If true, url should be comma-separated list of nodes
403
+ """
404
+
405
+
406
+ @dataclass(eq=False, repr=False)
407
+ class DynamoDbBackendConfig(betterproto.Message):
408
+ """
409
+ DynamoDB backend configuration
410
+
411
+ ## Purpose
412
+ Configuration for AWS DynamoDB-based backends (locks, keyvalue, tuplespace).
413
+ """
414
+
415
+ region: str = betterproto.string_field(1)
416
+ """AWS region (e.g., "us-east-1")"""
417
+
418
+ table_prefix: str = betterproto.string_field(2)
419
+ """
420
+ Table name prefix (default: "plexspaces-")
421
+ Actual table names will be: {prefix}{component_name}
422
+ """
423
+
424
+ endpoint_url: str = betterproto.string_field(3)
425
+ """
426
+ Endpoint URL (for local testing with DynamoDB Local)
427
+ Leave empty for production (uses AWS service)
428
+ """
429
+
430
+ access_key_id: str = betterproto.string_field(4)
431
+ """AWS access key ID (should be from env var, not config file)"""
432
+
433
+ secret_access_key: str = betterproto.string_field(5)
434
+ """AWS secret access key (should be from env var, not config file)"""
435
+
436
+
437
+ @dataclass(eq=False, repr=False)
438
+ class StorageProviderConfig(betterproto.Message):
439
+ """
440
+ Storage provider configuration
441
+
442
+ ## Purpose
443
+ Configuration for components that support multiple storage providers.
444
+ Uses provider pattern (similar to Kubernetes StorageClass) for cleaner design.
445
+
446
+ ## Industry Best Practices
447
+ - Provider-based: One provider per config (not oneof with all options)
448
+ - Explicit: Must specify provider (no magic defaults)
449
+ - Extensible: Easy to add new providers without breaking changes
450
+
451
+ ## Backend Selection Logic
452
+ 1. Provider must be explicitly specified (no auto-detection)
453
+ 2. If provider is UNSPECIFIED, config_manager sets defaults:
454
+ - locks: REDIS (if available), else use shared db
455
+ 3. Validation: Fail if provider not available (feature flags)
456
+
457
+ ## Environment Variables
458
+ - PLEXSPACES_{COMPONENT}_PROVIDER: Provider type (e.g., "redis", "postgres")
459
+ - Provider-specific env vars follow pattern: PLEXSPACES_{COMPONENT}_{PROVIDER}_{SETTING}
460
+ """
461
+
462
+ provider: "StorageProvider" = betterproto.enum_field(1)
463
+ """Storage provider type (required)"""
464
+
465
+ postgres: "SharedDbConfig" = betterproto.message_field(2, group="config")
466
+ """PostgreSQL configuration (uses shared db config)"""
467
+
468
+ sqlite: "SqliteBackendConfig" = betterproto.message_field(3, group="config")
469
+ """SQLite configuration"""
470
+
471
+ redis: "RedisBackendConfig" = betterproto.message_field(4, group="config")
472
+ """Redis configuration"""
473
+
474
+ dynamodb: "DynamoDbBackendConfig" = betterproto.message_field(5, group="config")
475
+ """DynamoDB configuration"""
476
+
477
+
478
+ @dataclass(eq=False, repr=False)
479
+ class SqliteBackendConfig(betterproto.Message):
480
+ """
481
+ SQLite backend configuration
482
+
483
+ ## Purpose
484
+ Configuration for SQLite-based backends (primarily for channel).
485
+ """
486
+
487
+ database_path: str = betterproto.string_field(1)
488
+ """
489
+ SQLite database path
490
+ - ":memory:" for in-memory database
491
+ - File path for persistent database
492
+ """
493
+
494
+ wal_mode: bool = betterproto.bool_field(2)
495
+ """Enable WAL mode for better concurrency (default: true)"""
496
+
497
+ synchronous: str = betterproto.string_field(3)
498
+ """Synchronous mode: "NORMAL", "FULL", "OFF" (default: "NORMAL")"""
499
+
500
+
501
+ @dataclass(eq=False, repr=False)
502
+ class FrameworkInfo(betterproto.Message):
503
+ """
504
+ Framework information
505
+
506
+ ## Purpose
507
+ Runtime information about the PlexSpaces framework version and build.
508
+ Auto-populated at build time from Cargo.toml.
509
+ """
510
+
511
+ version: str = betterproto.string_field(1)
512
+ """Framework version (from Cargo.toml, e.g., "0.1.0")"""
513
+
514
+ build_date: str = betterproto.string_field(2)
515
+ """Build date (ISO 8601 format, e.g., "2025-01-25T10:30:00Z")"""
516
+
517
+ git_commit: str = betterproto.string_field(3)
518
+ """Git commit hash (if available)"""
519
+
520
+ build_target: str = betterproto.string_field(4)
521
+ """Build target (e.g., "x86_64-unknown-linux-gnu")"""
522
+
523
+
524
+ class BlobServiceStub(betterproto.ServiceStub):
525
+ async def upload_blob(
526
+ self,
527
+ upload_blob_request: "UploadBlobRequest",
528
+ *,
529
+ timeout: Optional[float] = None,
530
+ deadline: Optional["Deadline"] = None,
531
+ metadata: Optional["MetadataLike"] = None
532
+ ) -> "UploadBlobResponse":
533
+ return await self._unary_unary(
534
+ "/plexspaces.storage.v1.BlobService/UploadBlob",
535
+ upload_blob_request,
536
+ UploadBlobResponse,
537
+ timeout=timeout,
538
+ deadline=deadline,
539
+ metadata=metadata,
540
+ )
541
+
542
+ async def download_blob(
543
+ self,
544
+ download_blob_request: "DownloadBlobRequest",
545
+ *,
546
+ timeout: Optional[float] = None,
547
+ deadline: Optional["Deadline"] = None,
548
+ metadata: Optional["MetadataLike"] = None
549
+ ) -> AsyncIterator[DownloadBlobResponse]:
550
+ async for response in self._unary_stream(
551
+ "/plexspaces.storage.v1.BlobService/DownloadBlob",
552
+ download_blob_request,
553
+ DownloadBlobResponse,
554
+ timeout=timeout,
555
+ deadline=deadline,
556
+ metadata=metadata,
557
+ ):
558
+ yield response
559
+
560
+ async def get_blob_metadata(
561
+ self,
562
+ get_blob_metadata_request: "GetBlobMetadataRequest",
563
+ *,
564
+ timeout: Optional[float] = None,
565
+ deadline: Optional["Deadline"] = None,
566
+ metadata: Optional["MetadataLike"] = None
567
+ ) -> "GetBlobMetadataResponse":
568
+ return await self._unary_unary(
569
+ "/plexspaces.storage.v1.BlobService/GetBlobMetadata",
570
+ get_blob_metadata_request,
571
+ GetBlobMetadataResponse,
572
+ timeout=timeout,
573
+ deadline=deadline,
574
+ metadata=metadata,
575
+ )
576
+
577
+ async def list_blobs(
578
+ self,
579
+ list_blobs_request: "ListBlobsRequest",
580
+ *,
581
+ timeout: Optional[float] = None,
582
+ deadline: Optional["Deadline"] = None,
583
+ metadata: Optional["MetadataLike"] = None
584
+ ) -> "ListBlobsResponse":
585
+ return await self._unary_unary(
586
+ "/plexspaces.storage.v1.BlobService/ListBlobs",
587
+ list_blobs_request,
588
+ ListBlobsResponse,
589
+ timeout=timeout,
590
+ deadline=deadline,
591
+ metadata=metadata,
592
+ )
593
+
594
+ async def delete_blob(
595
+ self,
596
+ delete_blob_request: "DeleteBlobRequest",
597
+ *,
598
+ timeout: Optional[float] = None,
599
+ deadline: Optional["Deadline"] = None,
600
+ metadata: Optional["MetadataLike"] = None
601
+ ) -> "DeleteBlobResponse":
602
+ return await self._unary_unary(
603
+ "/plexspaces.storage.v1.BlobService/DeleteBlob",
604
+ delete_blob_request,
605
+ DeleteBlobResponse,
606
+ timeout=timeout,
607
+ deadline=deadline,
608
+ metadata=metadata,
609
+ )
610
+
611
+ async def generate_presigned_url(
612
+ self,
613
+ generate_presigned_url_request: "GeneratePresignedUrlRequest",
614
+ *,
615
+ timeout: Optional[float] = None,
616
+ deadline: Optional["Deadline"] = None,
617
+ metadata: Optional["MetadataLike"] = None
618
+ ) -> "GeneratePresignedUrlResponse":
619
+ return await self._unary_unary(
620
+ "/plexspaces.storage.v1.BlobService/GeneratePresignedUrl",
621
+ generate_presigned_url_request,
622
+ GeneratePresignedUrlResponse,
623
+ timeout=timeout,
624
+ deadline=deadline,
625
+ metadata=metadata,
626
+ )
627
+
628
+
629
+ class BlobServiceBase(ServiceBase):
630
+
631
+ async def upload_blob(
632
+ self, upload_blob_request: "UploadBlobRequest"
633
+ ) -> "UploadBlobResponse":
634
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
635
+
636
+ async def download_blob(
637
+ self, download_blob_request: "DownloadBlobRequest"
638
+ ) -> AsyncIterator[DownloadBlobResponse]:
639
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
640
+ yield DownloadBlobResponse()
641
+
642
+ async def get_blob_metadata(
643
+ self, get_blob_metadata_request: "GetBlobMetadataRequest"
644
+ ) -> "GetBlobMetadataResponse":
645
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
646
+
647
+ async def list_blobs(
648
+ self, list_blobs_request: "ListBlobsRequest"
649
+ ) -> "ListBlobsResponse":
650
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
651
+
652
+ async def delete_blob(
653
+ self, delete_blob_request: "DeleteBlobRequest"
654
+ ) -> "DeleteBlobResponse":
655
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
656
+
657
+ async def generate_presigned_url(
658
+ self, generate_presigned_url_request: "GeneratePresignedUrlRequest"
659
+ ) -> "GeneratePresignedUrlResponse":
660
+ raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
661
+
662
+ async def __rpc_upload_blob(
663
+ self, stream: "grpclib.server.Stream[UploadBlobRequest, UploadBlobResponse]"
664
+ ) -> None:
665
+ request = await stream.recv_message()
666
+ response = await self.upload_blob(request)
667
+ await stream.send_message(response)
668
+
669
+ async def __rpc_download_blob(
670
+ self, stream: "grpclib.server.Stream[DownloadBlobRequest, DownloadBlobResponse]"
671
+ ) -> None:
672
+ request = await stream.recv_message()
673
+ await self._call_rpc_handler_server_stream(
674
+ self.download_blob,
675
+ stream,
676
+ request,
677
+ )
678
+
679
+ async def __rpc_get_blob_metadata(
680
+ self,
681
+ stream: "grpclib.server.Stream[GetBlobMetadataRequest, GetBlobMetadataResponse]",
682
+ ) -> None:
683
+ request = await stream.recv_message()
684
+ response = await self.get_blob_metadata(request)
685
+ await stream.send_message(response)
686
+
687
+ async def __rpc_list_blobs(
688
+ self, stream: "grpclib.server.Stream[ListBlobsRequest, ListBlobsResponse]"
689
+ ) -> None:
690
+ request = await stream.recv_message()
691
+ response = await self.list_blobs(request)
692
+ await stream.send_message(response)
693
+
694
+ async def __rpc_delete_blob(
695
+ self, stream: "grpclib.server.Stream[DeleteBlobRequest, DeleteBlobResponse]"
696
+ ) -> None:
697
+ request = await stream.recv_message()
698
+ response = await self.delete_blob(request)
699
+ await stream.send_message(response)
700
+
701
+ async def __rpc_generate_presigned_url(
702
+ self,
703
+ stream: "grpclib.server.Stream[GeneratePresignedUrlRequest, GeneratePresignedUrlResponse]",
704
+ ) -> None:
705
+ request = await stream.recv_message()
706
+ response = await self.generate_presigned_url(request)
707
+ await stream.send_message(response)
708
+
709
+ def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
710
+ return {
711
+ "/plexspaces.storage.v1.BlobService/UploadBlob": grpclib.const.Handler(
712
+ self.__rpc_upload_blob,
713
+ grpclib.const.Cardinality.UNARY_UNARY,
714
+ UploadBlobRequest,
715
+ UploadBlobResponse,
716
+ ),
717
+ "/plexspaces.storage.v1.BlobService/DownloadBlob": grpclib.const.Handler(
718
+ self.__rpc_download_blob,
719
+ grpclib.const.Cardinality.UNARY_STREAM,
720
+ DownloadBlobRequest,
721
+ DownloadBlobResponse,
722
+ ),
723
+ "/plexspaces.storage.v1.BlobService/GetBlobMetadata": grpclib.const.Handler(
724
+ self.__rpc_get_blob_metadata,
725
+ grpclib.const.Cardinality.UNARY_UNARY,
726
+ GetBlobMetadataRequest,
727
+ GetBlobMetadataResponse,
728
+ ),
729
+ "/plexspaces.storage.v1.BlobService/ListBlobs": grpclib.const.Handler(
730
+ self.__rpc_list_blobs,
731
+ grpclib.const.Cardinality.UNARY_UNARY,
732
+ ListBlobsRequest,
733
+ ListBlobsResponse,
734
+ ),
735
+ "/plexspaces.storage.v1.BlobService/DeleteBlob": grpclib.const.Handler(
736
+ self.__rpc_delete_blob,
737
+ grpclib.const.Cardinality.UNARY_UNARY,
738
+ DeleteBlobRequest,
739
+ DeleteBlobResponse,
740
+ ),
741
+ "/plexspaces.storage.v1.BlobService/GeneratePresignedUrl": grpclib.const.Handler(
742
+ self.__rpc_generate_presigned_url,
743
+ grpclib.const.Cardinality.UNARY_UNARY,
744
+ GeneratePresignedUrlRequest,
745
+ GeneratePresignedUrlResponse,
746
+ ),
747
+ }