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
@@ -10,8 +10,9 @@ instances following the Clean Architecture principles.
10
10
  import hashlib
11
11
  import json
12
12
  import logging
13
+ from collections.abc import Callable
13
14
  from datetime import datetime, timezone
14
- from typing import Any, Callable, Dict
15
+ from typing import Any
15
16
 
16
17
  import jsonpointer # type: ignore
17
18
  import jsonschema
@@ -256,8 +257,8 @@ class ExtractAssembleDataUseCase:
256
257
  async def _register_document_with_services(
257
258
  self,
258
259
  document: Document,
259
- queries: Dict[str, KnowledgeServiceQuery],
260
- ) -> Dict[str, str]:
260
+ queries: dict[str, KnowledgeServiceQuery],
261
+ ) -> dict[str, str]:
261
262
  """
262
263
  Register the document with all knowledge services needed for assembly.
263
264
 
@@ -301,7 +302,7 @@ class ExtractAssembleDataUseCase:
301
302
  @try_use_case_step("queries_retrieval")
302
303
  async def _retrieve_all_queries(
303
304
  self, assembly_specification: AssemblySpecification
304
- ) -> Dict[str, KnowledgeServiceQuery]:
305
+ ) -> dict[str, KnowledgeServiceQuery]:
305
306
  """Retrieve all knowledge service queries needed for this assembly."""
306
307
  query_ids = list(assembly_specification.knowledge_service_queries.values())
307
308
 
@@ -351,8 +352,8 @@ class ExtractAssembleDataUseCase:
351
352
  self,
352
353
  document: Document,
353
354
  assembly_specification: AssemblySpecification,
354
- document_registrations: Dict[str, str],
355
- queries: Dict[str, KnowledgeServiceQuery],
355
+ document_registrations: dict[str, str],
356
+ queries: dict[str, KnowledgeServiceQuery],
356
357
  ) -> str:
357
358
  """
358
359
  Perform a single assembly iteration using knowledge services.
@@ -379,7 +380,7 @@ class ExtractAssembleDataUseCase:
379
380
 
380
381
  """
381
382
  # Initialize the result data structure
382
- assembled_data: Dict[str, Any] = {}
383
+ assembled_data: dict[str, Any] = {}
383
384
 
384
385
  # Process each knowledge service query
385
386
  # TODO: This is where we may want to fan-out/fan-in to do these
@@ -470,7 +471,7 @@ class ExtractAssembleDataUseCase:
470
471
  return document
471
472
 
472
473
  def _extract_schema_section(
473
- self, jsonschema: Dict[str, Any], schema_pointer: str
474
+ self, jsonschema: dict[str, Any], schema_pointer: str
474
475
  ) -> Any:
475
476
  """Extract relevant section of JSON schema using JSON Pointer."""
476
477
  if not schema_pointer:
@@ -495,7 +496,7 @@ Please structure your response according to this JSON schema:
495
496
  Return only valid JSON that conforms to this schema, without any surrounding
496
497
  text or markdown formatting."""
497
498
 
498
- def _parse_query_result(self, result_data: Dict[str, Any]) -> Any:
499
+ def _parse_query_result(self, result_data: dict[str, Any]) -> Any:
499
500
  """Parse the query result to extract the JSON response."""
500
501
  response_text = result_data.get("response", "")
501
502
  if not response_text:
@@ -514,7 +515,7 @@ text or markdown formatting."""
514
515
 
515
516
  def _store_result_in_assembled_data(
516
517
  self,
517
- assembled_data: Dict[str, Any],
518
+ assembled_data: dict[str, Any],
518
519
  schema_pointer: str,
519
520
  result_data: Any,
520
521
  ) -> None:
@@ -572,7 +573,7 @@ text or markdown formatting."""
572
573
  @try_use_case_step("assembled_document_creation")
573
574
  async def _create_assembled_document(
574
575
  self,
575
- assembled_data: Dict[str, Any],
576
+ assembled_data: dict[str, Any],
576
577
  assembly_specification: AssemblySpecification,
577
578
  ) -> str:
578
579
  """Create and store the assembled document."""
