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
@@ -8,11 +8,13 @@ All domain models use Pydantic BaseModel for validation, serialization,
8
8
  and type safety, following the patterns established in the sample project.
9
9
  """
10
10
 
11
- from pydantic import BaseModel, Field, field_validator, model_validator
12
- from pydantic import ValidationInfo
13
- from typing import Callable, Optional, List, Dict, Any
11
+ from collections.abc import Callable
14
12
  from datetime import datetime, timezone
15
13
  from enum import Enum
14
+ from typing import Any
15
+
16
+ from pydantic import BaseModel, Field, ValidationInfo, field_validator, model_validator
17
+
16
18
  from julee.domain.models.custom_fields.content_stream import (
17
19
  ContentStream,
18
20
  )
@@ -75,27 +77,25 @@ class Document(BaseModel):
75
77
 
76
78
  # Document processing state
77
79
  status: DocumentStatus = DocumentStatus.CAPTURED
78
- knowledge_service_id: Optional[str] = None
79
- assembly_types: List[str] = Field(default_factory=list)
80
+ knowledge_service_id: str | None = None
81
+ assembly_types: list[str] = Field(default_factory=list)
80
82
 
81
83
  # Timestamps
82
- created_at: Optional[datetime] = Field(
84
+ created_at: datetime | None = Field(
83
85
  default_factory=lambda: datetime.now(timezone.utc)
84
86
  )
85
- updated_at: Optional[datetime] = Field(
87
+ updated_at: datetime | None = Field(
86
88
  default_factory=lambda: datetime.now(timezone.utc)
87
89
  )
88
90
 
89
91
  # Additional data and content stream
90
- additional_metadata: Dict[str, Any] = Field(default_factory=dict)
91
- content: Optional[ContentStream] = Field(default=None, exclude=True)
92
- content_string: Optional[str] = Field(
92
+ additional_metadata: dict[str, Any] = Field(default_factory=dict)
93
+ content: ContentStream | None = Field(default=None, exclude=True)
94
+
95
+ content_bytes: bytes | None = Field(
93
96
  default=None,
94
- description="Small content as string (few KB max). Use for "
95
- "workflow-generated content to avoid ContentStream serialization "
96
- "issues. For larger content, ensure calling from concrete "
97
- "implementations (ie. outside workflows and use-cases) and use "
98
- "content field instead.",
97
+ description="Raw content as bytes for cases where direct in-memory "
98
+ "binary payloads are preferred over ContentStream.",
99
99
  )
100
100
 
101
101
  @field_validator("document_id")
@@ -122,29 +122,22 @@ class Document(BaseModel):
122
122
  @field_validator("content_multihash")
123
123
  @classmethod
124
124
  def content_multihash_must_not_be_empty(cls, v: str) -> str:
125
- # TODO: actually validate the multihash against the content?
126
125
  if not v or not v.strip():
127
126
  raise ValueError("Content multihash cannot be empty")
128
127
  return v.strip()
129
128
 
130
129
  @model_validator(mode="after")
131
130
  def validate_content_fields(self, info: ValidationInfo) -> "Document":
132
- """Ensure document has either content or content_string, not both."""
133
- # Check if we're in a Temporal deserialization context
131
+ """Ensure document has at least content, or content_bytes."""
132
+
133
+ # Skip validation in Temporal deserialization context
134
134
  if info.context and info.context.get("temporal_validation"):
135
135
  return self
136
136
 
137
- # Normal validation for direct instantiation
138
137
  has_content = self.content is not None
139
- has_content_string = self.content_string is not None
140
-
141
- if has_content and has_content_string:
142
- raise ValueError(
143
- "Document cannot have both content and content_string. "
144
- "Provide only one."
145
- )
146
- elif not has_content and not has_content_string:
147
- raise ValueError(
148
- "Document must have either content or content_string. " "Provide one."
149
- )
138
+ has_content_bytes = self.content_bytes is not None
139
+
140
+ if not (has_content or has_content_bytes):
141
+ raise ValueError("Document must have one of: content, or content_bytes.")
142
+
150
143
  return self
@@ -8,14 +8,15 @@ Document domain objects with sensible defaults.
8
8
  import io
9
9
  from datetime import datetime, timezone
10
10
  from typing import Any
11
+
11
12
  from factory.base import Factory
12
- from factory.faker import Faker
13
13
  from factory.declarations import LazyAttribute, LazyFunction
14
+ from factory.faker import Faker
14
15
 
15
- from julee.domain.models.document import Document, DocumentStatus
16
16
  from julee.domain.models.custom_fields.content_stream import (
17
17
  ContentStream,
18
18
  )
19
+ from julee.domain.models.document import Document, DocumentStatus
19
20
 
20
21
 
21
22
  # Helper functions to generate content bytes consistently
@@ -19,11 +19,14 @@ Design decisions documented:
19
19
  - Documents act as readable streams with standard methods
20
20
  """
