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.
- julee/api/app.py +9 -8
- julee/api/dependencies.py +15 -15
- julee/api/requests.py +10 -9
- julee/api/responses.py +2 -1
- julee/api/routers/__init__.py +5 -5
- julee/api/routers/assembly_specifications.py +5 -4
- julee/api/routers/documents.py +1 -1
- julee/api/routers/knowledge_service_configs.py +4 -3
- julee/api/routers/knowledge_service_queries.py +7 -6
- julee/api/routers/system.py +4 -3
- julee/api/routers/workflows.py +4 -5
- julee/api/services/system_initialization.py +6 -6
- julee/api/tests/routers/test_assembly_specifications.py +4 -3
- julee/api/tests/routers/test_documents.py +11 -10
- julee/api/tests/routers/test_knowledge_service_configs.py +7 -6
- julee/api/tests/routers/test_knowledge_service_queries.py +4 -3
- julee/api/tests/routers/test_system.py +5 -4
- julee/api/tests/routers/test_workflows.py +5 -4
- julee/api/tests/test_app.py +5 -4
- julee/api/tests/test_dependencies.py +3 -2
- julee/api/tests/test_requests.py +2 -1
- julee/contrib/__init__.py +15 -0
- julee/contrib/polling/__init__.py +47 -0
- julee/contrib/polling/domain/__init__.py +17 -0
- julee/contrib/polling/domain/models/__init__.py +13 -0
- julee/contrib/polling/domain/models/polling_config.py +39 -0
- julee/contrib/polling/domain/services/__init__.py +11 -0
- julee/contrib/polling/domain/services/poller.py +39 -0
- julee/contrib/polling/infrastructure/__init__.py +15 -0
- julee/contrib/polling/infrastructure/services/__init__.py +12 -0
- julee/contrib/polling/infrastructure/services/polling/__init__.py +12 -0
- julee/contrib/polling/infrastructure/services/polling/http/__init__.py +12 -0
- julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +80 -0
- julee/contrib/polling/infrastructure/temporal/__init__.py +20 -0
- julee/contrib/polling/infrastructure/temporal/activities.py +42 -0
- julee/contrib/polling/infrastructure/temporal/activity_names.py +20 -0
- julee/contrib/polling/infrastructure/temporal/proxies.py +45 -0
- julee/contrib/polling/tests/__init__.py +6 -0
- julee/contrib/polling/tests/unit/__init__.py +6 -0
- julee/contrib/polling/tests/unit/infrastructure/__init__.py +7 -0
- julee/contrib/polling/tests/unit/infrastructure/services/__init__.py +6 -0
- julee/contrib/polling/tests/unit/infrastructure/services/polling/__init__.py +6 -0
- julee/contrib/polling/tests/unit/infrastructure/services/polling/http/__init__.py +7 -0
- julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +163 -0
- julee/docs/__init__.py +5 -0
- julee/docs/sphinx_hcd/__init__.py +76 -0
- julee/docs/sphinx_hcd/accelerators.py +1175 -0
- julee/docs/sphinx_hcd/apps.py +518 -0
- julee/docs/sphinx_hcd/config.py +148 -0
- julee/docs/sphinx_hcd/epics.py +453 -0
- julee/docs/sphinx_hcd/integrations.py +310 -0
- julee/docs/sphinx_hcd/journeys.py +797 -0
- julee/docs/sphinx_hcd/personas.py +457 -0
- julee/docs/sphinx_hcd/stories.py +960 -0
- julee/docs/sphinx_hcd/utils.py +185 -0
- julee/domain/models/__init__.py +5 -6
- julee/domain/models/assembly/assembly.py +7 -7
- julee/domain/models/assembly/tests/factories.py +2 -1
- julee/domain/models/assembly/tests/test_assembly.py +16 -13
- julee/domain/models/assembly_specification/assembly_specification.py +11 -10
- julee/domain/models/assembly_specification/knowledge_service_query.py +7 -6
- julee/domain/models/assembly_specification/tests/factories.py +2 -1
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +9 -6
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +3 -1
- julee/domain/models/custom_fields/content_stream.py +3 -2
- julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -1
- julee/domain/models/document/document.py +23 -30
- julee/domain/models/document/tests/factories.py +3 -2
- julee/domain/models/document/tests/test_document.py +20 -37
- julee/domain/models/knowledge_service_config/knowledge_service_config.py +4 -4
- julee/domain/models/policy/__init__.py +4 -4
- julee/domain/models/policy/document_policy_validation.py +17 -17
- julee/domain/models/policy/policy.py +10 -10
- julee/domain/models/policy/tests/factories.py +2 -1
- julee/domain/models/policy/tests/test_document_policy_validation.py +3 -1
- julee/domain/models/policy/tests/test_policy.py +2 -1
- julee/domain/repositories/__init__.py +3 -3
- julee/domain/repositories/assembly.py +3 -1
- julee/domain/repositories/assembly_specification.py +2 -0
- julee/domain/repositories/base.py +5 -4
- julee/domain/repositories/document.py +3 -1
- julee/domain/repositories/document_policy_validation.py +3 -1
- julee/domain/repositories/knowledge_service_config.py +2 -0
- julee/domain/repositories/knowledge_service_query.py +1 -0
- julee/domain/repositories/policy.py +3 -1
- julee/domain/use_cases/decorators.py +3 -2
- julee/domain/use_cases/extract_assemble_data.py +14 -13
- julee/domain/use_cases/initialize_system_data.py +88 -34
- julee/domain/use_cases/tests/test_extract_assemble_data.py +10 -10
- julee/domain/use_cases/tests/test_initialize_system_data.py +2 -2
- julee/domain/use_cases/tests/test_validate_document.py +11 -11
- julee/domain/use_cases/validate_document.py +14 -14
- julee/fixtures/documents.yaml +4 -43
- julee/fixtures/knowledge_service_queries.yaml +9 -0
- julee/maintenance/__init__.py +1 -0
- julee/maintenance/release.py +243 -0
- julee/repositories/memory/assembly.py +6 -5
- julee/repositories/memory/assembly_specification.py +8 -9
- julee/repositories/memory/base.py +12 -11
- julee/repositories/memory/document.py +27 -20
- julee/repositories/memory/document_policy_validation.py +7 -6
- julee/repositories/memory/knowledge_service_config.py +8 -7
- julee/repositories/memory/knowledge_service_query.py +8 -7
- julee/repositories/memory/policy.py +6 -5
- julee/repositories/memory/tests/test_document.py +24 -22
- julee/repositories/memory/tests/test_document_policy_validation.py +2 -1
- julee/repositories/memory/tests/test_policy.py +2 -1
- julee/repositories/minio/assembly.py +4 -4
- julee/repositories/minio/assembly_specification.py +6 -8
- julee/repositories/minio/client.py +22 -25
- julee/repositories/minio/document.py +36 -33
- julee/repositories/minio/document_policy_validation.py +5 -5
- julee/repositories/minio/knowledge_service_config.py +6 -6
- julee/repositories/minio/knowledge_service_query.py +6 -9
- julee/repositories/minio/policy.py +4 -4
- julee/repositories/minio/tests/fake_client.py +11 -9
- julee/repositories/minio/tests/test_assembly.py +3 -1
- julee/repositories/minio/tests/test_assembly_specification.py +2 -1
- julee/repositories/minio/tests/test_client_protocol.py +5 -5
- julee/repositories/minio/tests/test_document.py +23 -22
- julee/repositories/minio/tests/test_document_policy_validation.py +3 -1
- julee/repositories/minio/tests/test_knowledge_service_config.py +4 -2
- julee/repositories/minio/tests/test_knowledge_service_query.py +3 -2
- julee/repositories/minio/tests/test_policy.py +3 -1
- julee/repositories/temporal/activities.py +5 -5
- julee/repositories/temporal/proxies.py +5 -5
- julee/services/knowledge_service/__init__.py +1 -2
- julee/services/knowledge_service/anthropic/knowledge_service.py +8 -7
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +11 -10
- julee/services/knowledge_service/factory.py +8 -8
- julee/services/knowledge_service/knowledge_service.py +12 -14
- julee/services/knowledge_service/memory/knowledge_service.py +13 -12
- julee/services/knowledge_service/memory/test_knowledge_service.py +10 -7
- julee/services/knowledge_service/test_factory.py +11 -10
- julee/services/temporal/activities.py +10 -10
- julee/services/temporal/proxies.py +2 -2
- julee/util/domain.py +6 -6
- julee/util/repos/minio/file_storage.py +8 -9
- julee/util/repos/temporal/client_proxies/file_storage.py +3 -4
- julee/util/repos/temporal/data_converter.py +6 -6
- julee/util/repos/temporal/minio_file_storage.py +1 -1
- julee/util/repos/temporal/proxies/file_storage.py +2 -3
- julee/util/repositories.py +4 -3
- julee/util/temporal/decorators.py +20 -18
- julee/util/tests/test_decorators.py +13 -15
- julee/util/validation/repository.py +3 -3
- julee/util/validation/type_guards.py +12 -11
- julee/worker.py +9 -8
- julee/workflows/__init__.py +2 -2
- julee/workflows/extract_assemble.py +2 -1
- julee/workflows/validate_document.py +3 -2
- {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/METADATA +3 -3
- julee-0.1.4.dist-info/RECORD +196 -0
- julee/fixtures/assembly_specifications.yaml +0 -70
- julee-0.1.2.dist-info/RECORD +0 -161
- {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/WHEEL +0 -0
- {julee-0.1.2.dist-info → julee-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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:
|
|
260
|
-
) ->
|
|
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
|
-
) ->
|
|
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:
|
|
355
|
-
queries:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
|
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) ->
|
|
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,
|
|
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:
|
|
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) ->
|
|
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,
|
|
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:
|
|
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) ->
|
|
537
|
+
def _load_fixture_assembly_specifications(self) -> list[dict[str, Any]]:
|
|
537
538
|
"""
|
|
538
|
-
Load assembly specifications from
|
|
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
|
-
|
|
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,
|
|
562
|
-
|
|
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:
|
|
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) ->
|
|
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,
|
|
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:
|
|
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
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
11
|
+
from unittest.mock import AsyncMock
|
|
14
12
|
|
|
15
|
-
|
|
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.
|
|
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
|
-
) ->
|
|
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,
|
|
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:
|
|
427
|
-
) ->
|
|
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:
|
|
468
|
-
queries:
|
|
469
|
-
) ->
|
|
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:
|
|
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:
|
|
555
|
-
required_scores:
|
|
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:
|
|
595
|
-
document_registrations:
|
|
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:
|
|
717
|
+
def _extract_transformed_content(self, result_data: dict) -> str:
|
|
718
718
|
"""
|
|
719
719
|
Extract transformed document content from knowledge service result.
|
|
720
720
|
|
julee/fixtures/documents.yaml
CHANGED
|
@@ -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."""
|