@@ -593,7 +594,7 @@ text or markdown formatting."""
593
594
  size_bytes=len(content_bytes),
594
595
  content_multihash=self._calculate_multihash_from_content(content_bytes),
595
596
  status=DocumentStatus.ASSEMBLED,
596
- content_string=assembled_content, # Use content_string for small
597
+ content_bytes=assembled_content,
597
598
  created_at=self.now_fn(),
598
599
  updated_at=self.now_fn(),
599
600
  )
@@ -605,7 +606,7 @@ text or markdown formatting."""
605
606
 
606
607
  def _validate_assembled_data(
607
608
  self,
608
- assembled_data: Dict[str, Any],
609
+ assembled_data: dict[str, Any],
609
610
  assembly_specification: AssemblySpecification,
610
611
  ) -> None:
611
612
  """Validate that the assembled data conforms to the JSON schema."""
@@ -13,10 +13,11 @@ The use case follows clean architecture principles:
13
13
  """
14
14
 
15
15
  import hashlib
16
+ import json
16
17
  import logging
17
18
  from datetime import datetime, timezone
18
19
  from pathlib import Path
19
- from typing import Any, Dict, List
20
+ from typing import Any
20
21
 
21
22
  import yaml
22
23
 
@@ -192,7 +193,7 @@ class InitializeSystemDataUseCase:
192
193
  )
193
194
  raise
194
195
 
195
- def _load_fixture_configurations(self) -> List[Dict[str, Any]]:
196
+ def _load_fixture_configurations(self) -> list[dict[str, Any]]:
196
197
  """
197
198
  Load knowledge service configurations from the YAML fixture file.
198
199
 
@@ -217,7 +218,7 @@ class InitializeSystemDataUseCase:
217
218
  )
218
219
 
219
220
  try:
220
- with open(fixture_path, "r", encoding="utf-8") as f:
221
+ with open(fixture_path, encoding="utf-8") as f:
221
222
  fixture_data = yaml.safe_load(f)
222
223
 
223
224
  if not fixture_data or "knowledge_services" not in fixture_data:
@@ -240,7 +241,7 @@ class InitializeSystemDataUseCase:
240
241
  raise yaml.YAMLError(f"Invalid YAML in fixture file: {e}")
241
242
 
242
243
  def _create_config_from_fixture_data(
243
- self, config_data: Dict[str, Any]
244
+ self, config_data: dict[str, Any]
244
245
  ) -> KnowledgeServiceConfig:
245
246
  """
246
247
  Create a KnowledgeServiceConfig from fixture data.
@@ -363,7 +364,7 @@ class InitializeSystemDataUseCase:
363
364
  )
364
365
  raise
365
366
 
366
- def _load_fixture_queries(self) -> List[Dict[str, Any]]:
367
+ def _load_fixture_queries(self) -> list[dict[str, Any]]:
367
368
  """
368
369
  Load knowledge service queries from the YAML fixture file.
369
370
 
@@ -388,7 +389,7 @@ class InitializeSystemDataUseCase:
388
389
  )
389
390
 
390
391
  try:
391
- with open(fixture_path, "r", encoding="utf-8") as f:
392
+ with open(fixture_path, encoding="utf-8") as f:
392
393
  fixture_data = yaml.safe_load(f)
393
394
 
394
395
  if not fixture_data or "knowledge_service_queries" not in fixture_data:
@@ -413,7 +414,7 @@ class InitializeSystemDataUseCase:
413
414
  raise yaml.YAMLError(f"Invalid YAML in queries fixture file: {e}")
414
415
 
415
416
  def _create_query_from_fixture_data(
416
- self, query_data: Dict[str, Any]
417
+ self, query_data: dict[str, Any]
417
418
  ) -> KnowledgeServiceQuery:
418
419
  """
419
420
  Create a KnowledgeServiceQuery from fixture data.
@@ -533,9 +534,9 @@ class InitializeSystemDataUseCase:
533
534
  )
534
535
  raise
535
536
 
536
- def _load_fixture_assembly_specifications(self) -> List[Dict[str, Any]]:
537
+ def _load_fixture_assembly_specifications(self) -> list[dict[str, Any]]:
537
538
  """
