julee 0.1.1__py3-none-any.whl → 0.1.3__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 (157) 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 +5 -4
  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 +82 -0
  47. julee/docs/sphinx_hcd/accelerators.py +1078 -0
  48. julee/docs/sphinx_hcd/apps.py +499 -0
  49. julee/docs/sphinx_hcd/config.py +148 -0
  50. julee/docs/sphinx_hcd/epics.py +448 -0
  51. julee/docs/sphinx_hcd/integrations.py +306 -0
  52. julee/docs/sphinx_hcd/journeys.py +783 -0
  53. julee/docs/sphinx_hcd/personas.py +435 -0
  54. julee/docs/sphinx_hcd/stories.py +932 -0
  55. julee/docs/sphinx_hcd/utils.py +180 -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 +14 -9
  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 +12 -10
  68. julee/domain/models/document/tests/factories.py +3 -2
  69. julee/domain/models/document/tests/test_document.py +6 -3
  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 +12 -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 +33 -16
  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 +23 -13
  88. julee/domain/use_cases/initialize_system_data.py +13 -13
  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 +26 -15
  93. julee/maintenance/__init__.py +1 -0
  94. julee/maintenance/release.py +188 -0
  95. julee/repositories/__init__.py +4 -1
  96. julee/repositories/memory/assembly.py +6 -5
  97. julee/repositories/memory/assembly_specification.py +9 -9
  98. julee/repositories/memory/base.py +12 -11
  99. julee/repositories/memory/document.py +8 -7
  100. julee/repositories/memory/document_policy_validation.py +7 -6
  101. julee/repositories/memory/knowledge_service_config.py +9 -7
  102. julee/repositories/memory/knowledge_service_query.py +9 -7
  103. julee/repositories/memory/policy.py +6 -5
  104. julee/repositories/memory/tests/test_document.py +6 -4
  105. julee/repositories/memory/tests/test_document_policy_validation.py +2 -1
  106. julee/repositories/memory/tests/test_policy.py +2 -1
  107. julee/repositories/minio/assembly.py +4 -4
  108. julee/repositories/minio/assembly_specification.py +6 -8
  109. julee/repositories/minio/client.py +22 -25
  110. julee/repositories/minio/document.py +11 -11
  111. julee/repositories/minio/document_policy_validation.py +5 -5
  112. julee/repositories/minio/knowledge_service_config.py +8 -8
  113. julee/repositories/minio/knowledge_service_query.py +6 -9
  114. julee/repositories/minio/policy.py +4 -4
  115. julee/repositories/minio/tests/fake_client.py +11 -9
  116. julee/repositories/minio/tests/test_assembly.py +3 -1
  117. julee/repositories/minio/tests/test_assembly_specification.py +2 -1
  118. julee/repositories/minio/tests/test_client_protocol.py +5 -5
  119. julee/repositories/minio/tests/test_document.py +7 -6
  120. julee/repositories/minio/tests/test_document_policy_validation.py +3 -1
  121. julee/repositories/minio/tests/test_knowledge_service_config.py +4 -2
  122. julee/repositories/minio/tests/test_knowledge_service_query.py +3 -2
  123. julee/repositories/minio/tests/test_policy.py +3 -1
  124. julee/repositories/temporal/activities.py +5 -5
  125. julee/repositories/temporal/proxies.py +5 -5
  126. julee/services/knowledge_service/__init__.py +1 -2
  127. julee/services/knowledge_service/anthropic/knowledge_service.py +8 -7
  128. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +11 -10
  129. julee/services/knowledge_service/factory.py +8 -8
  130. julee/services/knowledge_service/knowledge_service.py +22 -18
  131. julee/services/knowledge_service/memory/knowledge_service.py +13 -12
  132. julee/services/knowledge_service/memory/test_knowledge_service.py +10 -7
  133. julee/services/knowledge_service/test_factory.py +11 -10
  134. julee/services/temporal/activities.py +10 -10
  135. julee/services/temporal/proxies.py +2 -2
  136. julee/util/domain.py +6 -6
  137. julee/util/repos/minio/file_storage.py +8 -9
  138. julee/util/repos/temporal/client_proxies/file_storage.py +3 -4
  139. julee/util/repos/temporal/data_converter.py +6 -6
  140. julee/util/repos/temporal/minio_file_storage.py +1 -1
  141. julee/util/repos/temporal/proxies/file_storage.py +2 -3
  142. julee/util/repositories.py +6 -7
  143. julee/util/temporal/activities.py +1 -1
  144. julee/util/temporal/decorators.py +28 -33
  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.1.dist-info → julee-0.1.3.dist-info}/METADATA +4 -1
  153. julee-0.1.3.dist-info/RECORD +197 -0
  154. julee-0.1.1.dist-info/RECORD +0 -161
  155. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/WHEEL +0 -0
  156. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/licenses/LICENSE +0 -0
  157. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Release preparation and tagging script.
