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.
- 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 +5 -4
- 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 +82 -0
- julee/docs/sphinx_hcd/accelerators.py +1078 -0
- julee/docs/sphinx_hcd/apps.py +499 -0
- julee/docs/sphinx_hcd/config.py +148 -0
- julee/docs/sphinx_hcd/epics.py +448 -0
- julee/docs/sphinx_hcd/integrations.py +306 -0
- julee/docs/sphinx_hcd/journeys.py +783 -0
- julee/docs/sphinx_hcd/personas.py +435 -0
- julee/docs/sphinx_hcd/stories.py +932 -0
- julee/docs/sphinx_hcd/utils.py +180 -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 +14 -9
- 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 +12 -10
- julee/domain/models/document/tests/factories.py +3 -2
- julee/domain/models/document/tests/test_document.py +6 -3
- 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 +12 -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 +33 -16
- 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 +23 -13
- julee/domain/use_cases/initialize_system_data.py +13 -13
- 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 +26 -15
- julee/maintenance/__init__.py +1 -0
- julee/maintenance/release.py +188 -0
- julee/repositories/__init__.py +4 -1
- julee/repositories/memory/assembly.py +6 -5
- julee/repositories/memory/assembly_specification.py +9 -9
- julee/repositories/memory/base.py +12 -11
- julee/repositories/memory/document.py +8 -7
- julee/repositories/memory/document_policy_validation.py +7 -6
- julee/repositories/memory/knowledge_service_config.py +9 -7
- julee/repositories/memory/knowledge_service_query.py +9 -7
- julee/repositories/memory/policy.py +6 -5
- julee/repositories/memory/tests/test_document.py +6 -4
- 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 +11 -11
- julee/repositories/minio/document_policy_validation.py +5 -5
- julee/repositories/minio/knowledge_service_config.py +8 -8
- 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 +7 -6
- 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 +22 -18
- 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 +6 -7
- julee/util/temporal/activities.py +1 -1
- julee/util/temporal/decorators.py +28 -33
- 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.1.dist-info → julee-0.1.3.dist-info}/METADATA +4 -1
- julee-0.1.3.dist-info/RECORD +197 -0
- julee-0.1.1.dist-info/RECORD +0 -161
- {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/WHEEL +0 -0
- {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Shared utilities for sphinx_hcd extension.
|
|
2
|
+
|
|
3
|
+
Common functions used across multiple extension modules.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
from docutils import nodes
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def normalize_name(name: str) -> str:
|
|
11
|
+
"""Normalize a name for comparison (lowercase, hyphens to spaces).
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
name: Name to normalize
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Normalized lowercase name with consistent spacing
|
|
18
|
+
"""
|
|
19
|
+
return name.lower().replace("-", " ").replace("_", " ").strip()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def slugify(text: str) -> str:
|
|
23
|
+
"""Create a URL-safe slug from text.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
text: Text to slugify
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
URL-safe slug string
|
|
30
|
+
"""
|
|
31
|
+
slug = text.lower()
|
|
32
|
+
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
|
|
33
|
+
slug = re.sub(r'[\s_]+', '-', slug)
|
|
34
|
+
slug = re.sub(r'-+', '-', slug)
|
|
35
|
+
return slug.strip('-')
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def kebab_to_snake(name: str) -> str:
|
|
39
|
+
"""Convert kebab-case to snake_case for Python module names.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
name: Kebab-case name (e.g., 'audit-analysis')
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Snake_case name (e.g., 'audit_analysis')
|
|
46
|
+
"""
|
|
47
|
+
return name.replace("-", "_")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def parse_list_option(value: str) -> list[str]:
|
|
51
|
+
"""Parse a newline-separated list option with optional bullet prefixes.
|
|
52
|
+
|
|
53
|
+
Handles RST-style lists like:
|
|
54
|
+
- First item
|
|
55
|
+
- Second item with (commas, inside)
|
|
56
|
+
|
|
57
|
+
Does NOT split on commas to preserve items containing parenthetical lists.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
value: Raw option string
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
List of stripped item strings
|
|
64
|
+
"""
|
|
65
|
+
if not value:
|
|
66
|
+
return []
|
|
67
|
+
items = []
|
|
68
|
+
for line in value.strip().split('\n'):
|
|
69
|
+
item = line.strip().lstrip('- ')
|
|
70
|
+
if item:
|
|
71
|
+
items.append(item)
|
|
72
|
+
return items
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def parse_csv_option(value: str) -> list[str]:
|
|
76
|
+
"""Parse a comma-separated list option.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
value: Raw option string
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of stripped item strings
|
|
83
|
+
"""
|
|
84
|
+
if not value:
|
|
85
|
+
return []
|
|
86
|
+
return [item.strip() for item in value.split(',') if item.strip()]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def parse_integration_options(value: str) -> list[dict]:
|
|
90
|
+
"""Parse integration options with optional descriptions.
|
|
91
|
+
|
|
92
|
+
Supports format: integration-slug (description of data)
|
|
93
|
+
Example: pilot-data-collection (CMA documents, audit reports)
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
value: Raw option string
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
List of dicts with 'slug' and 'description' keys
|
|
100
|
+
"""
|
|
101
|
+
if not value:
|
|
102
|
+
return []
|
|
103
|
+
|
|
104
|
+
items = []
|
|
105
|
+
for line in value.strip().split('\n'):
|
|
106
|
+
line = line.strip().lstrip('- ')
|
|
107
|
+
if not line:
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
# Parse: slug (description) or just slug
|
|
111
|
+
match = re.match(r'^([a-z0-9-]+)\s*(?:\(([^)]+)\))?$', line.strip())
|
|
112
|
+
if match:
|
|
113
|
+
items.append({
|
|
114
|
+
'slug': match.group(1),
|
|
115
|
+
'description': match.group(2).strip() if match.group(2) else None,
|
|
116
|
+
})
|
|
117
|
+
else:
|
|
118
|
+
# Fallback: treat whole line as slug
|
|
119
|
+
items.append({
|
|
120
|
+
'slug': line.strip(),
|
|
121
|
+
'description': None,
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
return items
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def path_to_root(docname: str) -> str:
|
|
128
|
+
"""Calculate relative path from a document to the docs root.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
docname: Document name (e.g., 'users/journeys/build-vocabulary')
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Relative path prefix (e.g., '../../')
|
|
135
|
+
"""
|
|
136
|
+
depth = docname.count("/")
|
|
137
|
+
return "../" * depth
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def make_reference(uri: str, text: str, is_code: bool = False) -> nodes.reference:
|
|
141
|
+
"""Create a reference node.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
uri: Target URI
|
|
145
|
+
text: Link text
|
|
146
|
+
is_code: If True, render text as code/literal
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
docutils reference node
|
|
150
|
+
"""
|
|
151
|
+
ref = nodes.reference("", "", refuri=uri)
|
|
152
|
+
if is_code:
|
|
153
|
+
ref += nodes.literal(text=text)
|
|
154
|
+
else:
|
|
155
|
+
ref += nodes.Text(text)
|
|
156
|
+
return ref
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def make_internal_link(
|
|
160
|
+
docname: str,
|
|
161
|
+
target_doc: str,
|
|
162
|
+
text: str,
|
|
163
|
+
anchor: str | None = None,
|
|
164
|
+
) -> nodes.reference:
|
|
165
|
+
"""Create an internal document link with proper relative path.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
docname: Current document name
|
|
169
|
+
target_doc: Target document path (e.g., 'applications/staff-portal')
|
|
170
|
+
text: Link text
|
|
171
|
+
anchor: Optional anchor within target page
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
docutils reference node
|
|
175
|
+
"""
|
|
176
|
+
prefix = path_to_root(docname)
|
|
177
|
+
uri = f"{prefix}{target_doc}.html"
|
|
178
|
+
if anchor:
|
|
179
|
+
uri = f"{uri}#{anchor}"
|
|
180
|
+
return make_reference(uri, text)
|
julee/domain/models/__init__.py
CHANGED
|
@@ -10,11 +10,6 @@ Re-exports commonly used models for convenient importing:
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
# Document models
|
|
13
|
-
from .document import Document, DocumentStatus
|
|
14
|
-
|
|
15
|
-
# Custom field types
|
|
16
|
-
from .custom_fields.content_stream import ContentStream
|
|
17
|
-
|
|
18
13
|
# Assembly models
|
|
19
14
|
from .assembly import Assembly, AssemblyStatus
|
|
20
15
|
from .assembly_specification import (
|
|
@@ -23,11 +18,15 @@ from .assembly_specification import (
|
|
|
23
18
|
KnowledgeServiceQuery,
|
|
24
19
|
)
|
|
25
20
|
|
|
21
|
+
# Custom field types
|
|
22
|
+
from .custom_fields.content_stream import ContentStream
|
|
23
|
+
from .document import Document, DocumentStatus
|
|
24
|
+
|
|
26
25
|
# Configuration models
|
|
27
26
|
from .knowledge_service_config import KnowledgeServiceConfig
|
|
28
27
|
|
|
29
28
|
# Policy models
|
|
30
|
-
from .policy import Policy, PolicyStatus
|
|
29
|
+
from .policy import DocumentPolicyValidation, Policy, PolicyStatus
|
|
31
30
|
|
|
32
31
|
__all__ = [
|
|
33
32
|
# Document models
|
|
@@ -12,11 +12,11 @@ All domain models use Pydantic BaseModel for validation, serialization,
|
|
|
12
12
|
and type safety, following the patterns established in the sample project.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from pydantic import BaseModel, Field, field_validator
|
|
16
|
-
from typing import Optional
|
|
17
15
|
from datetime import datetime, timezone
|
|
18
16
|
from enum import Enum
|
|
19
17
|
|
|
18
|
+
from pydantic import BaseModel, Field, field_validator
|
|
19
|
+
|
|
20
20
|
|
|
21
21
|
class AssemblyStatus(str, Enum):
|
|
22
22
|
"""Status of an assembly process."""
|
|
@@ -52,16 +52,16 @@ class Assembly(BaseModel):
|
|
|
52
52
|
|
|
53
53
|
# Assembly process tracking
|
|
54
54
|
status: AssemblyStatus = AssemblyStatus.PENDING
|
|
55
|
-
assembled_document_id:
|
|
55
|
+
assembled_document_id: str | None = Field(
|
|
56
56
|
default=None,
|
|
57
57
|
description="ID of the assembled document produced by this assembly",
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
# Assembly metadata
|
|
61
|
-
created_at:
|
|
61
|
+
created_at: datetime | None = Field(
|
|
62
62
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
63
63
|
)
|
|
64
|
-
updated_at:
|
|
64
|
+
updated_at: datetime | None = Field(
|
|
65
65
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
66
66
|
)
|
|
67
67
|
|
|
@@ -89,8 +89,8 @@ class Assembly(BaseModel):
|
|
|
89
89
|
@field_validator("assembled_document_id")
|
|
90
90
|
@classmethod
|
|
91
91
|
def assembled_document_id_must_not_be_empty_if_provided(
|
|
92
|
-
cls, v:
|
|
93
|
-
) ->
|
|
92
|
+
cls, v: str | None
|
|
93
|
+
) -> str | None:
|
|
94
94
|
if v is not None and (not v or not v.strip()):
|
|
95
95
|
raise ValueError("Assembled document ID cannot be empty string")
|
|
96
96
|
return v.strip() if v else None
|
|
@@ -6,9 +6,10 @@ Assembly domain objects with sensible defaults.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from datetime import datetime, timezone
|
|
9
|
+
|
|
9
10
|
from factory.base import Factory
|
|
10
|
-
from factory.faker import Faker
|
|
11
11
|
from factory.declarations import LazyFunction
|
|
12
|
+
from factory.faker import Faker
|
|
12
13
|
|
|
13
14
|
from julee.domain.models.assembly import (
|
|
14
15
|
Assembly,
|
|
@@ -19,11 +19,14 @@ Design decisions documented:
|
|
|
19
19
|
- Timestamps are automatically set with timezone-aware defaults
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
import pytest
|
|
23
22
|
import json
|
|
24
23
|
from datetime import datetime, timezone
|
|
25
24
|
|
|
25
|
+
import pytest
|
|
26
|
+
from pydantic import ValidationError
|
|
27
|
+
|
|
26
28
|
from julee.domain.models.assembly import Assembly, AssemblyStatus
|
|
29
|
+
|
|
27
30
|
from .factories import AssemblyFactory
|
|
28
31
|
|
|
29
32
|
|
|
@@ -79,7 +82,7 @@ class TestAssemblyInstantiation:
|
|
|
79
82
|
assert assembly.updated_at is not None
|
|
80
83
|
else:
|
|
81
84
|
# Should raise validation error
|
|
82
|
-
with pytest.raises(
|
|
85
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
83
86
|
Assembly(
|
|
84
87
|
assembly_id=assembly_id,
|
|
85
88
|
assembly_specification_id=assembly_specification_id,
|
|
@@ -222,7 +225,7 @@ class TestAssemblyFieldValidation:
|
|
|
222
225
|
assert valid_assembly.assembly_id == "valid-id"
|
|
223
226
|
|
|
224
227
|
# Invalid cases
|
|
225
|
-
with pytest.raises(
|
|
228
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
226
229
|
Assembly(
|
|
227
230
|
assembly_id="",
|
|
228
231
|
assembly_specification_id="spec-id",
|
|
@@ -230,7 +233,7 @@ class TestAssemblyFieldValidation:
|
|
|
230
233
|
workflow_id="test-workflow-123",
|
|
231
234
|
)
|
|
232
235
|
|
|
233
|
-
with pytest.raises(
|
|
236
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
234
237
|
Assembly(
|
|
235
238
|
assembly_id=" ",
|
|
236
239
|
assembly_specification_id="spec-id",
|
|
@@ -250,7 +253,7 @@ class TestAssemblyFieldValidation:
|
|
|
250
253
|
assert valid_assembly.assembly_specification_id == "valid-spec-id"
|
|
251
254
|
|
|
252
255
|
# Invalid cases
|
|
253
|
-
with pytest.raises(
|
|
256
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
254
257
|
Assembly(
|
|
255
258
|
assembly_id="asm-id",
|
|
256
259
|
assembly_specification_id="",
|
|
@@ -258,7 +261,7 @@ class TestAssemblyFieldValidation:
|
|
|
258
261
|
workflow_id="test-workflow-123",
|
|
259
262
|
)
|
|
260
263
|
|
|
261
|
-
with pytest.raises(
|
|
264
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
262
265
|
Assembly(
|
|
263
266
|
assembly_id="asm-id",
|
|
264
267
|
assembly_specification_id=" ",
|
|
@@ -278,7 +281,7 @@ class TestAssemblyFieldValidation:
|
|
|
278
281
|
assert valid_assembly.input_document_id == "valid-doc-id"
|
|
279
282
|
|
|
280
283
|
# Invalid cases
|
|
281
|
-
with pytest.raises(
|
|
284
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
282
285
|
Assembly(
|
|
283
286
|
assembly_id="asm-id",
|
|
284
287
|
assembly_specification_id="spec-id",
|
|
@@ -286,7 +289,7 @@ class TestAssemblyFieldValidation:
|
|
|
286
289
|
workflow_id="test-workflow-123",
|
|
287
290
|
)
|
|
288
291
|
|
|
289
|
-
with pytest.raises(
|
|
292
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
290
293
|
Assembly(
|
|
291
294
|
assembly_id="asm-id",
|
|
292
295
|
assembly_specification_id="spec-id",
|
|
@@ -345,7 +348,7 @@ class TestAssemblyDocumentManagement:
|
|
|
345
348
|
assert none_assembly.assembled_document_id is None
|
|
346
349
|
|
|
347
350
|
# Invalid cases - empty string
|
|
348
|
-
with pytest.raises(
|
|
351
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
349
352
|
Assembly(
|
|
350
353
|
assembly_id="asm-id",
|
|
351
354
|
assembly_specification_id="spec-id",
|
|
@@ -355,7 +358,7 @@ class TestAssemblyDocumentManagement:
|
|
|
355
358
|
)
|
|
356
359
|
|
|
357
360
|
# Invalid cases - whitespace only
|
|
358
|
-
with pytest.raises(
|
|
361
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
359
362
|
Assembly(
|
|
360
363
|
assembly_id="asm-id",
|
|
361
364
|
assembly_specification_id="spec-id",
|
|
@@ -391,7 +394,7 @@ class TestAssemblyWorkflowIdValidation:
|
|
|
391
394
|
assert valid_assembly.workflow_id == "valid-workflow-id"
|
|
392
395
|
|
|
393
396
|
# Invalid cases - empty string
|
|
394
|
-
with pytest.raises(
|
|
397
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
395
398
|
Assembly(
|
|
396
399
|
assembly_id="asm-id",
|
|
397
400
|
assembly_specification_id="spec-id",
|
|
@@ -400,7 +403,7 @@ class TestAssemblyWorkflowIdValidation:
|
|
|
400
403
|
)
|
|
401
404
|
|
|
402
405
|
# Invalid cases - whitespace only
|
|
403
|
-
with pytest.raises(
|
|
406
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
404
407
|
Assembly(
|
|
405
408
|
assembly_id="asm-id",
|
|
406
409
|
assembly_specification_id="spec-id",
|
|
@@ -421,7 +424,7 @@ class TestAssemblyWorkflowIdValidation:
|
|
|
421
424
|
def test_workflow_id_required(self) -> None:
|
|
422
425
|
"""Test that workflow_id is required."""
|
|
423
426
|
# workflow_id is required and cannot be omitted
|
|
424
|
-
with pytest.raises(
|
|
427
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
425
428
|
Assembly( # type: ignore[call-arg]
|
|
426
429
|
assembly_id="asm-id",
|
|
427
430
|
assembly_specification_id="spec-id",
|
|
@@ -13,12 +13,13 @@ All domain models use Pydantic BaseModel for validation, serialization,
|
|
|
13
13
|
and type safety, following the patterns established in the sample project.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from pydantic import BaseModel, Field, field_validator
|
|
17
|
-
from typing import Optional, Dict, Any
|
|
18
16
|
from datetime import datetime, timezone
|
|
19
17
|
from enum import Enum
|
|
20
|
-
import
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
21
20
|
import jsonpointer # type: ignore
|
|
21
|
+
import jsonschema
|
|
22
|
+
from pydantic import BaseModel, Field, field_validator
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class AssemblySpecificationStatus(str, Enum):
|
|
@@ -56,14 +57,14 @@ class AssemblySpecification(BaseModel):
|
|
|
56
57
|
"service for document-assembly matching"
|
|
57
58
|
)
|
|
58
59
|
|
|
59
|
-
jsonschema:
|
|
60
|
+
jsonschema: dict[str, Any] = Field(
|
|
60
61
|
description="JSON Schema defining the structure of data to be "
|
|
61
62
|
"extracted for this assembly"
|
|
62
63
|
)
|
|
63
64
|
|
|
64
65
|
# AssemblySpecification configuration
|
|
65
66
|
status: AssemblySpecificationStatus = AssemblySpecificationStatus.ACTIVE
|
|
66
|
-
knowledge_service_queries:
|
|
67
|
+
knowledge_service_queries: dict[str, str] = Field(
|
|
67
68
|
default_factory=dict,
|
|
68
69
|
description="Mapping from JSON Pointer paths to "
|
|
69
70
|
"KnowledgeServiceQuery IDs. Keys are JSON Pointer strings "
|
|
@@ -73,10 +74,10 @@ class AssemblySpecification(BaseModel):
|
|
|
73
74
|
|
|
74
75
|
# AssemblySpecification metadata
|
|
75
76
|
version: str = Field(default="0.1.0", description="Assembly definition version")
|
|
76
|
-
created_at:
|
|
77
|
+
created_at: datetime | None = Field(
|
|
77
78
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
78
79
|
)
|
|
79
|
-
updated_at:
|
|
80
|
+
updated_at: datetime | None = Field(
|
|
80
81
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
81
82
|
)
|
|
82
83
|
# May later add a detailed description, change log, additional metadata
|
|
@@ -105,7 +106,7 @@ class AssemblySpecification(BaseModel):
|
|
|
105
106
|
|
|
106
107
|
@field_validator("jsonschema")
|
|
107
108
|
@classmethod
|
|
108
|
-
def jsonschema_must_be_valid(cls, v:
|
|
109
|
+
def jsonschema_must_be_valid(cls, v: dict[str, Any]) -> dict[str, Any]:
|
|
109
110
|
if not isinstance(v, dict):
|
|
110
111
|
raise ValueError("JSON Schema must be a dictionary")
|
|
111
112
|
|
|
@@ -124,8 +125,8 @@ class AssemblySpecification(BaseModel):
|
|
|
124
125
|
@field_validator("knowledge_service_queries")
|
|
125
126
|
@classmethod
|
|
126
127
|
def knowledge_service_queries_must_be_valid(
|
|
127
|
-
cls, v:
|
|
128
|
-
) ->
|
|
128
|
+
cls, v: dict[str, str], info: Any
|
|
129
|
+
) -> dict[str, str]:
|
|
129
130
|
if not isinstance(v, dict):
|
|
130
131
|
raise ValueError("Knowledge service queries must be a dictionary")
|
|
131
132
|
|
|
@@ -15,9 +15,10 @@ All domain models use Pydantic BaseModel for validation, serialization,
|
|
|
15
15
|
and type safety, following the patterns established in the sample project.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from pydantic import BaseModel, Field, field_validator
|
|
19
|
-
from typing import Optional, Dict, Any
|
|
20
18
|
from datetime import datetime, timezone
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from pydantic import BaseModel, Field, field_validator
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class KnowledgeServiceQuery(BaseModel):
|
|
@@ -37,26 +38,30 @@ class KnowledgeServiceQuery(BaseModel):
|
|
|
37
38
|
|
|
38
39
|
Examples of query_metadata usage:
|
|
39
40
|
|
|
40
|
-
For Anthropic services
|
|
41
|
+
For Anthropic services::
|
|
42
|
+
|
|
41
43
|
query_metadata = {
|
|
42
44
|
"model": "claude-sonnet-4-20250514",
|
|
43
45
|
"max_tokens": 4000,
|
|
44
46
|
"temperature": 0.1
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
For OpenAI services
|
|
49
|
+
For OpenAI services::
|
|
50
|
+
|
|
48
51
|
query_metadata = {
|
|
49
52
|
"model": "gpt-4",
|
|
50
53
|
"temperature": 0.2,
|
|
51
54
|
"top_p": 0.9
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
For custom services
|
|
57
|
+
For custom services::
|
|
58
|
+
|
|
55
59
|
query_metadata = {
|
|
56
60
|
"endpoint": "custom-model-v2",
|
|
57
61
|
"timeout": 30,
|
|
58
62
|
"retries": 3
|
|
59
63
|
}
|
|
64
|
+
|
|
60
65
|
"""
|
|
61
66
|
|
|
62
67
|
# Core query identification
|
|
@@ -73,13 +78,13 @@ class KnowledgeServiceQuery(BaseModel):
|
|
|
73
78
|
)
|
|
74
79
|
|
|
75
80
|
# Service-specific configuration
|
|
76
|
-
query_metadata:
|
|
81
|
+
query_metadata: dict[str, Any] | None = Field(
|
|
77
82
|
default_factory=dict,
|
|
78
83
|
description="Service-specific metadata and configuration options "
|
|
79
84
|
"such as model selection, temperature, max_tokens, etc. "
|
|
80
85
|
"The structure depends on the specific knowledge service being used.",
|
|
81
86
|
)
|
|
82
|
-
assistant_prompt:
|
|
87
|
+
assistant_prompt: str | None = Field(
|
|
83
88
|
default=None,
|
|
84
89
|
description="Optional assistant message content to constrain "
|
|
85
90
|
"or prime the model's response. This is added as the final "
|
|
@@ -87,10 +92,10 @@ class KnowledgeServiceQuery(BaseModel):
|
|
|
87
92
|
"allowing control over response format and structure.",
|
|
88
93
|
)
|
|
89
94
|
|
|
90
|
-
created_at:
|
|
95
|
+
created_at: datetime | None = Field(
|
|
91
96
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
92
97
|
)
|
|
93
|
-
updated_at:
|
|
98
|
+
updated_at: datetime | None = Field(
|
|
94
99
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
95
100
|
)
|
|
96
101
|
|
|
@@ -7,9 +7,10 @@ AssemblySpecification domain objects with sensible defaults.
|
|
|
7
7
|
|
|
8
8
|
from datetime import datetime, timezone
|
|
9
9
|
from typing import Any
|
|
10
|
+
|
|
10
11
|
from factory.base import Factory
|
|
11
|
-
from factory.faker import Faker
|
|
12
12
|
from factory.declarations import LazyAttribute, LazyFunction
|
|
13
|
+
from factory.faker import Faker
|
|
13
14
|
|
|
14
15
|
from julee.domain.models.assembly_specification import (
|
|
15
16
|
AssemblySpecification,
|
|
@@ -18,14 +18,17 @@ Design decisions documented:
|
|
|
18
18
|
- Status defaults to ACTIVE
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
import pytest
|
|
22
21
|
import json
|
|
23
|
-
from typing import
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
import pytest
|
|
25
|
+
from pydantic import ValidationError
|
|
24
26
|
|
|
25
27
|
from julee.domain.models.assembly_specification import (
|
|
26
28
|
AssemblySpecification,
|
|
27
29
|
AssemblySpecificationStatus,
|
|
28
30
|
)
|
|
31
|
+
|
|
29
32
|
from .factories import AssemblyFactory
|
|
30
33
|
|
|
31
34
|
|
|
@@ -136,7 +139,7 @@ class TestAssemblyInstantiation:
|
|
|
136
139
|
assembly_specification_id: str,
|
|
137
140
|
name: str,
|
|
138
141
|
applicability: str,
|
|
139
|
-
jsonschema:
|
|
142
|
+
jsonschema: dict[str, Any],
|
|
140
143
|
expected_success: bool,
|
|
141
144
|
) -> None:
|
|
142
145
|
"""Test assembly creation with various field validation scenarios."""
|
|
@@ -158,7 +161,7 @@ class TestAssemblyInstantiation:
|
|
|
158
161
|
assert assembly.version == "0.1.0" # Default
|
|
159
162
|
else:
|
|
160
163
|
# Should raise validation error
|
|
161
|
-
with pytest.raises(
|
|
164
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
162
165
|
AssemblySpecification(
|
|
163
166
|
assembly_specification_id=assembly_specification_id,
|
|
164
167
|
name=name,
|
|
@@ -213,7 +216,7 @@ class TestAssemblyKnowledgeServiceQueriesValidation:
|
|
|
213
216
|
assert assembly.knowledge_service_queries == knowledge_service_queries
|
|
214
217
|
else:
|
|
215
218
|
# Should raise validation error
|
|
216
|
-
with pytest.raises(
|
|
219
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
217
220
|
AssemblySpecification(
|
|
218
221
|
assembly_specification_id="test-id",
|
|
219
222
|
name="Test Assembly",
|
|
@@ -486,5 +489,5 @@ class TestAssemblyVersionValidation:
|
|
|
486
489
|
assembly = AssemblyFactory.build(version=version)
|
|
487
490
|
assert assembly.version == version.strip()
|
|
488
491
|
else:
|
|
489
|
-
with pytest.raises(
|
|
492
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
490
493
|
AssemblyFactory.build(version=version)
|
|
@@ -19,10 +19,12 @@ Design decisions documented:
|
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import pytest
|
|
22
|
+
from pydantic import ValidationError
|
|
22
23
|
|
|
23
24
|
from julee.domain.models.assembly_specification import (
|
|
24
25
|
KnowledgeServiceQuery,
|
|
25
26
|
)
|
|
27
|
+
|
|
26
28
|
from .factories import KnowledgeServiceQueryFactory
|
|
27
29
|
|
|
28
30
|
|
|
@@ -137,7 +139,7 @@ class TestKnowledgeServiceQueryInstantiation:
|
|
|
137
139
|
assert query.prompt == prompt.strip()
|
|
138
140
|
else:
|
|
139
141
|
# Should raise validation error
|
|
140
|
-
with pytest.raises(
|
|
142
|
+
with pytest.raises((ValueError, ValidationError)):
|
|
141
143
|
KnowledgeServiceQuery(
|
|
142
144
|
query_id=query_id,
|
|
143
145
|
name=name,
|
|
@@ -6,10 +6,11 @@ validation
|
|
|
6
6
|
for specialized data types used in the document processing workflow.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import io
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
9
12
|
from pydantic import GetCoreSchemaHandler
|
|
10
13
|
from pydantic_core import core_schema
|
|
11
|
-
from typing import Any
|
|
12
|
-
import io
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class ContentStream:
|
|
@@ -12,10 +12,11 @@ Design decisions documented:
|
|
|
12
12
|
- ContentStream works with Pydantic validation without arbitrary_types_allowed
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
import pytest
|
|
16
15
|
import io
|
|
17
16
|
from typing import Any
|
|
18
17
|
|
|
18
|
+
import pytest
|
|
19
|
+
|
|
19
20
|
from julee.domain.models.custom_fields.content_stream import (
|
|
20
21
|
ContentStream,
|
|
21
22
|
)
|