538
- Load assembly specifications from the YAML fixture file.
539
+ Load assembly specifications from a YAML or JSON fixture file.
539
540
 
540
541
  Returns:
541
542
  List of specification dictionaries from the fixture file
@@ -543,23 +544,34 @@ class InitializeSystemDataUseCase:
543
544
  Raises:
544
545
  FileNotFoundError: If the fixture file doesn't exist
545
546
  yaml.YAMLError: If the fixture file is invalid YAML
547
+ json.JSONDecodeError: If the fixture file is invalid JSON
546
548
  KeyError: If required fields are missing from the fixture
549
+ ValueError: If the specification section is malformed
547
550
  """
548
- fixture_path = self._get_demo_fixture_path("assembly_specifications.yaml")
551
+ # Accept both .yaml and .json files
552
+ fixture_path = None
553
+ for ext in ("json", "yaml"):
554
+ candidate = self._get_demo_fixture_path(f"assembly_specifications.{ext}")
555
+ if candidate.exists():
556
+ fixture_path = candidate
557
+ break
558
+
559
+ if fixture_path is None:
560
+ raise FileNotFoundError(
561
+ "Assembly specifications fixture file not found (.yaml or .json)"
562
+ )
549
563
 
550
564
  self.logger.debug(
551
565
  "Loading assembly specifications fixture file",
552
566
  extra={"fixture_path": str(fixture_path)},
553
567
  )
554
568
 
555
- if not fixture_path.exists():
556
- raise FileNotFoundError(
557
- f"Assembly specifications fixture file not found: {fixture_path}"
558
- )
559
-
560
569
  try:
561
- with open(fixture_path, "r", encoding="utf-8") as f:
562
- fixture_data = yaml.safe_load(f)
570
+ with open(fixture_path, encoding="utf-8") as f:
571
+ if fixture_path.suffix.lower() == ".json":
572
+ fixture_data = json.load(f)
573
+ else:
574
+ fixture_data = yaml.safe_load(f)
563
575
 
564
576
  if not fixture_data or "assembly_specifications" not in fixture_data:
565
577
  raise KeyError(
@@ -569,8 +581,7 @@ class InitializeSystemDataUseCase:
569
581
  specs = fixture_data["assembly_specifications"]
570
582
  if not isinstance(specs, list):
571
583
  raise ValueError(
572
- "'assembly_specifications' must be a list of "
573
- "specification configurations"
584
+ "'assembly_specifications' must be a list of specification configurations"
574
585
  )
575
586
 
576
587
  self.logger.debug(
@@ -585,8 +596,15 @@ class InitializeSystemDataUseCase:
585
596
  f"Invalid YAML in assembly specifications fixture file: {e}"
586
597
  )
587
598
 
599
+ except json.JSONDecodeError as e:
600
+ raise json.JSONDecodeError(
601
+ f"Invalid JSON in assembly specifications fixture file: {e}",
602
+ e.doc,
603
+ e.pos,
604
+ )
605
+
588
606
  def _create_assembly_spec_from_fixture_data(
589
- self, spec_data: Dict[str, Any]
607
+ self, spec_data: dict[str, Any]
590
608
  ) -> AssemblySpecification:
591
609
  """
592
610
  Create an AssemblySpecification from fixture data.
@@ -719,7 +737,7 @@ class InitializeSystemDataUseCase:
719
737
  )
720
738
  raise
721
739
 
722
- def _load_fixture_documents(self) -> List[Dict[str, Any]]:
740
+ def _load_fixture_documents(self) -> list[dict[str, Any]]:
723
741
  """
724
742
  Load documents from the YAML fixture file.
725
743
 
@@ -742,7 +760,7 @@ class InitializeSystemDataUseCase:
742
760
  raise FileNotFoundError(f"Documents fixture file not found: {fixture_path}")
743
761
 
744
762
  try:
745
- with open(fixture_path, "r", encoding="utf-8") as f:
763
+ with open(fixture_path, encoding="utf-8") as f:
746
764
  fixture_data = yaml.safe_load(f)
747
765
 
748
766
  if not fixture_data or "documents" not in fixture_data:
@@ -764,7 +782,7 @@ class InitializeSystemDataUseCase:
764
782
  except yaml.YAMLError as e:
765
783
  raise yaml.YAMLError(f"Invalid YAML in documents fixture file: {e}")
766
784
 
767
- def _create_document_from_fixture_data(self, doc_data: Dict[str, Any]) -> Document:
785
+ def _create_document_from_fixture_data(self, doc_data: dict[str, Any]) -> Document:
768
786
  """