21
21
 
22
- import pytest
23
22
  import json
24
23
 
24
+ import pytest
25
+ from pydantic import ValidationError
26
+
25
27
  from julee.domain.models.document import Document
26
- from .factories import DocumentFactory, ContentStreamFactory
28
+
29
+ from .factories import ContentStreamFactory, DocumentFactory
27
30
 
28
31
 
29
32
  class TestDocumentInstantiation:
@@ -164,7 +167,7 @@ class TestDocumentInstantiation:
164
167
  assert doc.content_multihash.strip() == multihash.strip()
165
168
  else:
166
169
  # Should raise validation error
167
- with pytest.raises(Exception): # Could be ValueError or ValidationError
170
+ with pytest.raises((ValueError, ValidationError)):
168
171
  Document(
169
172
  document_id=document_id,
170
173
  original_filename=original_filename,
@@ -200,32 +203,12 @@ class TestDocumentSerialization:
200
203
 
201
204
 
202
205
  class TestDocumentContentValidation:
203
- """Test Document content and content_string validation rules."""
204
-
205
- def test_document_with_both_content_and_content_string_fails(
206
- self,
207
- ) -> None:
208
- """Test that both content and content_string raises error."""
209
- content_stream = ContentStreamFactory.build()
210
- content_string = '{"type": "string"}'
211
-
212
- with pytest.raises(
213
- ValueError, match="cannot have both content and content_string"
214
- ):
215
- Document(
216
- document_id="test-doc-both",
217
- original_filename="both.json",
218
- content_type="application/json",
219
- size_bytes=100,
220
- content_multihash="test_hash",
221
- content=content_stream,
222
- content_string=content_string,
223
- )
206
+ """Test Document content and content_bytes validation rules."""
224
207
 
225
- def test_document_without_content_or_content_string_fails(self) -> None:
226
- """Test that no content or content_string raises error."""
208
+ def test_document_without_content_or_content_bytes_fails(self) -> None:
209
+ """Test that no content or content_bytes raises error."""
227
210
  with pytest.raises(
228
- ValueError, match="must have either content or content_string"
211
+ ValueError, match="must have one of: content, or content_bytes."
229
212
  ):
230
213
  Document(
231
214
  document_id="test-doc-no-content",
@@ -234,7 +217,7 @@ class TestDocumentContentValidation:
234
217
  size_bytes=100,
235
218
  content_multihash="test_hash",
236
219
  content=None,
237
- content_string=None,
220
+ content_bytes=None,
238
221
  )
239
222
 
240
223
  def test_document_with_content_only_succeeds(self) -> None:
@@ -248,15 +231,15 @@ class TestDocumentContentValidation:
248
231
  size_bytes=100,
249
232
  content_multihash="test_hash",
250
233
  content=content_stream,
251
- content_string=None,
234
+ content_bytes=None,
252
235
  )
253
236
 
254
237
  assert doc.content is not None
255
- assert doc.content_string is None
238
+ assert doc.content_bytes is None
256
239
 
257
- def test_document_with_content_string_only_succeeds(self) -> None:
258
- """Test that document with only content_string field succeeds."""
259
- content_string = '{"type": "string"}'
240
+ def test_document_with_content_bytes_only_succeeds(self) -> None:
241
+ """Test that document with only content_bytes field succeeds."""
242
+ content_bytes = b'{"type": "string"}'
260
243
 
261
244
  doc = Document(
262
245
  document_id="test-doc-string",
@@ -265,11 +248,11 @@ class TestDocumentContentValidation:
265
248
  size_bytes=100,
266
249
  content_multihash="test_hash",
267
250
  content=None,
268
- content_string=content_string,
251
+ content_bytes=content_bytes,
269
252
  )
270
253
 
271
254
  assert doc.content is None
272
- assert doc.content_string == content_string
255
+ assert doc.content_bytes == content_bytes
273
256
 
274
257
  def test_document_deserialization_with_empty_content_succeeds(
275
258
  self,
@@ -284,7 +267,7 @@ class TestDocumentContentValidation:
284
267
  "size_bytes": 100,
285
268
  "content_multihash": "test_hash",
286
269
  "content": None,
287
- "content_string": None,
270
+ "content_bytes": None,
288
271
  }
