julee 0.1.2__py3-none-any.whl → 0.1.4__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 (158) hide show
  1. julee/api/app.py +9 -8
  2. julee/api/dependencies.py +15 -15
  3. julee/api/requests.py +10 -9
  4. julee/api/responses.py +2 -1
  5. julee/api/routers/__init__.py +5 -5
  6. julee/api/routers/assembly_specifications.py +5 -4
  7. julee/api/routers/documents.py +1 -1
  8. julee/api/routers/knowledge_service_configs.py +4 -3
  9. julee/api/routers/knowledge_service_queries.py +7 -6
  10. julee/api/routers/system.py +4 -3
  11. julee/api/routers/workflows.py +4 -5
  12. julee/api/services/system_initialization.py +6 -6
  13. julee/api/tests/routers/test_assembly_specifications.py +4 -3
  14. julee/api/tests/routers/test_documents.py +11 -10
  15. julee/api/tests/routers/test_knowledge_service_configs.py +7 -6
  16. julee/api/tests/routers/test_knowledge_service_queries.py +4 -3
  17. julee/api/tests/routers/test_system.py +5 -4
  18. julee/api/tests/routers/test_workflows.py +5 -4
  19. julee/api/tests/test_app.py +5 -4
  20. julee/api/tests/test_dependencies.py +3 -2
  21. julee/api/tests/test_requests.py +2 -1
  22. julee/contrib/__init__.py +15 -0
  23. julee/contrib/polling/__init__.py +47 -0
  24. julee/contrib/polling/domain/__init__.py +17 -0
  25. julee/contrib/polling/domain/models/__init__.py +13 -0
  26. julee/contrib/polling/domain/models/polling_config.py +39 -0
  27. julee/contrib/polling/domain/services/__init__.py +11 -0
  28. julee/contrib/polling/domain/services/poller.py +39 -0
  29. julee/contrib/polling/infrastructure/__init__.py +15 -0
  30. julee/contrib/polling/infrastructure/services/__init__.py +12 -0
  31. julee/contrib/polling/infrastructure/services/polling/__init__.py +12 -0
  32. julee/contrib/polling/infrastructure/services/polling/http/__init__.py +12 -0
  33. julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +80 -0
  34. julee/contrib/polling/infrastructure/temporal/__init__.py +20 -0
  35. julee/contrib/polling/infrastructure/temporal/activities.py +42 -0
  36. julee/contrib/polling/infrastructure/temporal/activity_names.py +20 -0
  37. julee/contrib/polling/infrastructure/temporal/proxies.py +45 -0
  38. julee/contrib/polling/tests/__init__.py +6 -0
  39. julee/contrib/polling/tests/unit/__init__.py +6 -0
  40. julee/contrib/polling/tests/unit/infrastructure/__init__.py +7 -0
  41. julee/contrib/polling/tests/unit/infrastructure/services/__init__.py +6 -0
  42. julee/contrib/polling/tests/unit/infrastructure/services/polling/__init__.py +6 -0
  43. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/__init__.py +7 -0
  44. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +163 -0
  45. julee/docs/__init__.py +5 -0
  46. julee/docs/sphinx_hcd/__init__.py +76 -0
  47. julee/docs/sphinx_hcd/accelerators.py +1175 -0
  48. julee/docs/sphinx_hcd/apps.py +518 -0
  49. julee/docs/sphinx_hcd/config.py +148 -0
  50. julee/docs/sphinx_hcd/epics.py +453 -0
  51. julee/docs/sphinx_hcd/integrations.py +310 -0
  52. julee/docs/sphinx_hcd/journeys.py +797 -0
  53. julee/docs/sphinx_hcd/personas.py +457 -0
  54. julee/docs/sphinx_hcd/stories.py +960 -0
  55. julee/docs/sphinx_hcd/utils.py +185 -0
  56. julee/domain/models/__init__.py +5 -6
  57. julee/domain/models/assembly/assembly.py +7 -7
  58. julee/domain/models/assembly/tests/factories.py +2 -1
  59. julee/domain/models/assembly/tests/test_assembly.py +16 -13
  60. julee/domain/models/assembly_specification/assembly_specification.py +11 -10
  61. julee/domain/models/assembly_specification/knowledge_service_query.py +7 -6
  62. julee/domain/models/assembly_specification/tests/factories.py +2 -1
  63. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +9 -6
  64. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +3 -1
  65. julee/domain/models/custom_fields/content_stream.py +3 -2
  66. julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -1
  67. julee/domain/models/document/document.py +23 -30
  68. julee/domain/models/document/tests/factories.py +3 -2
  69. julee/domain/models/document/tests/test_document.py +20 -37
  70. julee/domain/models/knowledge_service_config/knowledge_service_config.py +4 -4
  71. julee/domain/models/policy/__init__.py +4 -4
  72. julee/domain/models/policy/document_policy_validation.py +17 -17
  73. julee/domain/models/policy/policy.py +10 -10
  74. julee/domain/models/policy/tests/factories.py +2 -1
  75. julee/domain/models/policy/tests/test_document_policy_validation.py +3 -1
  76. julee/domain/models/policy/tests/test_policy.py +2 -1
  77. julee/domain/repositories/__init__.py +3 -3
  78. julee/domain/repositories/assembly.py +3 -1
  79. julee/domain/repositories/assembly_specification.py +2 -0
  80. julee/domain/repositories/base.py +5 -4
  81. julee/domain/repositories/document.py +3 -1
  82. julee/domain/repositories/document_policy_validation.py +3 -1
  83. julee/domain/repositories/knowledge_service_config.py +2 -0
  84. julee/domain/repositories/knowledge_service_query.py +1 -0
  85. julee/domain/repositories/policy.py +3 -1
  86. julee/domain/use_cases/decorators.py +3 -2
  87. julee/domain/use_cases/extract_assemble_data.py +14 -13
  88. julee/domain/use_cases/initialize_system_data.py +88 -34
  89. julee/domain/use_cases/tests/test_extract_assemble_data.py +10 -10
  90. julee/domain/use_cases/tests/test_initialize_system_data.py +2 -2
  91. julee/domain/use_cases/tests/test_validate_document.py +11 -11
  92. julee/domain/use_cases/validate_document.py +14 -14
  93. julee/fixtures/documents.yaml +4 -43
  94. julee/fixtures/knowledge_service_queries.yaml +9 -0
  95. julee/maintenance/__init__.py +1 -0
  96. julee/maintenance/release.py +243 -0
  97. julee/repositories/memory/assembly.py +6 -5
  98. julee/repositories/memory/assembly_specification.py +8 -9
  99. julee/repositories/memory/base.py +12 -11
  100. julee/repositories/memory/document.py +27 -20
  101. julee/repositories/memory/document_policy_validation.py +7 -6
  102. julee/repositories/memory/knowledge_service_config.py +8 -7
  103. julee/repositories/memory/knowledge_service_query.py +8 -7
  104. julee/repositories/memory/policy.py +6 -5
  105. julee/repositories/memory/tests/test_document.py +24 -22
  106. julee/repositories/memory/tests/test_document_policy_validation.py +2 -1
  107. julee/repositories/memory/tests/test_policy.py +2 -1
  108. julee/repositories/minio/assembly.py +4 -4
  109. julee/repositories/minio/assembly_specification.py +6 -8
  110. julee/repositories/minio/client.py +22 -25
  111. julee/repositories/minio/document.py +36 -33
  112. julee/repositories/minio/document_policy_validation.py +5 -5
  113. julee/repositories/minio/knowledge_service_config.py +6 -6
  114. julee/repositories/minio/knowledge_service_query.py +6 -9
  115. julee/repositories/minio/policy.py +4 -4
  116. julee/repositories/minio/tests/fake_client.py +11 -9
  117. julee/repositories/minio/tests/test_assembly.py +3 -1
  118. julee/repositories/minio/tests/test_assembly_specification.py +2 -1
  119. julee/repositories/minio/tests/test_client_protocol.py +5 -5
  120. julee/repositories/minio/tests/test_document.py +23 -22
  121. julee/repositories/minio/tests/test_document_policy_validation.py +3 -1
  122. julee/repositories/minio/tests/test_knowledge_service_config.py +4 -2
  123. julee/repositories/minio/tests/test_knowledge_service_query.py +3 -2
  124. julee/repositories/minio/tests/test_policy.py +3 -1
  125. julee/repositories/temporal/activities.py +5 -5
  126. julee/repositories/temporal/proxies.py +5 -5
  127. julee/services/knowledge_service/__init__.py +1 -2
  128. julee/services/knowledge_service/anthropic/knowledge_service.py +8 -7
  129. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +11 -10
  130. julee/services/knowledge_service/factory.py +8 -8
  131. julee/services/knowledge_service/knowledge_service.py +12 -14
  132. julee/services/knowledge_service/memory/knowledge_service.py +13 -12
  133. julee/services/knowledge_service/memory/test_knowledge_service.py +10 -7
  134. julee/services/knowledge_service/test_factory.py +11 -10
  135. julee/services/temporal/activities.py +10 -10
  136. julee/services/temporal/proxies.py +2 -2
  137. julee/util/domain.py +6 -6
  138. julee/util/repos/minio/file_storage.py +8 -9
  139. julee/util/repos/temporal/client_proxies/file_storage.py +3 -4
  140. julee/util/repos/temporal/data_converter.py +6 -6
  141. julee/util/repos/temporal/minio_file_storage.py +1 -1
  142. julee/util/repos/temporal/proxies/file_storage.py +2 -3
  143. julee/util/repositories.py +4 -3
  144. julee/util/temporal/decorators.py +20 -18
  145. julee/util/tests/test_decorators.py +13 -15
  146. julee/util/validation/repository.py +3 -3
  147. julee/util/validation/type_guards.py +12 -11
  148. julee/worker.py +9 -8
  149. julee/workflows/__init__.py +2 -2
  150. julee/workflows/extract_assemble.py +2 -1
  151. julee/workflows/validate_document.py +3 -2
  152. {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/METADATA +3 -3
  153. julee-0.1.4.dist-info/RECORD +196 -0
  154. julee/fixtures/assembly_specifications.yaml +0 -70
  155. julee-0.1.2.dist-info/RECORD +0 -161
  156. {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/WHEEL +0 -0
  157. {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/licenses/LICENSE +0 -0
  158. {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/top_level.txt +0 -0
@@ -6,20 +6,20 @@ KnowledgeService instances based on the service API configuration.
6
6
  """
7
7
 
8
8
  import logging
9
+ from typing import Any
9
10
 
11
+ from julee.domain.models.document import Document
10
12
  from julee.domain.models.knowledge_service_config import (
11
13
  KnowledgeServiceConfig,
12
14
  ServiceApi,
13
15
  )
14
- from .knowledge_service import KnowledgeService
15
- from .anthropic import AnthropicKnowledgeService
16
- from julee.domain.models.document import Document
17
16
  from julee.services.knowledge_service import (
18
- QueryResult,
19
17
  FileRegistrationResult,
18
+ QueryResult,
20
19
  )
21
- from typing import Dict, Any, List, Optional
22
20
 
21
+ from .anthropic import AnthropicKnowledgeService
22
+ from .knowledge_service import KnowledgeService
23
23
 
24
24
  logger = logging.getLogger(__name__)
25
25
 
@@ -47,9 +47,9 @@ class ConfigurableKnowledgeService(KnowledgeService):
47
47
  self,
48
48
  config: KnowledgeServiceConfig,
49
49
  query_text: str,
50
- service_file_ids: Optional[List[str]] = None,
51
- query_metadata: Optional[Dict[str, Any]] = None,
52
- assistant_prompt: Optional[str] = None,
50
+ service_file_ids: list[str] | None = None,
51
+ query_metadata: dict[str, Any] | None = None,
52
+ assistant_prompt: str | None = None,
53
53
  ) -> QueryResult:
54
54
  """Execute a query against the knowledge service."""
55
55
  service = knowledge_service_factory(config)
@@ -11,16 +11,14 @@ Concrete implementations of this protocol are provided for different external
11
11
  services (Anthropic, OpenAI, etc.) and are created via factory functions.
12
12
  """
13
13
 
14
+ from datetime import datetime, timezone
14
15
  from typing import (
16
+ TYPE_CHECKING,
17
+ Any,
15
18
  Protocol,
16
- Optional,
17
- List,
18
19
  runtime_checkable,
19
- Dict,
20
- Any,
21
- TYPE_CHECKING,
22
20
  )
23
- from datetime import datetime, timezone
21
+
24
22
  from pydantic import BaseModel, Field
25
23
 
26
24
  if TYPE_CHECKING:
@@ -36,15 +34,15 @@ class QueryResult(BaseModel):
36
34
 
37
35
  query_id: str = Field(description="Unique identifier for this query execution")
38
36
  query_text: str = Field(description="The original query text that was executed")
39
- result_data: Dict[str, Any] = Field(
37
+ result_data: dict[str, Any] = Field(
40
38
  default_factory=dict,
41
39
  description="The structured result data from the query",
42
40
  )
43
- execution_time_ms: Optional[int] = Field(
41
+ execution_time_ms: int | None = Field(
44
42
  default=None,
45
43
  description="Time taken to execute the query in milliseconds",
46
44
  )
47
- created_at: Optional[datetime] = Field(
45
+ created_at: datetime | None = Field(
48
46
  default_factory=lambda: datetime.now(timezone.utc)
49
47
  )
50
48
 
@@ -56,11 +54,11 @@ class FileRegistrationResult(BaseModel):
56
54
  knowledge_service_file_id: str = Field(
57
55
  description="The file identifier assigned by the knowledge service"
58
56
  )
59
- registration_metadata: Dict[str, Any] = Field(
57
+ registration_metadata: dict[str, Any] = Field(
60
58
  default_factory=dict,
61
59
  description="Additional metadata from the registration process",
62
60
  )
63
- created_at: Optional[datetime] = Field(
61
+ created_at: datetime | None = Field(
64
62
  default_factory=lambda: datetime.now(timezone.utc)
65
63
  )
66
64
 
@@ -113,9 +111,9 @@ class KnowledgeService(Protocol):
113
111
  self,
114
112
  config: "KnowledgeServiceConfig",
115
113
  query_text: str,
116
- service_file_ids: Optional[List[str]] = None,
117
- query_metadata: Optional[Dict[str, Any]] = None,
118
- assistant_prompt: Optional[str] = None,
114
+ service_file_ids: list[str] | None = None,
115
+ query_metadata: dict[str, Any] | None = None,
116
+ assistant_prompt: str | None = None,
119
117
  ) -> QueryResult:
120
118
  """Execute a query against the external knowledge service.
121
119
 
@@ -8,18 +8,19 @@ scenarios where external service dependencies should be avoided.
8
8
  """
9
9
 
10
10
  import logging
11
- from typing import Optional, List, Dict, Deque, Any
12
- from datetime import datetime, timezone
13
11
  from collections import deque
12
+ from datetime import datetime, timezone
13
+ from typing import Any
14
14
 
15
+ from julee.domain.models.document import Document
15
16
  from julee.domain.models.knowledge_service_config import (
16
17
  KnowledgeServiceConfig,
17
18
  )
18
- from julee.domain.models.document import Document
19
+
19
20
  from ..knowledge_service import (
21
+ FileRegistrationResult,
20
22
  KnowledgeService,
21
23
  QueryResult,
22
- FileRegistrationResult,
23
24
  )
24
25
 
25
26
  logger = logging.getLogger(__name__)
@@ -59,10 +60,10 @@ class MemoryKnowledgeService(KnowledgeService):
59
60
  self.config = config
60
61
 
61
62
  # Storage for file registrations, keyed by knowledge_service_file_id
62
- self._registered_files: Dict[str, FileRegistrationResult] = {}
63
+ self._registered_files: dict[str, FileRegistrationResult] = {}
63
64
 
64
65
  # Queue of canned query results to return
65
- self._canned_query_results: Deque[QueryResult] = deque()
66
+ self._canned_query_results: deque[QueryResult] = deque()
66
67
 
67
68
  def add_canned_query_result(self, query_result: QueryResult) -> None:
68
69
  """Add a canned query result to be returned by execute_query.
@@ -80,7 +81,7 @@ class MemoryKnowledgeService(KnowledgeService):
80
81
  )
81
82
  self._canned_query_results.append(query_result)
82
83
 
83
- def add_canned_query_results(self, query_results: List[QueryResult]) -> None:
84
+ def add_canned_query_results(self, query_results: list[QueryResult]) -> None:
84
85
  """Add multiple canned query results to be returned by execute_query.
85
86
 
86
87
  Args:
@@ -109,7 +110,7 @@ class MemoryKnowledgeService(KnowledgeService):
109
110
 
110
111
  def get_registered_file(
111
112
  self, knowledge_service_file_id: str
112
- ) -> Optional[FileRegistrationResult]:
113
+ ) -> FileRegistrationResult | None:
113
114
  """Get a registered file by its knowledge service file ID.
114
115
 
115
116
  Args:
@@ -120,7 +121,7 @@ class MemoryKnowledgeService(KnowledgeService):
120
121
  """
121
122
  return self._registered_files.get(knowledge_service_file_id)
122
123
 
123
- def get_all_registered_files(self) -> Dict[str, FileRegistrationResult]:
124
+ def get_all_registered_files(self) -> dict[str, FileRegistrationResult]:
124
125
  """Get all registered files.
125
126
 
126
127
  Returns:
@@ -203,9 +204,9 @@ class MemoryKnowledgeService(KnowledgeService):
203
204
  self,
204
205
  config: KnowledgeServiceConfig,
205
206
  query_text: str,
206
- service_file_ids: Optional[List[str]] = None,
207
- query_metadata: Optional[Dict[str, Any]] = None,
208
- assistant_prompt: Optional[str] = None,
207
+ service_file_ids: list[str] | None = None,
208
+ query_metadata: dict[str, Any] | None = None,
209
+ assistant_prompt: str | None = None,
209
210
  ) -> QueryResult:
210
211
  """Execute a query by returning a canned response.
211
212
 
@@ -6,19 +6,22 @@ KnowledgeService protocol, verifying file registration storage and
6
6
  canned query response functionality.
7
7
  """
8
8
 
9
- import pytest
9
+ import io
10
10
  from datetime import datetime, timezone
11
- from julee.domain.models.knowledge_service_config import (
12
- KnowledgeServiceConfig,
13
- )
14
- from julee.domain.models.document import Document, DocumentStatus
15
- from julee.domain.models.knowledge_service_config import ServiceApi
11
+
12
+ import pytest
13
+
16
14
  from julee.domain.models.custom_fields.content_stream import (
17
15
  ContentStream,
18
16
  )
17
+ from julee.domain.models.document import Document, DocumentStatus
18
+ from julee.domain.models.knowledge_service_config import (
19
+ KnowledgeServiceConfig,
20
+ ServiceApi,
21
+ )
22
+
19
23
  from ..knowledge_service import QueryResult
20
24
  from .knowledge_service import MemoryKnowledgeService
21
- import io
22
25
 
23
26
 
24
27
  @pytest.fixture
@@ -5,25 +5,26 @@ This module contains tests for the factory function that creates
5
5
  KnowledgeService implementations based on configuration.
6
6
  """
7
7
 
8
+ import io
9
+ from datetime import datetime, timezone
10
+
8
11
  import pytest
9
12
 
10
- from julee.domain.models.knowledge_service_config import (
11
- KnowledgeServiceConfig,
12
- )
13
- from julee.domain.models.document import Document, DocumentStatus
14
- from julee.domain.models.knowledge_service_config import ServiceApi
15
13
  from julee.domain.models.custom_fields.content_stream import (
16
14
  ContentStream,
17
15
  )
18
- from julee.services.knowledge_service import ensure_knowledge_service
19
- from julee.services.knowledge_service.factory import (
20
- knowledge_service_factory,
16
+ from julee.domain.models.document import Document, DocumentStatus
17
+ from julee.domain.models.knowledge_service_config import (
18
+ KnowledgeServiceConfig,
19
+ ServiceApi,
21
20
  )
21
+ from julee.services.knowledge_service import ensure_knowledge_service
22
22
  from julee.services.knowledge_service.anthropic import (
23
23
  AnthropicKnowledgeService,
24
24
  )
25
- import io
26
- from datetime import datetime, timezone
25
+ from julee.services.knowledge_service.factory import (
26
+ knowledge_service_factory,
27
+ )
27
28
 
28
29
 
29
30
  @pytest.fixture
@@ -13,23 +13,23 @@ The class follows the naming pattern documented in systemPatterns.org:
13
13
 
14
14
  import io
15
15
  import logging
16
+
16
17
  from typing_extensions import override
17
18
 
18
- from julee.util.temporal.decorators import temporal_activity_registration
19
- from julee.services.knowledge_service.factory import (
20
- ConfigurableKnowledgeService,
21
- )
22
- from julee.domain.repositories.document import DocumentRepository
19
+ from julee.domain.models.document import Document
23
20
  from julee.domain.models.knowledge_service_config import (
24
21
  KnowledgeServiceConfig,
25
22
  )
26
- from julee.domain.models.document import Document
27
- from ..knowledge_service import FileRegistrationResult
28
-
29
- # Import activity name bases from shared module
23
+ from julee.domain.repositories.document import DocumentRepository
24
+ from julee.services.knowledge_service.factory import (
25
+ ConfigurableKnowledgeService,
26
+ )
30
27
  from julee.services.temporal.activity_names import (
31
28
  KNOWLEDGE_SERVICE_ACTIVITY_BASE,
32
29
  )
30
+ from julee.util.temporal.decorators import temporal_activity_registration
31
+
32
+ from ..knowledge_service import FileRegistrationResult
33
33
 
34
34
 
35
35
  @temporal_activity_registration(KNOWLEDGE_SERVICE_ACTIVITY_BASE)
@@ -79,7 +79,7 @@ class TemporalKnowledgeService(ConfigurableKnowledgeService):
79
79
  return await super().register_file(config, document)
80
80
 
81
81
 
82
- # Export the temporal service class for use in worker.py
82
+ # Export the temporal service classes for use in worker.py
83
83
  __all__ = [
84
84
  "TemporalKnowledgeService",
85
85
  "KNOWLEDGE_SERVICE_ACTIVITY_BASE",
@@ -11,13 +11,13 @@ workflow.execute_activity() with the appropriate activity names, timeouts,
11
11
  and retry policies.
12
12
  """
13
13
 
14
- from julee.util.temporal.decorators import temporal_workflow_proxy
15
14
  from julee.services.knowledge_service import KnowledgeService
16
15
 
17
16
  # Import activity name bases from shared module
18
17
  from julee.services.temporal.activity_names import (
19
18
  KNOWLEDGE_SERVICE_ACTIVITY_BASE,
20
19
  )
20
+ from julee.util.temporal.decorators import temporal_workflow_proxy
21
21
 
22
22
 
23
23
  @temporal_workflow_proxy(
@@ -35,7 +35,7 @@ class WorkflowKnowledgeServiceProxy(KnowledgeService):
35
35
  pass
36
36
 
37
37
 
38
- # Export the workflow proxy class
38
+ # Export the workflow proxy classes
39
39
  __all__ = [
40
40
  "WorkflowKnowledgeServiceProxy",
41
41
  ]
julee/util/domain.py CHANGED
@@ -1,23 +1,23 @@
1
+ from datetime import datetime, timezone
2
+
1
3
  from pydantic import (
2
4
  BaseModel,
3
5
  Field,
4
6
  field_validator,
5
7
  )
6
- from typing import Optional, Dict
7
- from datetime import datetime, timezone
8
8
 
9
9
 
10
10
  class FileMetadata(BaseModel):
11
11
  """Metadata about a stored file."""
12
12
 
13
13
  file_id: str
14
- filename: Optional[str] = None
15
- content_type: Optional[str] = None
16
- size_bytes: Optional[int] = None
14
+ filename: str | None = None
15
+ content_type: str | None = None
16
+ size_bytes: int | None = None
17
17
  uploaded_at: str = Field(
18
18
  default_factory=lambda: datetime.now(timezone.utc).isoformat()
19
19
  )
20
- metadata: Dict[str, str] = Field(default_factory=dict)
20
+ metadata: dict[str, str] = Field(default_factory=dict)
21
21
 
22
22
 
23
23
  class FileUploadArgs(BaseModel):
@@ -1,7 +1,6 @@
1
1
  import io
2
2
  import logging
3
3
  import os
4
- from typing import Optional
5
4
 
6
5
  from minio import Minio # type: ignore[import-untyped]
7
6
  from minio.error import S3Error # type: ignore[import-untyped]
@@ -20,11 +19,11 @@ class MinioFileStorageRepository(FileStorageRepository):
20
19
 
21
20
  def __init__(
22
21
  self,
23
- endpoint: Optional[str] = None,
24
- access_key: Optional[str] = None,
25
- secret_key: Optional[str] = None,
22
+ endpoint: str | None = None,
23
+ access_key: str | None = None,
24
+ secret_key: str | None = None,
26
25
  secure: bool = False,
27
- bucket_name: Optional[str] = None,
26
+ bucket_name: str | None = None,
28
27
  ):
29
28
  self._endpoint = (
30
29
  endpoint
@@ -48,7 +47,7 @@ class MinioFileStorageRepository(FileStorageRepository):
48
47
  else os.environ.get("MINIO_BUCKET_NAME", "file-storage")
49
48
  )
50
49
 
51
- self._client: Optional[Minio] = None
50
+ self._client: Minio | None = None
52
51
  logger.debug(
53
52
  "MinioFileStorageRepository initialized",
54
53
  extra={
@@ -134,7 +133,7 @@ class MinioFileStorageRepository(FileStorageRepository):
134
133
  )
135
134
  raise
136
135
 
137
- async def download_file(self, file_id: str) -> Optional[bytes]:
136
+ async def download_file(self, file_id: str) -> bytes | None:
138
137
  """Download a file from Minio storage by its ID."""
139
138
  client = await self._get_client()
140
139
  logger.info(
@@ -161,7 +160,7 @@ class MinioFileStorageRepository(FileStorageRepository):
161
160
  )
162
161
  raise
163
162
 
164
- async def get_file_metadata(self, file_id: str) -> Optional[FileMetadata]:
163
+ async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
165
164
  """Retrieve metadata for a stored file from Minio."""
166
165
  client = await self._get_client()
167
166
  logger.info(
@@ -178,7 +177,7 @@ class MinioFileStorageRepository(FileStorageRepository):
178
177
  "content_type": stat.content_type,
179
178
  },
180
179
  )
181
- uploaded_at_str: Optional[str] = (
180
+ uploaded_at_str: str | None = (
182
181
  stat.last_modified.isoformat() if stat.last_modified else None
183
182
  )
184
183
  # Extract filename and metadata more explicitly
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from typing import Optional
3
2
 
4
3
  from temporalio.client import Client
5
4
 
@@ -19,7 +18,7 @@ class TemporalFileStorageRepository(FileStorageRepository):
19
18
  def __init__(
20
19
  self,
21
20
  client: Client,
22
- concrete_repo: Optional[FileStorageRepository] = None,
21
+ concrete_repo: FileStorageRepository | None = None,
23
22
  ):
24
23
  self.client = client
25
24
  self.concrete_repo = concrete_repo
@@ -39,7 +38,7 @@ class TemporalFileStorageRepository(FileStorageRepository):
39
38
  result = await handle.result()
40
39
  return result # type: ignore[no-any-return]
41
40
 
42
- async def download_file(self, file_id: str) -> Optional[bytes]:
41
+ async def download_file(self, file_id: str) -> bytes | None:
43
42
  """Download a file via Temporal activity."""
44
43
  logger.debug(f"Client calling activity to download file: {file_id}")
45
44
 
@@ -53,7 +52,7 @@ class TemporalFileStorageRepository(FileStorageRepository):
53
52
  result = await handle.result()
54
53
  return result # type: ignore[no-any-return]
55
54
 
56
- async def get_file_metadata(self, file_id: str) -> Optional[FileMetadata]:
55
+ async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
57
56
  """Retrieve file metadata via Temporal activity."""
58
57
  logger.debug(f"Client calling activity to get file metadata: {file_id}")
59
58
 
@@ -7,20 +7,20 @@ This allows domain models to implement context-aware validation that can
7
7
  be more permissive during Temporal serialization/deserialization.
8
8
  """
9
9
 
10
- from typing import Any, Optional, Type
10
+ from typing import Any
11
11
 
12
+ import temporalio.api.common.v1
12
13
  from pydantic import TypeAdapter
13
14
  from temporalio.contrib.pydantic import (
14
15
  PydanticJSONPlainPayloadConverter,
15
16
  ToJsonOptions,
16
17
  )
17
18
  from temporalio.converter import (
18
- DataConverter,
19
19
  CompositePayloadConverter,
20
+ DataConverter,
20
21
  DefaultPayloadConverter,
21
22
  JSONPlainPayloadConverter,
22
23
  )
23
- import temporalio.api.common.v1
24
24
 
25
25
 
26
26
  class TemporalValidationPydanticConverter(PydanticJSONPlainPayloadConverter):
@@ -36,7 +36,7 @@ class TemporalValidationPydanticConverter(PydanticJSONPlainPayloadConverter):
36
36
  def from_payload(
37
37
  self,
38
38
  payload: temporalio.api.common.v1.Payload,
39
- type_hint: Optional[Type] = None,
39
+ type_hint: type | None = None,
40
40
  ) -> Any:
41
41
  """Deserialize payload with temporal_validation context.
42
42
 
@@ -69,7 +69,7 @@ class TemporalValidationPayloadConverter(CompositePayloadConverter):
69
69
  ensuring all Pydantic models get temporal_validation context.
70
70
  """
71
71
 
72
- def __init__(self, to_json_options: Optional[ToJsonOptions] = None) -> None:
72
+ def __init__(self, to_json_options: ToJsonOptions | None = None) -> None:
73
73
  """Initialize with custom JSON converter adding temporal context."""
74
74
  # Create our custom JSON converter with temporal validation
75
75
  json_payload_converter = TemporalValidationPydanticConverter(to_json_options)
@@ -89,7 +89,7 @@ class TemporalValidationPayloadConverter(CompositePayloadConverter):
89
89
 
90
90
 
91
91
  def create_temporal_data_converter(
92
- to_json_options: Optional[ToJsonOptions] = None,
92
+ to_json_options: ToJsonOptions | None = None,
93
93
  ) -> DataConverter:
94
94
  """Create a data converter with temporal validation support.
95
95
 
@@ -1,5 +1,5 @@
1
- from julee.util.temporal.decorators import temporal_activity_registration
2
1
  from julee.util.repos.minio.file_storage import MinioFileStorageRepository
2
+ from julee.util.temporal.decorators import temporal_activity_registration
3
3
 
4
4
 
5
5
  @temporal_activity_registration("util.file_storage.minio")
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from typing import Optional
3
2
 
4
3
  from temporalio import workflow
5
4
 
@@ -35,7 +34,7 @@ class WorkflowFileStorageRepositoryProxy(FileStorageRepository):
35
34
  )
36
35
  return FileMetadata.model_validate(result)
37
36
 
38
- async def download_file(self, file_id: str) -> Optional[bytes]:
37
+ async def download_file(self, file_id: str) -> bytes | None:
39
38
  """Download a file from storage via Temporal activity."""
40
39
  logger.debug(f"Workflow calling activity to download file: {file_id}")
41
40
  result = await workflow.execute_activity(
@@ -45,7 +44,7 @@ class WorkflowFileStorageRepositoryProxy(FileStorageRepository):
45
44
  )
46
45
  return result # type: ignore[no-any-return]
47
46
 
48
- async def get_file_metadata(self, file_id: str) -> Optional[FileMetadata]:
47
+ async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
49
48
  """Retrieve file metadata via Temporal activity."""
50
49
  logger.debug(f"Workflow calling activity to get file metadata: {file_id}")
51
50
  result = await workflow.execute_activity(
@@ -1,4 +1,5 @@
1
- from typing import Protocol, Optional, runtime_checkable
1
+ from typing import Protocol, runtime_checkable
2
+
2
3
  from julee.util.domain import FileMetadata, FileUploadArgs
3
4
 
4
5
 
@@ -30,7 +31,7 @@ class FileStorageRepository(Protocol):
30
31
  """
31
32
  ...
32
33
 
33
- async def download_file(self, file_id: str) -> Optional[bytes]:
34
+ async def download_file(self, file_id: str) -> bytes | None:
34
35
  """Download a file from storage by its ID.
35
36
 
36
37
  Args:
@@ -41,7 +42,7 @@ class FileStorageRepository(Protocol):
41
42
  """
42
43
  ...
43
44
 
44
- async def get_file_metadata(self, file_id: str) -> Optional[FileMetadata]:
45
+ async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
45
46
  """Retrieve metadata for a stored file.
46
47
 
47
48
  Args:
@@ -10,12 +10,10 @@ Both reduce boilerplate and ensure consistent patterns.
10
10
  import functools
11
11
  import inspect
12
12
  import logging
13
+ from collections.abc import Callable
13
14
  from datetime import timedelta
14
15
  from typing import (
15
16
  Any,
16
- Callable,
17
- Optional,
18
- Type,
19
17
  TypeVar,
20
18
  get_args,
21
19
  get_origin,
@@ -34,7 +32,7 @@ logger = logging.getLogger(__name__)
34
32
  T = TypeVar("T")
35
33
 
36
34
 
37
- def _extract_concrete_type_from_base(cls: type) -> Optional[type]:
35
+ def _extract_concrete_type_from_base(cls: type) -> type | None:
38
36
  """
39
37
  Extract the concrete type argument from a generic base class.
40
38
 
@@ -113,7 +111,7 @@ def _substitute_typevar_with_concrete(annotation: Any, concrete_type: type) -> A
113
111
 
114
112
  def temporal_activity_registration(
115
113
  activity_prefix: str,
116
- ) -> Callable[[Type[T]], Type[T]]:
114
+ ) -> Callable[[type[T]], type[T]]:
117
115
  """
118
116
  Class decorator that wraps async protocol methods as Temporal activities.
119
117
 
@@ -139,7 +137,7 @@ def temporal_activity_registration(
139
137
  # - refund_payment -> "sample.payment_repo.minio.refund_payment"
140
138
  """
141
139
 
142
- def decorator(cls: Type[T]) -> Type[T]:
140
+ def decorator(cls: type[T]) -> type[T]:
143
141
  logger.debug(
144
142
  f"Applying temporal_activity_registration decorator to {cls.__name__}"
145
143
  )
@@ -180,8 +178,9 @@ def temporal_activity_registration(
180
178
  return wrapper_method
181
179
 
182
180
  # Create the wrapper and apply activity decorator
183
- wrapper_method = create_wrapper_method(method, name)
184
- wrapped_method = activity.defn(name=activity_name)(wrapper_method)
181
+ wrapped_method = activity.defn(name=activity_name)(
182
+ create_wrapper_method(method, name)
183
+ )
185
184
 
186
185
  # Replace the method on the class with the wrapped version
187
186
  setattr(cls, name, wrapped_method)
@@ -204,8 +203,8 @@ def temporal_activity_registration(
204
203
  def temporal_workflow_proxy(
205
204
  activity_base: str,
206
205
  default_timeout_seconds: int = 30,
207
- retry_methods: Optional[list[str]] = None,
208
- ) -> Callable[[Type[T]], Type[T]]:
206
+ retry_methods: list[str] | None = None,
207
+ ) -> Callable[[type[T]], type[T]]:
209
208
  """
210
209
  Class decorator that automatically creates workflow proxy methods that
211
210
  delegate to Temporal activities.
@@ -238,7 +237,7 @@ def temporal_workflow_proxy(
238
237
  # - generate_id() -> calls "julee.document_repo.minio.generate_id" with retry
239
238
  """
240
239
 
241
- def decorator(cls: Type[T]) -> Type[T]:
240
+ def decorator(cls: type[T]) -> type[T]:
242
241
  logger.debug(f"Applying temporal_workflow_proxy decorator to {cls.__name__}")
243
242
 
244
243
  retry_methods_set = set(retry_methods or [])
@@ -390,14 +389,17 @@ def temporal_workflow_proxy(
390
389
  return workflow_method
391
390
 
392
391
  # Create and set the method on the class
393
- workflow_method = create_workflow_method(
392
+ setattr(
393
+ cls,
394
394
  method_name,
395
- needs_validation,
396
- is_optional,
397
- inner_type,
398
- original_method,
395
+ create_workflow_method(
396
+ method_name,
397
+ needs_validation,
398
+ is_optional,
399
+ inner_type,
400
+ original_method,
401
+ ),
399
402
  )
400
- setattr(cls, method_name, workflow_method)
401
403
  wrapped_methods.append(method_name)
402
404
 
403
405
  # Always generate __init__ that calls super() for consistent init
@@ -409,7 +411,7 @@ def temporal_workflow_proxy(
409
411
  proxy_self.activity_fail_fast_retry_policy = fail_fast_retry_policy
410
412
  logger.debug(f"Initialized {cls.__name__}")
411
413
 
412
- setattr(cls, "__init__", __init__)
414
+ cls.__init__ = __init__
413
415
 
414
416
  logger.info(
415
417
  f"Temporal workflow proxy decorator applied to {cls.__name__}",