769
787
  Create a Document from fixture data.
770
788
 
@@ -782,24 +800,62 @@ class InitializeSystemDataUseCase:
782
800
  "document_id",
783
801
  "original_filename",
784
802
  "content_type",
785
- "content",
786
803
  ]
787
804
 
788
- # Validate required fields
789
805
  for field in required_fields:
790
806
  if field not in doc_data:
791
807
  raise KeyError(f"Required field '{field}' missing from document")
792
808
 
793
- # Get content and calculate hash
794
- content = doc_data["content"]
795
- content_bytes = content.encode("utf-8")
796
- size_bytes = len(content_bytes)
809
+ content_type = doc_data["content_type"]
810
+ is_text = content_type.startswith("text/") or content_type in {
811
+ "application/json",
812
+ "application/xml",
813
+ "application/javascript",
814
+ }
815
+
816
+ if "content" in doc_data:
817
+ content = doc_data["content"]
818
+
819
+ if isinstance(content, bytes):
820
+ content_bytes = content
821
+ elif isinstance(content, str):
822
+ content_bytes = content.encode("utf-8")
823
+ else:
824
+ raise TypeError(
825
+ f"Unsupported type for 'content': {type(content)!r}. Expected str or bytes."
826
+ )
827
+ else:
828
+ current_file = Path(__file__)
829
+ julee_dir = current_file.parent.parent.parent
830
+ fixture_path = julee_dir / "fixtures" / doc_data["original_filename"]
797
831
 
798
- # Create multihash (using SHA-256)
832
+ open_mode = "r" if is_text else "rb"
833
+ encoding = "utf-8" if is_text else None
834
+
835
+ try:
836
+ with fixture_path.open(open_mode, encoding=encoding) as f:
837
+ content = f.read()
838
+ except FileNotFoundError as e:
839
+ self.logger.error(
840
+ "Fixture file not found for document",
841
+ extra={
842
+ "document_id": doc_data["document_id"],
843
+ "fixture_path": str(fixture_path),
844
+ },
845
+ )
846
+ raise FileNotFoundError(
847
+ f"Fixture file '{fixture_path}' not found for document "
848
+ f"{doc_data['document_id']}"
849
+ ) from e
850
+
851
+ content_bytes = content.encode("utf-8") if is_text else content
852
+
853
+ self.logger.info(content_bytes)
854
+
855
+ size_bytes = len(content_bytes)
799
856
  sha256_hash = hashlib.sha256(content_bytes).hexdigest()
800
857
  content_multihash = f"sha256-{sha256_hash}"
801
858
 
802
- # Parse status
803
859
  status = DocumentStatus.CAPTURED
804
860
  if "status" in doc_data:
805
861
  try:
@@ -809,12 +865,10 @@ class InitializeSystemDataUseCase:
809
865
  f"Invalid status '{doc_data['status']}', using default 'captured'"
810
866
  )
811
867
 
812
- # Get optional fields
813
868
  knowledge_service_id = doc_data.get("knowledge_service_id")
814
869
  assembly_types = doc_data.get("assembly_types", [])
815
870
  additional_metadata = doc_data.get("additional_metadata", {})
816
871
 
817
- # Create document
818
872
  document = Document(
819
873
  document_id=doc_data["document_id"],
820
874
  original_filename=doc_data["original_filename"],
@@ -827,7 +881,7 @@ class InitializeSystemDataUseCase:
827
881
  created_at=datetime.now(timezone.utc),
828
882
  updated_at=datetime.now(timezone.utc),
829
883
  additional_metadata=additional_metadata,
830
- content_string=content, # Store content as string for fixtures
884
+ content_bytes=content_bytes,
831
885
  )
832
886
 