289
272
 
290
273
  # Should succeed with temporal_validation context
@@ -294,4 +277,4 @@ class TestDocumentContentValidation:
294
277
 
295
278
  assert doc.document_id == "test-temporal"
296
279
  assert doc.content is None
297
- assert doc.content_string is None
280
+ assert doc.content_bytes is None
@@ -13,11 +13,11 @@ All domain models use Pydantic BaseModel for validation, serialization,
13
13
  and type safety, following the patterns established in the sample project.
14
14
  """
15
15
 
16
- from pydantic import BaseModel, Field, field_validator
17
- from typing import Optional
18
16
  from datetime import datetime, timezone
19
17
  from enum import Enum
20
18
 
19
+ from pydantic import BaseModel, Field, field_validator
20
+
21
21
 
22
22
  class ServiceApi(str, Enum):
23
23
  """Supported knowledge service APIs."""
@@ -48,10 +48,10 @@ class KnowledgeServiceConfig(BaseModel):
48
48
  )
49
49
 
50
50
  # Timestamps
51
- created_at: Optional[datetime] = Field(
51
+ created_at: datetime | None = Field(
52
52
  default_factory=lambda: datetime.now(timezone.utc)
53
53
  )
54
- updated_at: Optional[datetime] = Field(
54
+ updated_at: datetime | None = Field(
55
55
  default_factory=lambda: datetime.now(timezone.utc)
56
56
  )
57
57
 
@@ -1,11 +1,11 @@
1
- from .policy import (
2
- Policy,
3
- PolicyStatus,
4
- )
5
1
  from .document_policy_validation import (
6
2
  DocumentPolicyValidation,
7
3
  DocumentPolicyValidationStatus,
8
4
  )
5
+ from .policy import (
6
+ Policy,
7
+ PolicyStatus,
8
+ )
9
9
 
10
10
  __all__ = [
11
11
  "Policy",
@@ -17,11 +17,11 @@ All domain models use Pydantic BaseModel for validation, serialization,
17
17
  and type safety, following the patterns established in the sample project.
18
18
  """
19
19
 
20
- from pydantic import BaseModel, Field, field_validator
21
- from typing import Optional, List, Tuple
22
20
  from datetime import datetime, timezone
23
21
  from enum import Enum
24
22
 
23
+ from pydantic import BaseModel, Field, field_validator
24
+
25
25
 
26
26
  class DocumentPolicyValidationStatus(str, Enum):
27
27
  """Status of a document policy validation process."""
@@ -70,7 +70,7 @@ class DocumentPolicyValidation(BaseModel):
70
70
  status: DocumentPolicyValidationStatus = DocumentPolicyValidationStatus.PENDING
71
71
 
72
72
  # Initial validation results