4
+
5
+ Usage:
6
+ release.py prepare X.Y.Z # Create release branch and PR
7
+ release.py tag X.Y.Z # Tag after PR is merged
8
+ """
9
+
10
+ import re
11
+ import subprocess
12
+ import sys
13
+ from pathlib import Path
14
+
15
+
16
+ def run(cmd: str, check: bool = True, capture: bool = True) -> subprocess.CompletedProcess:
17
+ """Run a shell command."""
18
+ result = subprocess.run(cmd, shell=True, capture_output=capture, text=True)
19
+ if check and result.returncode != 0:
20
+ print(f"ERROR: {cmd}", file=sys.stderr)
21
+ if result.stderr:
22
+ print(result.stderr, file=sys.stderr)
23
+ sys.exit(1)
24
+ return result
25
+
26
+
27
+ def get_repo_root() -> Path:
28
+ """Get the repository root directory."""
29
+ result = run("git rev-parse --show-toplevel")
30
+ return Path(result.stdout.strip())
31
+
32
+
33
+ def get_package_init(repo_root: Path) -> Path | None:
34
+ """Find __init__.py with __version__ in src/ directory."""
35
+ src_dir = repo_root / "src"
36
+ if not src_dir.exists():
37
+ return None
38
+ packages = [p for p in src_dir.iterdir() if p.is_dir() and not p.name.startswith("_")]
39
+ if len(packages) != 1:
40
+ # Multiple packages (bounded contexts) - no single __init__.py to update
41
+ return None
42
+ init_file = packages[0] / "__init__.py"
43
+ if init_file.exists() and "__version__" in init_file.read_text():
44
+ return init_file
45
+ return None
46
+
47
+
48
+ def validate_version(version: str) -> None:
49
+ """Validate version string format."""
50
+ if not re.match(r"^\d+\.\d+\.\d+$", version):
51
+ print(f"ERROR: Invalid version format '{version}'. Expected X.Y.Z", file=sys.stderr)
52
+ sys.exit(1)
53
+
54
+
55
+ def validate_git_state(require_master: bool = True) -> None:
56
+ """Validate git working tree is clean and on correct branch."""
57
+ # Check for uncommitted changes
58
+ result = run("git status --porcelain")
59
+ if result.stdout.strip():
60
+ print("ERROR: Working tree has uncommitted changes", file=sys.stderr)
61
+ sys.exit(1)
62
+
63
+ if require_master:
64
+ # Check we're on master
65
+ result = run("git branch --show-current")
66
+ branch = result.stdout.strip()
67
+ if branch not in ("master", "main"):
68
+ print(f"ERROR: Must be on master or main branch, currently on '{branch}'", file=sys.stderr)
69
+ sys.exit(1)
70
+
71
+ # Check we're up to date with remote
72
+ run("git fetch origin")
73
+ result = run("git rev-list HEAD...origin/master --count 2>/dev/null || git rev-list HEAD...origin/main --count", check=False)
74
+ if result.stdout.strip() != "0":
75
+ print("ERROR: Branch is not up to date with remote", file=sys.stderr)
76
+ sys.exit(1)
77
+
78
+
79
+ def update_version_in_file(file_path: Path, version: str, pattern: str, replacement: str) -> None:
80
+ """Update version string in a file."""
81
+ content = file_path.read_text()
82
+ new_content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
83
+ if content == new_content:
84
+ print(f"WARNING: No version replacement made in {file_path}", file=sys.stderr)
85
+ file_path.write_text(new_content)
86
+
87
+
88
+ def prepare(version: str) -> None:
89
+ """Prepare a release: create branch, update versions, push, create PR."""
90
+ validate_version(version)
91
+ validate_git_state(require_master=True)
92
+
93
+ repo_root = get_repo_root()
94
+ branch_name = f"release/v{version}"
95
+
96
+ # Create release branch
97
+ print(f"Creating branch {branch_name}...")
98
+ run(f"git checkout -b {branch_name}")
99
+
100
+ # Update pyproject.toml
101
+ pyproject = repo_root / "pyproject.toml"
102
+ print(f"Updating {pyproject}...")
103
+ update_version_in_file(
104
+ pyproject,
105
+ version,
106
+ r'^version\s*=\s*"[^"]*"',
107
+ f'version = "{version}"',
108
+ )
109
+
110
+ # Update __init__.py if it exists with __version__
111
+ init_file = get_package_init(repo_root)
112
+ if init_file:
113
+ print(f"Updating {init_file}...")
114
+ update_version_in_file(
115
+ init_file,
116
+ version,
117
+ r'^__version__\s*=\s*"[^"]*"',
118
+ f'__version__ = "{version}"',
119
+ )
120
+
121
+ # Commit
122
+ print("Committing version bump...")
123
+ run(f'git add -A && git commit -m "release: bump version to {version}"')
124
+
125
+ # Push
126
+ print(f"Pushing {branch_name}...")
127
+ run(f"git push -u origin {branch_name}")
128
+
129
+ # Create PR
130
+ print("Creating pull request...")
131
+ result = run(
132
+ f'gh pr create --title "Release v{version}" --body "Bump version to {version}"',
133
+ check=False,
134
+ )
135
+ if result.returncode != 0:
136
+ print(f"\nTo create PR manually:\n gh pr create --title 'Release v{version}'")
137
+
138
+ print(f"\nRelease branch ready. After PR is merged, run:\n ./maintenance/release.py tag {version}")
139
+
140
+
141
+ def tag(version: str) -> None:
142
+ """Tag a release after PR is merged."""
143
+ validate_version(version)
144
+
145
+ # Checkout master and pull
146
+ print("Checking out master...")
147
+ run("git checkout master || git checkout main")
148
+ run("git pull")
149
+
150
+ validate_git_state(require_master=True)
151
+
152
+ tag_name = f"v{version}"
153
+
154
+ # Check tag doesn't already exist
155
+ result = run(f"git tag -l {tag_name}")
156
+ if result.stdout.strip():
157
+ print(f"ERROR: Tag {tag_name} already exists", file=sys.stderr)
158
+ sys.exit(1)
159
+
160
+ # Create and push tag
161
+ print(f"Creating tag {tag_name}...")
162
+ run(f"git tag {tag_name}")
163
+ print(f"Pushing tag {tag_name}...")
164
+ run(f"git push origin {tag_name}")
165
+
166
+ print(f"\nRelease {tag_name} tagged and pushed.")
167
+
168
+
169
+ def main() -> None:
170
+ if len(sys.argv) < 3:
171
+ print(__doc__)
172
+ sys.exit(1)
173
+
174
+ command = sys.argv[1]
175
+ version = sys.argv[2]
176
+
177
+ if command == "prepare":
178
+ prepare(version)
179
+ elif command == "tag":
180
+ tag(version)
181
+ else:
182
+ print(f"Unknown command: {command}", file=sys.stderr)
183
+ print(__doc__)
184
+ sys.exit(1)
185
+
186
+
187
+ if __name__ == "__main__":
188
+ main()
@@ -5,13 +5,16 @@ This package contains concrete implementations of the repository interfaces
5
5
  defined in julee.domain.repositories.
6
6
 
7
7
  Implementation packages:
8
+
8
9
  - memory: In-memory implementations for testing
9
10
  - minio: MinIO-based implementations for production
10
11
  - temporal: Temporal workflow proxy implementations
11
12
 
12
- Import implementations using their full module paths, e.g.:
13
+ Import implementations using their full module paths, e.g.::
14
+
13
15
  from julee.repositories.memory import MemoryDocumentRepository
14
16
  from julee.repositories.minio.document import (
15
17
  MinioDocumentRepository,
16
18
  )
19
+
17
20
  """