833
887
  self.logger.debug(
@@ -8,35 +8,35 @@ following the Clean Architecture principles.
8
8
 
9
9
  import io
10
10
  import json
11
- import pytest
12
-
13
- from unittest.mock import AsyncMock
14
11
  from datetime import datetime, timezone
12
+ from unittest.mock import AsyncMock
13
+
14
+ import pytest
15
15
 
16
- from julee.domain.use_cases import ExtractAssembleDataUseCase
17
16
  from julee.domain.models import (
18
17
  Assembly,
18
+ AssemblySpecification,
19
+ AssemblySpecificationStatus,
19
20
  AssemblyStatus,
21
+ ContentStream,
20
22
  Document,
21
23
  DocumentStatus,
22
- ContentStream,
23
- AssemblySpecification,
24
- AssemblySpecificationStatus,
25
- KnowledgeServiceQuery,
26
24
  KnowledgeServiceConfig,
25
+ KnowledgeServiceQuery,
27
26
  )
28
27
  from julee.domain.models.knowledge_service_config import ServiceApi
28
+ from julee.domain.use_cases import ExtractAssembleDataUseCase
29
29
  from julee.repositories.memory import (
30
- MemoryDocumentRepository,
31
30
  MemoryAssemblyRepository,
32
31
  MemoryAssemblySpecificationRepository,
32
+ MemoryDocumentRepository,
33
33
  MemoryKnowledgeServiceConfigRepository,
34
34
  MemoryKnowledgeServiceQueryRepository,
35
35
  )
36
+ from julee.services.knowledge_service import QueryResult
36
37
  from julee.services.knowledge_service.memory import (
37
38
  MemoryKnowledgeService,
38
39
  )
39
- from julee.services.knowledge_service import QueryResult
40
40
 
41
41
 
42
42
  class TestExtractAssembleDataUseCase:
@@ -86,7 +86,7 @@ def fixture_configs() -> list[dict]:
86
86
 
87
87
  assert fixture_path.exists(), f"Fixture file not found: {fixture_path}"
88
88
 
89
- with open(fixture_path, "r", encoding="utf-8") as f:
89
+ with open(fixture_path, encoding="utf-8") as f:
90
90
  fixture_data = yaml.safe_load(f)
91
91
 
92
92
  assert "knowledge_services" in fixture_data
@@ -320,7 +320,7 @@ class TestYamlFixtureIntegration:
320
320
  assert fixture_path.exists(), f"Fixture file not found: {fixture_path}"
321
321
 
322
322
  # Verify file can be parsed
323
- with open(fixture_path, "r", encoding="utf-8") as f:
323
+ with open(fixture_path, encoding="utf-8") as f:
324
324
  fixture_data = yaml.safe_load(f)
325
325
 
326
326
  # Verify structure
@@ -7,38 +7,38 @@ following the Clean Architecture principles.
7
7
  """
8
8
 
9
9
  import io
10
- import pytest
11
- from unittest.mock import AsyncMock
12
10
  from datetime import datetime, timezone
13
- from pydantic import ValidationError
11
+ from unittest.mock import AsyncMock
14
12
 
15
- from julee.domain.use_cases import ValidateDocumentUseCase
13
+ import pytest
14
+ from pydantic import ValidationError
16
15
 
17
16
  from julee.domain.models import (
17
+ ContentStream,
18
18
  Document,
19
19
  DocumentStatus,
20
- ContentStream,
21
20
  KnowledgeServiceConfig,
22
21
  KnowledgeServiceQuery,
23
22
  )
23
+ from julee.domain.models.knowledge_service_config import ServiceApi
24
24
  from julee.domain.models.policy import (
25
- Policy,
26
- PolicyStatus,
27
25
  DocumentPolicyValidation,
28
26
  DocumentPolicyValidationStatus,
27
+ Policy,
28
+ PolicyStatus,
29
29
  )
30
- from julee.domain.models.knowledge_service_config import ServiceApi
30
+ from julee.domain.use_cases import ValidateDocumentUseCase
31
31
  from julee.repositories.memory import (
32
+ MemoryDocumentPolicyValidationRepository,
32
33
  MemoryDocumentRepository,
33
- MemoryKnowledgeServiceQueryRepository,
34
34
  MemoryKnowledgeServiceConfigRepository,
35
+ MemoryKnowledgeServiceQueryRepository,
35
36
  MemoryPolicyRepository,
36
- MemoryDocumentPolicyValidationRepository,
37
37
  )
38
+ from julee.services.knowledge_service import QueryResult
38
39
  from julee.services.knowledge_service.memory import (
39
40
  MemoryKnowledgeService,
40
41
  )
41
- from julee.services.knowledge_service import QueryResult
42
42
 
43
43
 
44
44
  class TestValidateDocumentUseCase:
@@ -11,8 +11,8 @@ import hashlib
11
11
  import io
12
12
  import json
13
13
  import logging
14
+ from collections.abc import Callable
14
15
  from datetime import datetime
15
- from typing import Callable, Dict, List, Tuple
16
16
 
17
17
  import multihash
18
18
 
@@ -397,13 +397,13 @@ class ValidateDocumentUseCase:
397
397
  @try_use_case_step("all_queries_retrieval")
398
398
  async def _retrieve_all_queries(
399
399
  self, policy: Policy
400
- ) -> Dict[str, KnowledgeServiceQuery]:
400
+ ) -> dict[str, KnowledgeServiceQuery]:
401
401
  """Retrieve all knowledge service queries needed for validation and
402
402
  transformation."""
403
403
  all_queries = {}
404
404
 
405
405
  # Get validation queries
406
- for query_id, required_score in policy.validation_scores:
406
+ for query_id, _required_score in policy.validation_scores:
407
407
  query = await self.knowledge_service_query_repo.get(query_id)
408
408
  if not query:
409
409
  raise ValueError(f"Validation query not found: {query_id}")
@@ -423,8 +423,8 @@ class ValidateDocumentUseCase:
423
423
  async def _register_document_with_services(
424
424
  self,
425
425
  document: Document,
426
- queries: Dict[str, KnowledgeServiceQuery],
427
- ) -> Dict[str, str]:
426
+ queries: dict[str, KnowledgeServiceQuery],
427
+ ) -> dict[str, str]:
428
428
  """
429
429
  Register the document with all knowledge services needed for
430
430
  validation.
@@ -464,9 +464,9 @@ class ValidateDocumentUseCase:
464
464
  self,
465
465
  document: Document,
466
466
  policy: Policy,
467
- document_registrations: Dict[str, str],
468
- queries: Dict[str, KnowledgeServiceQuery],
469
- ) -> List[Tuple[str, int]]:
467
+ document_registrations: dict[str, str],
468
+ queries: dict[str, KnowledgeServiceQuery],
469
+ ) -> list[tuple[str, int]]:
470
470
  """
471
471
  Execute all validation queries and return the actual scores achieved.
472
472
 
@@ -528,7 +528,7 @@ class ValidateDocumentUseCase:
528
528
 
529
529
  return validation_scores
530
530
 
531
- def _extract_score_from_result(self, result_data: Dict) -> int:
531
+ def _extract_score_from_result(self, result_data: dict) -> int:
532
532
  """