73
- validation_scores: List[Tuple[str, int]] = Field(
73
+ validation_scores: list[tuple[str, int]] = Field(
74
74
  default_factory=list,
75
75
  description="List of (knowledge_service_query_id, actual_score) "
76
76
  "tuples representing the scores achieved during initial validation. "
@@ -78,13 +78,13 @@ class DocumentPolicyValidation(BaseModel):
78
78
  )
79
79
 
80
80
  # Transformation results (if applicable)
81
- transformed_document_id: Optional[str] = Field(
81
+ transformed_document_id: str | None = Field(
82
82
  default=None,
83
83
  description="ID of the document after transformations have been "
84
84
  "applied. Only present if the policy includes transformation queries "
85
85
  "and they were executed",
86
86
  )
87
- post_transform_validation_scores: Optional[List[Tuple[str, int]]] = Field(
87
+ post_transform_validation_scores: list[tuple[str, int]] | None = Field(
88
88
  default=None,
89
89
  description="List of (knowledge_service_query_id, actual_score) "
90
90
  "tuples representing scores achieved after transformation. "
@@ -93,19 +93,19 @@ class DocumentPolicyValidation(BaseModel):
93
93
  )
94
94
 
95
95
  # Validation metadata
96
- started_at: Optional[datetime] = Field(
96
+ started_at: datetime | None = Field(
97
97
  default_factory=lambda: datetime.now(timezone.utc),
98
98
  description="When the validation process was initiated",
99
99
  )
100
- completed_at: Optional[datetime] = Field(
100
+ completed_at: datetime | None = Field(
101
101
  default=None, description="When the validation process completed"
102
102
  )
103
- error_message: Optional[str] = Field(
103
+ error_message: str | None = Field(
104
104
  default=None, description="Error message if validation process failed"
105
105
  )
106
106
 
107
107
  # Results summary
108
- passed: Optional[bool] = Field(
108
+ passed: bool | None = Field(
109
109
  default=None,
110
110
  description="Whether the document passed policy validation. "
111
111
  "None while validation is in progress, True/False when complete",
@@ -128,8 +128,8 @@ class DocumentPolicyValidation(BaseModel):
128
128
  @field_validator("validation_scores")
129
129
  @classmethod
130
130
  def validation_scores_must_be_valid(
131
- cls, v: List[Tuple[str, int]]
132
- ) -> List[Tuple[str, int]]:
131
+ cls, v: list[tuple[str, int]]
132
+ ) -> list[tuple[str, int]]:
133
133
  if not isinstance(v, list):
134
134
  raise ValueError("Validation scores must be a list")
135
135
 
@@ -142,8 +142,8 @@ class DocumentPolicyValidation(BaseModel):
142
142
  @field_validator("post_transform_validation_scores")
143
143
  @classmethod
144
144
  def post_transform_scores_must_be_valid(
145
- cls, v: Optional[List[Tuple[str, int]]]
146
- ) -> Optional[List[Tuple[str, int]]]:
145
+ cls, v: list[tuple[str, int]] | None
146
+ ) -> list[tuple[str, int]] | None:
147
147
  if v is None:
148
148
  return v
149
149
 
@@ -158,7 +158,7 @@ class DocumentPolicyValidation(BaseModel):
158
158
 
159
159
  @field_validator("error_message")
160
160
  @classmethod
161
- def error_message_must_be_valid(cls, v: Optional[str]) -> Optional[str]:
161
+ def error_message_must_be_valid(cls, v: str | None) -> str | None:
162
162
  if v is None:
163
163
  return v
164
164
  if not isinstance(v, str):
@@ -167,7 +167,7 @@ class DocumentPolicyValidation(BaseModel):
167
167
 
168
168
  @field_validator("transformed_document_id")
169
169
  @classmethod
170
- def transformed_document_id_must_be_valid(cls, v: Optional[str]) -> Optional[str]:
170
+ def transformed_document_id_must_be_valid(cls, v: str | None) -> str | None:
171
171
  if v is None:
172
172
  return v
173
173
  if not isinstance(v, str) or not v.strip():
@@ -178,8 +178,8 @@ class DocumentPolicyValidation(BaseModel):
178
178
 
179
179
  @classmethod
180
180
  def _validate_score_tuples(
181
- cls, scores: List[Tuple[str, int]], field_name: str
182
- ) -> List[Tuple[str, int]]:
181
+ cls, scores: list[tuple[str, int]], field_name: str
182
+ ) -> list[tuple[str, int]]:
183
183
  """Helper method to validate score tuple lists."""
184
184
  validated_scores = []
185
185
  query_ids_seen = set()
@@ -13,11 +13,11 @@ All domain models use Pydantic BaseModel for validation, serialization,
13
13
  and type safety, following the patterns established in the sample project.
14
14
  """
15
15
 
16
- from pydantic import BaseModel, Field, field_validator
17
- from typing import Optional, List, Tuple
18
16
  from datetime import datetime, timezone
19
17
  from enum import Enum
20
18
 
19
+ from pydantic import BaseModel, Field, field_validator
20
+
21
21
 
22
22
  class PolicyStatus(str, Enum):
23
23
  """Status of a policy configuration."""
@@ -55,12 +55,12 @@ class Policy(BaseModel):
55
55
 
56
56
  # Policy configuration
57
57
  status: PolicyStatus = PolicyStatus.ACTIVE
58
- validation_scores: List[Tuple[str, int]] = Field(
58
+ validation_scores: list[tuple[str, int]] = Field(
59
59
  description="List of (knowledge_service_query_id, required_score) "
60
60
  "tuples where required_score is between 0 and 100. All scores "
61
61
  "must be met or exceeded for the policy to pass"
62
62
  )
63
- transformation_queries: Optional[List[str]] = Field(
63
+ transformation_queries: list[str] | None = Field(
64
64
  default=None,
65
65
  description="Optional list of knowledge service query IDs for "
66
66
  "transformations to apply before re-validation. If not provided "
@@ -69,10 +69,10 @@ class Policy(BaseModel):
69
69
 
70
70
  # Policy metadata
71
71
  version: str = Field(default="0.1.0", description="Policy version")
72
- created_at: Optional[datetime] = Field(
72
+ created_at: datetime | None = Field(
73
73
  default_factory=lambda: datetime.now(timezone.utc)
74
74
  )
75
- updated_at: Optional[datetime] = Field(default=None)
75
+ updated_at: datetime | None = Field(default=None)
76
76
 
77
77
  @field_validator("policy_id")
78
78
  @classmethod
@@ -98,8 +98,8 @@ class Policy(BaseModel):
98
98
  @field_validator("validation_scores")
99
99
  @classmethod
100
100
  def validation_scores_must_be_valid(
101
- cls, v: List[Tuple[str, int]]
102
- ) -> List[Tuple[str, int]]:
101
+ cls, v: list[tuple[str, int]]
102
+ ) -> list[tuple[str, int]]:
103
103
  if not isinstance(v, list):
104
104
  raise ValueError("Validation scores must be a list")
105
105
 
@@ -147,8 +147,8 @@ class Policy(BaseModel):
147
147
  @field_validator("transformation_queries")
148
148
  @classmethod
149
149
  def transformation_queries_must_be_valid(
150
- cls, v: Optional[List[str]]
151
- ) -> Optional[List[str]]:
150
+ cls, v: list[str] | None
151
+ ) -> list[str] | None:
152
152
  if v is None:
153
153
  return v
154
154
 
@@ -6,9 +6,10 @@ Policy domain objects with sensible defaults.
6
6
  """
7
7
 
8
8
  from datetime import datetime, timezone
9
+
9
10
  from factory.base import Factory
10
- from factory.faker import Faker
11
11
  from factory.declarations import LazyFunction
12
+ from factory.faker import Faker
12
13
 
13
14
  from julee.domain.models.policy import (
14
15
  DocumentPolicyValidation,
@@ -13,14 +13,16 @@ Tests focus on:
13
13
  - Edge cases and error conditions
14
14
  """
15
15
 
16
- import pytest
17
16
  from datetime import datetime, timezone
17
+
18
+ import pytest
18
19
  from pydantic import ValidationError
19
20
 
20
21
  from julee.domain.models.policy import (
21
22
  DocumentPolicyValidation,
22
23
  DocumentPolicyValidationStatus,
23
24
  )
25
+
24
26
  from .factories import DocumentPolicyValidationFactory
25
27
 
26
28
 
@@ -5,8 +5,9 @@ These tests verify the behavior of Policy domain objects,
5
5
  including validation, serialization, and business logic.
6
6
  """
7
7
 
8
- import pytest
9
8
  from datetime import datetime, timezone
9
+
10
+ import pytest
10
11
  from pydantic import ValidationError
11
12
 
12
13
  from julee.domain.models.policy import (
@@ -6,14 +6,14 @@ Extract, Assemble, Publish workflow, following the Clean Architecture
6
6
  patterns established in the Fun-Police framework.
7
7
  """
8
8
 
9
- from .base import BaseRepository
10
- from .document import DocumentRepository
11
9
  from .assembly import AssemblyRepository
12
10
  from .assembly_specification import AssemblySpecificationRepository
11
+ from .base import BaseRepository
12
+ from .document import DocumentRepository
13
+ from .document_policy_validation import DocumentPolicyValidationRepository
13
14
  from .knowledge_service_config import KnowledgeServiceConfigRepository
14
15
  from .knowledge_service_query import KnowledgeServiceQueryRepository
15
16
  from .policy import PolicyRepository
16
- from .document_policy_validation import DocumentPolicyValidationRepository
17
17
 
18
18
  __all__ = [
19
19
  "BaseRepository",
@@ -27,8 +27,10 @@ In Temporal workflow contexts, these protocols are implemented by workflow
27
27
  stubs that delegate to activities for durability and proper error handling.
28
28
  """
29
29
 
30
- from typing import runtime_checkable, Protocol
30
+ from typing import Protocol, runtime_checkable
31
+
31
32
  from julee.domain.models import Assembly
33
+
32
34
  from .base import BaseRepository
33
35
 
34
36
 
@@ -30,9 +30,11 @@ stubs that delegate to activities for durability and proper error handling.
30
30
  """
31
31
 
32
32
  from typing import Protocol, runtime_checkable
33
+
33
34
  from julee.domain.models.assembly_specification import (
34
35
  AssemblySpecification,
35
36
  )
37
+
36
38
  from .base import BaseRepository
37
39
 
38
40
 
@@ -23,7 +23,8 @@ In Temporal workflow contexts, these protocols are implemented by workflow
23
23
  stubs that delegate to activities for durability and proper error handling.
24
24
  """
25
25
 
26
- from typing import Protocol, Optional, runtime_checkable, TypeVar, List, Dict
26
+ from typing import Protocol, TypeVar, runtime_checkable
27
+
27
28
  from pydantic import BaseModel
28
29
 
29
30
  # Type variable bound to Pydantic BaseModel for domain entities
@@ -42,7 +43,7 @@ class BaseRepository(Protocol[T]):
42
43
  T: The domain entity type (must extend Pydantic BaseModel)
43
44
  """
44
45
 
45
- async def get(self, entity_id: str) -> Optional[T]:
46
+ async def get(self, entity_id: str) -> T | None:
46
47
  """Retrieve an entity by ID.
47
48
 
48
49
  Args:
@@ -60,7 +61,7 @@ class BaseRepository(Protocol[T]):
60
61
  """
61
62
  ...
62
63
 
63
- async def get_many(self, entity_ids: List[str]) -> Dict[str, Optional[T]]:
64
+ async def get_many(self, entity_ids: list[str]) -> dict[str, T | None]:
64
65
  """Retrieve multiple entities by ID.
65
66
 
66
67
  Args:
@@ -103,7 +104,7 @@ class BaseRepository(Protocol[T]):
103
104
  """
104
105
  ...
105
106
 
106
- async def list_all(self) -> List[T]:
107
+ async def list_all(self) -> list[T]:
107
108
  """List all entities.
108
109
 
109
110
  Returns:
@@ -29,8 +29,10 @@ In Temporal workflow contexts, these protocols are implemented by workflow
29
29
  stubs that delegate to activities for durability and proper error handling.
30
30
  """
31
31
 
32
- from typing import runtime_checkable, Protocol
32
+ from typing import Protocol, runtime_checkable
33
+
33
34
  from julee.domain.models import Document
35
+
34
36
  from .base import BaseRepository
35
37
 
36
38
 
@@ -29,8 +29,10 @@ In Temporal workflow contexts, these protocols are implemented by workflow
29
29
  stubs that delegate to activities for durability and proper error handling.
30
30
  """
31
31
 
32
- from typing import runtime_checkable, Protocol
32
+ from typing import Protocol, runtime_checkable
33
+
33
34
  from julee.domain.models.policy import DocumentPolicyValidation
35
+
34
36
  from .base import BaseRepository
35
37
 
36
38
 
@@ -31,9 +31,11 @@ stubs that delegate to activities for durability and proper error handling.
31
31
  """
32
32
 
33
33
  from typing import Protocol, runtime_checkable
34
+
34
35
  from julee.domain.models.knowledge_service_config import (
35
36
  KnowledgeServiceConfig,
36
37
  )
38
+
37
39
  from .base import BaseRepository
38
40
 
39
41
 
@@ -25,6 +25,7 @@ from typing import Protocol, runtime_checkable
25
25
  from julee.domain.models.assembly_specification import (
26
26
  KnowledgeServiceQuery,
27
27
  )
28
+
28
29
  from .base import BaseRepository
29
30
 
30
31
 
@@ -29,8 +29,10 @@ In Temporal workflow contexts, these protocols are implemented by workflow
29
29
  stubs that delegate to activities for durability and proper error handling.
30
30
  """
31
31
 
32
- from typing import runtime_checkable, Protocol
32
+ from typing import Protocol, runtime_checkable
33
+
33
34
  from julee.domain.models import Policy
35
+
34
36
  from .base import BaseRepository
35
37
 
36
38
 
@@ -7,8 +7,9 @@ following the patterns established in the sample use cases.
7
7
  """
8
8
 
9
9
  import logging
10
+ from collections.abc import Awaitable, Callable
10
11
  from functools import wraps
11
- from typing import Any, Callable, Dict, Optional, TypeVar, Awaitable
12
+ from typing import Any, TypeVar
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
@@ -17,7 +18,7 @@ F = TypeVar("F", bound=Callable[..., Awaitable[Any]])
17
18
 
18
19
  def try_use_case_step(
19
20
  step_name: str,
20
- extra_context: Optional[Dict[str, Any]] = None,
21
+ extra_context: dict[str, Any] | None = None,
21
22
  ) -> Callable[[F], F]:
22
23
  """
23
24
  Decorator that wraps use case steps with consistent error handling and