@@ -12,10 +12,11 @@ All operations are still async to maintain interface compatibility.
12
12
  """
13
13
 
14
14
  import logging
15
- from typing import Optional, Dict, Any, List
15
+ from typing import Any
16
16
 
17
17
  from julee.domain.models.assembly import Assembly
18
18
  from julee.domain.repositories.assembly import AssemblyRepository
19
+
19
20
  from .base import MemoryRepositoryMixin
20
21
 
21
22
  logger = logging.getLogger(__name__)
@@ -34,11 +35,11 @@ class MemoryAssemblyRepository(AssemblyRepository, MemoryRepositoryMixin[Assembl
34
35
  """Initialize repository with empty in-memory storage."""
35
36
  self.logger = logger
36
37
  self.entity_name = "Assembly"
37
- self.storage_dict: Dict[str, Assembly] = {}
38
+ self.storage_dict: dict[str, Assembly] = {}
38
39
 
39
40
  logger.debug("Initializing MemoryAssemblyRepository")
40
41
 
41
- async def get(self, assembly_id: str) -> Optional[Assembly]:
42
+ async def get(self, assembly_id: str) -> Assembly | None:
42
43
  """Retrieve an assembly by ID.
43
44
 
44
45
  Args:
@@ -65,7 +66,7 @@ class MemoryAssemblyRepository(AssemblyRepository, MemoryRepositoryMixin[Assembl
65
66
  """
66
67
  return self.generate_entity_id("assembly")
67
68
 
68
- async def get_many(self, assembly_ids: List[str]) -> Dict[str, Optional[Assembly]]:
69
+ async def get_many(self, assembly_ids: list[str]) -> dict[str, Assembly | None]:
69
70
  """Retrieve multiple assemblies by ID.
70
71
 
71
72
  Args:
@@ -77,7 +78,7 @@ class MemoryAssemblyRepository(AssemblyRepository, MemoryRepositoryMixin[Assembl
77
78
  return self.get_many_entities(assembly_ids)
78
79
 
79
80
  def _add_entity_specific_log_data(
80
- self, entity: Assembly, log_data: Dict[str, Any]
81
+ self, entity: Assembly, log_data: dict[str, Any]
81
82
  ) -> None:
82
83
  """Add assembly-specific data to log entries."""
83
84
  super()._add_entity_specific_log_data(entity, log_data)
@@ -14,7 +14,7 @@ avoided. All operations are still async to maintain interface compatibility.
14
14
  """
15
15
 
16
16
  import logging
17
- from typing import Optional, Dict, Any, List
17
+ from typing import Any
18
18
 
19
19
  from julee.domain.models.assembly_specification import (
20
20
  AssemblySpecification,
@@ -22,6 +22,7 @@ from julee.domain.models.assembly_specification import (
22
22
  from julee.domain.repositories.assembly_specification import (
23
23
  AssemblySpecificationRepository,
24
24
  )
25
+
25
26
  from .base import MemoryRepositoryMixin
26
27
 
27
28
  logger = logging.getLogger(__name__)
@@ -36,6 +37,7 @@ class MemoryAssemblySpecificationRepository(
36
37
  dictionaries.
37
38
 
38
39
  This implementation stores assembly specifications in memory:
40
+
39
41
  - Specifications: Dictionary keyed by assembly_specification_id containing
40
42
  AssemblySpecification objects
41
43
 
@@ -47,13 +49,11 @@ class MemoryAssemblySpecificationRepository(
47
49
  """Initialize repository with empty in-memory storage."""
48
50
  self.logger = logger
49
51
  self.entity_name = "AssemblySpecification"
50
- self.storage_dict: Dict[str, AssemblySpecification] = {}
52
+ self.storage_dict: dict[str, AssemblySpecification] = {}
51
53
 
52
54
  logger.debug("Initializing MemoryAssemblySpecificationRepository")
53
55
 
54
- async def get(
55
- self, assembly_specification_id: str
56
- ) -> Optional[AssemblySpecification]:
56
+ async def get(self, assembly_specification_id: str) -> AssemblySpecification | None:
57
57
  """Retrieve an assembly specification by ID.
58
58
 
59
59
  Args:
@@ -81,8 +81,8 @@ class MemoryAssemblySpecificationRepository(
81
81
  return self.generate_entity_id("spec")
82
82
 
83
83
  async def get_many(
84
- self, assembly_specification_ids: List[str]
85
- ) -> Dict[str, Optional[AssemblySpecification]]:
84
+ self, assembly_specification_ids: list[str]
85
+ ) -> dict[str, AssemblySpecification | None]:
86
86
  """Retrieve multiple assembly specifications by ID.
87
87
 
88
88
  Args:
@@ -95,7 +95,7 @@ class MemoryAssemblySpecificationRepository(
95
95
  """
96
96
  return self.get_many_entities(assembly_specification_ids)
97
97
 
98
- async def list_all(self) -> List[AssemblySpecification]:
98
+ async def list_all(self) -> list[AssemblySpecification]:
99
99
  """List all assembly specifications.
100
100
 
101
101
  Returns:
@@ -117,7 +117,7 @@ class MemoryAssemblySpecificationRepository(
117
117
  return specifications
118
118
 
119
119
  def _add_entity_specific_log_data(
120
- self, entity: AssemblySpecification, log_data: Dict[str, Any]
120
+ self, entity: AssemblySpecification, log_data: dict[str, Any]
121
121
  ) -> None:
122
122
  """Add assembly specification-specific data to log entries."""
123
123
  super()._add_entity_specific_log_data(entity, log_data)
@@ -20,7 +20,8 @@ Classes using this mixin must provide:
20
20
 
21
21
  import uuid
22
22
  from datetime import datetime, timezone
23
- from typing import Optional, Dict, Any, TypeVar, Generic, List
23
+ from typing import Any, Generic, TypeVar
24
+
24
25
  from pydantic import BaseModel
25
26
 
26
27
  T = TypeVar("T", bound=BaseModel)
@@ -45,11 +46,11 @@ class MemoryRepositoryMixin(Generic[T]):
45
46
  """
46
47
 
47
48
  # Type annotations for attributes that implementing classes must provide
48
- storage_dict: Dict[str, T]
49
+ storage_dict: dict[str, T]
49
50
  entity_name: str
50
51
  logger: Any # logging.Logger, but avoiding import
51
52
 
52
- def get_entity(self, entity_id: str) -> Optional[T]:
53
+ def get_entity(self, entity_id: str) -> T | None:
53
54
  """Get an entity from memory storage with standardized logging.
54
55
 
55
56
  Args:
@@ -84,7 +85,7 @@ class MemoryRepositoryMixin(Generic[T]):
84
85
 
85
86
  return entity
86
87
 
87
- def get_many_entities(self, entity_ids: List[str]) -> Dict[str, Optional[T]]:
88
+ def get_many_entities(self, entity_ids: list[str]) -> dict[str, T | None]:
88
89
  """Get multiple entities from memory storage with standardized
89
90
  logging.
90
91
 
@@ -103,7 +104,7 @@ class MemoryRepositoryMixin(Generic[T]):
103
104
  },
104
105
  )
105
106
 
106
- result: Dict[str, Optional[T]] = {}
107
+ result: dict[str, T | None] = {}
107
108
  found_count = 0
108
109
 
109
110
  for entity_id in entity_ids:
@@ -160,7 +161,7 @@ class MemoryRepositoryMixin(Generic[T]):
160
161
  extra=success_extra,
161
162
  )
162
163
 
163
- def generate_entity_id(self, prefix: Optional[str] = None) -> str:
164
+ def generate_entity_id(self, prefix: str | None = None) -> str:
164
165
  """Generate a unique entity ID with consistent format.
165
166
 
166
167
  Args:
@@ -196,14 +197,14 @@ class MemoryRepositoryMixin(Generic[T]):
196
197
  hasattr(entity, "created_at")
197
198
  and getattr(entity, "created_at", None) is None
198
199
  ):
199
- setattr(entity, "created_at", now)
200
+ entity.created_at = now
200
201
 
201
202
  # Always update updated_at
202
203
  if hasattr(entity, "updated_at"):
203
- setattr(entity, "updated_at", now)
204
+ entity.updated_at = now
204
205
 
205
206
  def _add_entity_specific_log_data(
206
- self, entity: T, log_data: Dict[str, Any]
207
+ self, entity: T, log_data: dict[str, Any]
207
208
  ) -> None:
208
209
  """Add entity-specific data to log entries for richer logging.
209
210
 
@@ -216,12 +217,12 @@ class MemoryRepositoryMixin(Generic[T]):
216
217
  """
217
218
  # Default implementation adds basic model info
218
219
  if hasattr(entity, "status"):
219
- status = getattr(entity, "status")
220
+ status = entity.status
220
221
  log_data["status"] = (
221
222
  status.value if hasattr(status, "value") else str(status)
222
223
  )
223
224
 
224
225
  if hasattr(entity, "updated_at"):
225
- updated_at = getattr(entity, "updated_at")
226
+ updated_at = entity.updated_at
226
227
  if updated_at:
227
228
  log_data["updated_at"] = updated_at.isoformat()
@@ -14,13 +14,14 @@ All operations are still async to maintain interface compatibility.
14
14
  import hashlib
15
15
  import io
16
16
  import logging
17
- from typing import Optional, Dict, Any, List
17
+ from typing import Any
18
18
 
19
- from julee.domain.models.document import Document
20
19
  from julee.domain.models.custom_fields.content_stream import (
21
20
  ContentStream,
22
21
  )
22
+ from julee.domain.models.document import Document
23
23
  from julee.domain.repositories.document import DocumentRepository
24
+
24
25
  from .base import MemoryRepositoryMixin
25
26
 
26
27
  logger = logging.getLogger(__name__)
@@ -41,11 +42,11 @@ class MemoryDocumentRepository(DocumentRepository, MemoryRepositoryMixin[Documen
41
42
  """Initialize repository with empty in-memory storage."""
42
43
  self.logger = logger
43
44
  self.entity_name = "Document"
44
- self.storage_dict: Dict[str, Document] = {}
45
+ self.storage_dict: dict[str, Document] = {}
45
46
 
46
47
  logger.debug("Initializing MemoryDocumentRepository")
47
48
 
48
- async def get(self, document_id: str) -> Optional[Document]:
49
+ async def get(self, document_id: str) -> Document | None:
49
50
  """Retrieve a document with metadata and content.
50
51
 
51
52
  Args:
@@ -109,7 +110,7 @@ class MemoryDocumentRepository(DocumentRepository, MemoryRepositoryMixin[Documen
109
110
  """
110
111
  return self.generate_entity_id("doc")
111
112
 
112
- async def get_many(self, document_ids: List[str]) -> Dict[str, Optional[Document]]:
113
+ async def get_many(self, document_ids: list[str]) -> dict[str, Document | None]:
113
114
  """Retrieve multiple documents by ID.
114
115
 
115
116
  Args:
@@ -120,7 +121,7 @@ class MemoryDocumentRepository(DocumentRepository, MemoryRepositoryMixin[Documen
120
121
  """
121
122
  return self.get_many_entities(document_ids)
122
123
 
123
- async def list_all(self) -> List[Document]:
124
+ async def list_all(self) -> list[Document]:
124
125
  """List all documents.
125
126
 
126
127
  Returns:
@@ -142,7 +143,7 @@ class MemoryDocumentRepository(DocumentRepository, MemoryRepositoryMixin[Documen
142
143
  return documents
143
144
 
144
145
  def _add_entity_specific_log_data(
145
- self, entity: Document, log_data: Dict[str, Any]
146
+ self, entity: Document, log_data: dict[str, Any]
146
147
  ) -> None:
147
148
  """Add document-specific data to log entries."""
148
149
  super()._add_entity_specific_log_data(entity, log_data)
@@ -13,12 +13,13 @@ All operations are still async to maintain interface compatibility.
13
13
  """
14
14
 
15
15
  import logging
16
- from typing import Optional, Dict, Any, List
16
+ from typing import Any
17
17
 
18
18
  from julee.domain.models.policy import DocumentPolicyValidation
19
19
  from julee.domain.repositories.document_policy_validation import (
20
20
  DocumentPolicyValidationRepository,
21
21
  )
22
+
22
23
  from .base import MemoryRepositoryMixin
23
24
 
24
25
  logger = logging.getLogger(__name__)
@@ -41,11 +42,11 @@ class MemoryDocumentPolicyValidationRepository(
41
42
  """Initialize repository with empty in-memory storage."""
42
43
  self.logger = logger
43
44
  self.entity_name = "DocumentPolicyValidation"
44
- self.storage_dict: Dict[str, DocumentPolicyValidation] = {}
45
+ self.storage_dict: dict[str, DocumentPolicyValidation] = {}
45
46
 
46
47
  logger.debug("Initializing MemoryDocumentPolicyValidationRepository")
47
48
 
48
- async def get(self, validation_id: str) -> Optional[DocumentPolicyValidation]:
49
+ async def get(self, validation_id: str) -> DocumentPolicyValidation | None:
49
50
  """Retrieve a document policy validation by ID.
50
51
 
51
52
  Args:
@@ -73,8 +74,8 @@ class MemoryDocumentPolicyValidationRepository(
73
74
  return self.generate_entity_id("validation")
74
75
 
75
76
  async def get_many(
76
- self, validation_ids: List[str]
77
- ) -> Dict[str, Optional[DocumentPolicyValidation]]:
77
+ self, validation_ids: list[str]
78
+ ) -> dict[str, DocumentPolicyValidation | None]:
78
79
  """Retrieve multiple document policy validations by ID.
79
80
 
80
81
  Args:
@@ -87,7 +88,7 @@ class MemoryDocumentPolicyValidationRepository(
87
88
  return self.get_many_entities(validation_ids)
88
89
 
89
90
  def _add_entity_specific_log_data(
90
- self, entity: DocumentPolicyValidation, log_data: Dict[str, Any]
91
+ self, entity: DocumentPolicyValidation, log_data: dict[str, Any]
91
92
  ) -> None:
92
93
  """Add validation-specific data to log entries."""
93
94
  super()._add_entity_specific_log_data(entity, log_data)
@@ -14,7 +14,7 @@ interface compatibility.
14
14
  """
15
15
 
16
16
  import logging
17
- from typing import Optional, Dict, Any, List
17
+ from typing import Any
18
18
 
19
19
  from julee.domain.models.knowledge_service_config import (
20
20
  KnowledgeServiceConfig,
@@ -22,6 +22,7 @@ from julee.domain.models.knowledge_service_config import (
22
22
  from julee.domain.repositories.knowledge_service_config import (
23
23
  KnowledgeServiceConfigRepository,
24
24
  )
25
+
25
26
  from .base import MemoryRepositoryMixin
26
27
 
27
28
  logger = logging.getLogger(__name__)
@@ -36,6 +37,7 @@ class MemoryKnowledgeServiceConfigRepository(
36
37
  dictionaries.
37
38
 
38
39
  This implementation stores knowledge service configurations in memory:
40
+
39
41
  - Knowledge Services: Dictionary keyed by knowledge_service_id containing
40
42
  KnowledgeServiceConfig objects
41
43
 
@@ -47,11 +49,11 @@ class MemoryKnowledgeServiceConfigRepository(
47
49
  """Initialize repository with empty in-memory storage."""
48
50
  self.logger = logger
49
51
  self.entity_name = "KnowledgeServiceConfig"
50
- self.storage_dict: Dict[str, KnowledgeServiceConfig] = {}
52
+ self.storage_dict: dict[str, KnowledgeServiceConfig] = {}
51
53
 
52
54
  logger.debug("Initializing MemoryKnowledgeServiceConfigRepository")
53
55
 
54
- async def get(self, knowledge_service_id: str) -> Optional[KnowledgeServiceConfig]:
56
+ async def get(self, knowledge_service_id: str) -> KnowledgeServiceConfig | None:
55
57
  """Retrieve a knowledge service configuration by ID.
56
58
 
57
59
  Args:
@@ -79,8 +81,8 @@ class MemoryKnowledgeServiceConfigRepository(
79
81
  return self.generate_entity_id("ks")
80
82
 
81
83
  async def get_many(
82
- self, knowledge_service_ids: List[str]
83
- ) -> Dict[str, Optional[KnowledgeServiceConfig]]:
84
+ self, knowledge_service_ids: list[str]
85
+ ) -> dict[str, KnowledgeServiceConfig | None]:
84
86
  """Retrieve multiple knowledge service configs by ID.
85
87
 
86
88
  Args:
@@ -93,7 +95,7 @@ class MemoryKnowledgeServiceConfigRepository(
93
95
  """
94
96
  return self.get_many_entities(knowledge_service_ids)
95
97
 
96
- async def list_all(self) -> List[KnowledgeServiceConfig]:
98
+ async def list_all(self) -> list[KnowledgeServiceConfig]:
97
99
  """List all knowledge service configurations.
98
100
 
99
101
  Returns:
@@ -115,7 +117,7 @@ class MemoryKnowledgeServiceConfigRepository(
115
117
  return configs
116
118
 
117
119
  def _add_entity_specific_log_data(
118
- self, entity: KnowledgeServiceConfig, log_data: Dict[str, Any]
120
+ self, entity: KnowledgeServiceConfig, log_data: dict[str, Any]
119
121
  ) -> None:
120
122
  """Add knowledge service config-specific data to log entries."""
121
123
  super()._add_entity_specific_log_data(entity, log_data)
@@ -13,7 +13,7 @@ should be avoided.
13
13
  """
14
14
 
15
15
  import logging
16
- from typing import Dict, Optional, Any, List
16
+ from typing import Any
17
17
 
18
18
  from julee.domain.models.assembly_specification import (
19
19
  KnowledgeServiceQuery,
@@ -21,6 +21,7 @@ from julee.domain.models.assembly_specification import (
21
21
  from julee.domain.repositories.knowledge_service_query import (
22
22
  KnowledgeServiceQueryRepository,
23
23
  )
24
+
24
25
  from .base import MemoryRepositoryMixin
25
26
 
26
27
  logger = logging.getLogger(__name__)
@@ -35,6 +36,7 @@ class MemoryKnowledgeServiceQueryRepository(
35
36
  dictionaries.
36
37
 
37
38
  This implementation stores knowledge service queries in memory:
39
+
38
40
  - Queries: Dictionary keyed by query_id containing KnowledgeServiceQuery
39
41
  objects
40
42
 
@@ -46,11 +48,11 @@ class MemoryKnowledgeServiceQueryRepository(
46
48
  """Initialize repository with empty in-memory storage."""
47
49
  self.logger = logger
48
50
  self.entity_name = "KnowledgeServiceQuery"
49
- self.storage_dict: Dict[str, KnowledgeServiceQuery] = {}
51
+ self.storage_dict: dict[str, KnowledgeServiceQuery] = {}
50
52
 
51
53
  logger.debug("Initializing MemoryKnowledgeServiceQueryRepository")
52
54
 
53
- async def get(self, query_id: str) -> Optional[KnowledgeServiceQuery]:
55
+ async def get(self, query_id: str) -> KnowledgeServiceQuery | None:
54
56
  """Retrieve a knowledge service query by ID.
55
57
 
56
58
  Args:
@@ -70,8 +72,8 @@ class MemoryKnowledgeServiceQueryRepository(
70
72
  self.save_entity(query, "query_id")
71
73
 
72
74
  async def get_many(
73
- self, query_ids: List[str]
74
- ) -> Dict[str, Optional[KnowledgeServiceQuery]]:
75
+ self, query_ids: list[str]
76
+ ) -> dict[str, KnowledgeServiceQuery | None]:
75
77
  """Retrieve multiple knowledge service queries by ID.
76
78
 
77
79
  Args:
@@ -91,7 +93,7 @@ class MemoryKnowledgeServiceQueryRepository(
91
93
  """
92
94
  return self.generate_entity_id("query")
93
95
 
94
- async def list_all(self) -> List[KnowledgeServiceQuery]:
96
+ async def list_all(self) -> list[KnowledgeServiceQuery]:
95
97
  """List all knowledge service queries.
96
98
 
97
99
  Returns:
@@ -112,7 +114,7 @@ class MemoryKnowledgeServiceQueryRepository(
112
114
  return entities
113
115
 
114
116
  def _add_entity_specific_log_data(
115
- self, entity: KnowledgeServiceQuery, log_data: Dict[str, Any]
117
+ self, entity: KnowledgeServiceQuery, log_data: dict[str, Any]
116
118
  ) -> None:
117
119
  """Add knowledge service query-specific data to log entries."""
118
120
  super()._add_entity_specific_log_data(entity, log_data)