533
533
  Extract a numeric score from the knowledge service query result.
534
534
 
@@ -551,8 +551,8 @@ class ValidateDocumentUseCase:
551
551
 
552
552
  def _determine_validation_result(
553
553
  self,
554
- actual_scores: List[Tuple[str, int]],
555
- required_scores: List[Tuple[str, int]],
554
+ actual_scores: list[tuple[str, int]],
555
+ required_scores: list[tuple[str, int]],
556
556
  ) -> bool:
557
557
  """
558
558
  Determine if validation passed based on actual vs required scores.
@@ -591,8 +591,8 @@ class ValidateDocumentUseCase:
591
591
  self,
592
592
  document: Document,
593
593
  policy: Policy,
594
- all_queries: Dict[str, KnowledgeServiceQuery],
595
- document_registrations: Dict[str, str],
594
+ all_queries: dict[str, KnowledgeServiceQuery],
595
+ document_registrations: dict[str, str],
596
596
  ) -> Document:
597
597
  """
598
598
  Apply transformation queries to a document and return the
@@ -714,7 +714,7 @@ class ValidateDocumentUseCase:
714
714
 
715
715
  return transformed_document
716
716
 
717
- def _extract_transformed_content(self, result_data: Dict) -> str:
717
+ def _extract_transformed_content(self, result_data: dict) -> str:
718
718
  """
719
719
  Extract transformed document content from knowledge service result.
720
720
 
@@ -9,6 +9,10 @@
9
9
  # repository if they don't already exist (idempotent loading).
10
10
 
11
11
  documents:
12
+ - document_id: "product-spec-sheet"
13
+ original_filename: "Spec-Sheet-BondorPanel-v17.pdf"
14
+ content_type: "application/pdf"
15
+ status: "captured"
12
16
  - document_id: "meeting-transcript-q1-planning"
13
17
  original_filename: "q1_planning_meeting.txt"
14
18
  content_type: "text/plain"
@@ -19,49 +23,6 @@ documents:
19
23
  attendee_count: 3
20
24
  duration_minutes: 90
21
25
  department: "product"
22
- content: |
23
- Meeting Transcript - Q1 Planning Session
24
- Date: March 15, 2024
25
- Time: 2:00 PM - 3:30 PM
26
- Attendees: Sarah Chen (Product Manager), Mike Rodriguez (Engineering Lead),
27
- Lisa Wang (Designer)
28
-
29
- Sarah: Thanks everyone for joining. Let's kick off our Q1 planning. Mike,
30
- can you give us an update on the current sprint?
31
-
32
- Mike: Sure, we're about 80% through sprint 23. We've completed the user
33
- authentication module and are working on the data migration tool. Should be
34
- done by Friday.
35
-
36
- Lisa: Great! I've finished the mockups for the dashboard redesign. Sarah,
37
- have you had a chance to review them?
38
-
39
- Sarah: Yes, they look fantastic. I especially like the new navigation
40
- structure. When can we start implementation?
41
-
42
- Mike: I'd estimate 2 weeks for the frontend work, plus another week for
43
- backend API changes.
44
-
45
- Lisa: I can start on the component library updates while Mike works on the
46
- APIs.
47
-
48
- Sarah: Perfect. Let's also discuss the customer feedback integration. We had
49
- 47 responses to our survey.
50
-
51
- Mike: The main requests were for better reporting and mobile optimization.
52
-
53
- Sarah: Those should be our next priorities then. Lisa, can you start
54
- sketching mobile designs?
55
-
56
- Lisa: Absolutely. I'll have initial concepts by next Tuesday.
57
-
58
- Sarah: Excellent. Any other items?
59
-
60
- Mike: Just a heads up that we'll need to schedule downtime for the database
61
- migration, probably next weekend.
62
-
63
- Sarah: Noted. I'll coordinate with support. Meeting adjourned at 3:30 PM.
64
-
65
26
  - document_id: "customer-feedback-survey-q4"
66
27
  original_filename: "customer_survey_results_q4_2023.txt"
67
28
  content_type: "text/plain"
@@ -1,4 +1,13 @@
1
1
  knowledge_service_queries:
2
+ - query_id: "generate-dpp"
3
+ name: "Generate Digital Product Passport"
4
+ knowledge_service_id: "anthropic-4.5-as-a-knowledge-service"
5
+ prompt: "From this product specification sheet, extract the product information to generate a Digital Product Passport, that conforms to the provided schema, including the issuer, the credential subject and the validation dates. Please make sure that the DPP conforms to the provided schema and types and that you don't add any other fields."
6
+ assistant_prompt: "Looking at the product specification sheet, here's the digital product passport that conforms to the provided schema, without surrounding ```json ... ``` markers:"
7
+ query_metadata:
8
+ max_tokens: 3000
9
+ temperature: 0.1
10
+
2
11
  - query_id: "extract-meeting-info-query"
3
12
  name: "Extract Meeting Information"
4
13
  knowledge_service_id: "anthropic-4.5-as-a-knowledge-service"
@@ -0,0 +1 @@
1
+ """Maintenance utilities for julee and julee-based solutions."""