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
|
@@ -6,20 +6,20 @@ KnowledgeService instances based on the service API configuration.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
+
from typing import Any
|
|
9
10
|
|
|
11
|
+
from julee.domain.models.document import Document
|
|
10
12
|
from julee.domain.models.knowledge_service_config import (
|
|
11
13
|
KnowledgeServiceConfig,
|
|
12
14
|
ServiceApi,
|
|
13
15
|
)
|
|
14
|
-
from .knowledge_service import KnowledgeService
|
|
15
|
-
from .anthropic import AnthropicKnowledgeService
|
|
16
|
-
from julee.domain.models.document import Document
|
|
17
16
|
from julee.services.knowledge_service import (
|
|
18
|
-
QueryResult,
|
|
19
17
|
FileRegistrationResult,
|
|
18
|
+
QueryResult,
|
|
20
19
|
)
|
|
21
|
-
from typing import Dict, Any, List, Optional
|
|
22
20
|
|
|
21
|
+
from .anthropic import AnthropicKnowledgeService
|
|
22
|
+
from .knowledge_service import KnowledgeService
|
|
23
23
|
|
|
24
24
|
logger = logging.getLogger(__name__)
|
|
25
25
|
|
|
@@ -47,9 +47,9 @@ class ConfigurableKnowledgeService(KnowledgeService):
|
|
|
47
47
|
self,
|
|
48
48
|
config: KnowledgeServiceConfig,
|
|
49
49
|
query_text: str,
|
|
50
|
-
service_file_ids:
|
|
51
|
-
query_metadata:
|
|
52
|
-
assistant_prompt:
|
|
50
|
+
service_file_ids: list[str] | None = None,
|
|
51
|
+
query_metadata: dict[str, Any] | None = None,
|
|
52
|
+
assistant_prompt: str | None = None,
|
|
53
53
|
) -> QueryResult:
|
|
54
54
|
"""Execute a query against the knowledge service."""
|
|
55
55
|
service = knowledge_service_factory(config)
|
|
@@ -11,16 +11,14 @@ Concrete implementations of this protocol are provided for different external
|
|
|
11
11
|
services (Anthropic, OpenAI, etc.) and are created via factory functions.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
+
from datetime import datetime, timezone
|
|
14
15
|
from typing import (
|
|
16
|
+
TYPE_CHECKING,
|
|
17
|
+
Any,
|
|
15
18
|
Protocol,
|
|
16
|
-
Optional,
|
|
17
|
-
List,
|
|
18
19
|
runtime_checkable,
|
|
19
|
-
Dict,
|
|
20
|
-
Any,
|
|
21
|
-
TYPE_CHECKING,
|
|
22
20
|
)
|
|
23
|
-
|
|
21
|
+
|
|
24
22
|
from pydantic import BaseModel, Field
|
|
25
23
|
|
|
26
24
|
if TYPE_CHECKING:
|
|
@@ -36,15 +34,15 @@ class QueryResult(BaseModel):
|
|
|
36
34
|
|
|
37
35
|
query_id: str = Field(description="Unique identifier for this query execution")
|
|
38
36
|
query_text: str = Field(description="The original query text that was executed")
|
|
39
|
-
result_data:
|
|
37
|
+
result_data: dict[str, Any] = Field(
|
|
40
38
|
default_factory=dict,
|
|
41
39
|
description="The structured result data from the query",
|
|
42
40
|
)
|
|
43
|
-
execution_time_ms:
|
|
41
|
+
execution_time_ms: int | None = Field(
|
|
44
42
|
default=None,
|
|
45
43
|
description="Time taken to execute the query in milliseconds",
|
|
46
44
|
)
|
|
47
|
-
created_at:
|
|
45
|
+
created_at: datetime | None = Field(
|
|
48
46
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
49
47
|
)
|
|
50
48
|
|
|
@@ -56,11 +54,11 @@ class FileRegistrationResult(BaseModel):
|
|
|
56
54
|
knowledge_service_file_id: str = Field(
|
|
57
55
|
description="The file identifier assigned by the knowledge service"
|
|
58
56
|
)
|
|
59
|
-
registration_metadata:
|
|
57
|
+
registration_metadata: dict[str, Any] = Field(
|
|
60
58
|
default_factory=dict,
|
|
61
59
|
description="Additional metadata from the registration process",
|
|
62
60
|
)
|
|
63
|
-
created_at:
|
|
61
|
+
created_at: datetime | None = Field(
|
|
64
62
|
default_factory=lambda: datetime.now(timezone.utc)
|
|
65
63
|
)
|
|
66
64
|
|
|
@@ -113,9 +111,9 @@ class KnowledgeService(Protocol):
|
|
|
113
111
|
self,
|
|
114
112
|
config: "KnowledgeServiceConfig",
|
|
115
113
|
query_text: str,
|
|
116
|
-
service_file_ids:
|
|
117
|
-
query_metadata:
|
|
118
|
-
assistant_prompt:
|
|
114
|
+
service_file_ids: list[str] | None = None,
|
|
115
|
+
query_metadata: dict[str, Any] | None = None,
|
|
116
|
+
assistant_prompt: str | None = None,
|
|
119
117
|
) -> QueryResult:
|
|
120
118
|
"""Execute a query against the external knowledge service.
|
|
121
119
|
|
|
@@ -8,18 +8,19 @@ scenarios where external service dependencies should be avoided.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
|
-
from typing import Optional, List, Dict, Deque, Any
|
|
12
|
-
from datetime import datetime, timezone
|
|
13
11
|
from collections import deque
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Any
|
|
14
14
|
|
|
15
|
+
from julee.domain.models.document import Document
|
|
15
16
|
from julee.domain.models.knowledge_service_config import (
|
|
16
17
|
KnowledgeServiceConfig,
|
|
17
18
|
)
|
|
18
|
-
|
|
19
|
+
|
|
19
20
|
from ..knowledge_service import (
|
|
21
|
+
FileRegistrationResult,
|
|
20
22
|
KnowledgeService,
|
|
21
23
|
QueryResult,
|
|
22
|
-
FileRegistrationResult,
|
|
23
24
|
)
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
@@ -59,10 +60,10 @@ class MemoryKnowledgeService(KnowledgeService):
|
|
|
59
60
|
self.config = config
|
|
60
61
|
|
|
61
62
|
# Storage for file registrations, keyed by knowledge_service_file_id
|
|
62
|
-
self._registered_files:
|
|
63
|
+
self._registered_files: dict[str, FileRegistrationResult] = {}
|
|
63
64
|
|
|
64
65
|
# Queue of canned query results to return
|
|
65
|
-
self._canned_query_results:
|
|
66
|
+
self._canned_query_results: deque[QueryResult] = deque()
|
|
66
67
|
|
|
67
68
|
def add_canned_query_result(self, query_result: QueryResult) -> None:
|
|
68
69
|
"""Add a canned query result to be returned by execute_query.
|
|
@@ -80,7 +81,7 @@ class MemoryKnowledgeService(KnowledgeService):
|
|
|
80
81
|
)
|
|
81
82
|
self._canned_query_results.append(query_result)
|
|
82
83
|
|
|
83
|
-
def add_canned_query_results(self, query_results:
|
|
84
|
+
def add_canned_query_results(self, query_results: list[QueryResult]) -> None:
|
|
84
85
|
"""Add multiple canned query results to be returned by execute_query.
|
|
85
86
|
|
|
86
87
|
Args:
|
|
@@ -109,7 +110,7 @@ class MemoryKnowledgeService(KnowledgeService):
|
|
|
109
110
|
|
|
110
111
|
def get_registered_file(
|
|
111
112
|
self, knowledge_service_file_id: str
|
|
112
|
-
) ->
|
|
113
|
+
) -> FileRegistrationResult | None:
|
|
113
114
|
"""Get a registered file by its knowledge service file ID.
|
|
114
115
|
|
|
115
116
|
Args:
|
|
@@ -120,7 +121,7 @@ class MemoryKnowledgeService(KnowledgeService):
|
|
|
120
121
|
"""
|
|
121
122
|
return self._registered_files.get(knowledge_service_file_id)
|
|
122
123
|
|
|
123
|
-
def get_all_registered_files(self) ->
|
|
124
|
+
def get_all_registered_files(self) -> dict[str, FileRegistrationResult]:
|
|
124
125
|
"""Get all registered files.
|
|
125
126
|
|
|
126
127
|
Returns:
|
|
@@ -203,9 +204,9 @@ class MemoryKnowledgeService(KnowledgeService):
|
|
|
203
204
|
self,
|
|
204
205
|
config: KnowledgeServiceConfig,
|
|
205
206
|
query_text: str,
|
|
206
|
-
service_file_ids:
|
|
207
|
-
query_metadata:
|
|
208
|
-
assistant_prompt:
|
|
207
|
+
service_file_ids: list[str] | None = None,
|
|
208
|
+
query_metadata: dict[str, Any] | None = None,
|
|
209
|
+
assistant_prompt: str | None = None,
|
|
209
210
|
) -> QueryResult:
|
|
210
211
|
"""Execute a query by returning a canned response.
|
|
211
212
|
|
|
@@ -6,19 +6,22 @@ KnowledgeService protocol, verifying file registration storage and
|
|
|
6
6
|
canned query response functionality.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import io
|
|
10
10
|
from datetime import datetime, timezone
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
from julee.domain.models.document import Document, DocumentStatus
|
|
15
|
-
from julee.domain.models.knowledge_service_config import ServiceApi
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
16
14
|
from julee.domain.models.custom_fields.content_stream import (
|
|
17
15
|
ContentStream,
|
|
18
16
|
)
|
|
17
|
+
from julee.domain.models.document import Document, DocumentStatus
|
|
18
|
+
from julee.domain.models.knowledge_service_config import (
|
|
19
|
+
KnowledgeServiceConfig,
|
|
20
|
+
ServiceApi,
|
|
21
|
+
)
|
|
22
|
+
|
|
19
23
|
from ..knowledge_service import QueryResult
|
|
20
24
|
from .knowledge_service import MemoryKnowledgeService
|
|
21
|
-
import io
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
@pytest.fixture
|
|
@@ -5,25 +5,26 @@ This module contains tests for the factory function that creates
|
|
|
5
5
|
KnowledgeService implementations based on configuration.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import io
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
|
|
8
11
|
import pytest
|
|
9
12
|
|
|
10
|
-
from julee.domain.models.knowledge_service_config import (
|
|
11
|
-
KnowledgeServiceConfig,
|
|
12
|
-
)
|
|
13
|
-
from julee.domain.models.document import Document, DocumentStatus
|
|
14
|
-
from julee.domain.models.knowledge_service_config import ServiceApi
|
|
15
13
|
from julee.domain.models.custom_fields.content_stream import (
|
|
16
14
|
ContentStream,
|
|
17
15
|
)
|
|
18
|
-
from julee.
|
|
19
|
-
from julee.
|
|
20
|
-
|
|
16
|
+
from julee.domain.models.document import Document, DocumentStatus
|
|
17
|
+
from julee.domain.models.knowledge_service_config import (
|
|
18
|
+
KnowledgeServiceConfig,
|
|
19
|
+
ServiceApi,
|
|
21
20
|
)
|
|
21
|
+
from julee.services.knowledge_service import ensure_knowledge_service
|
|
22
22
|
from julee.services.knowledge_service.anthropic import (
|
|
23
23
|
AnthropicKnowledgeService,
|
|
24
24
|
)
|
|
25
|
-
import
|
|
26
|
-
|
|
25
|
+
from julee.services.knowledge_service.factory import (
|
|
26
|
+
knowledge_service_factory,
|
|
27
|
+
)
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
@pytest.fixture
|
|
@@ -13,23 +13,23 @@ The class follows the naming pattern documented in systemPatterns.org:
|
|
|
13
13
|
|
|
14
14
|
import io
|
|
15
15
|
import logging
|
|
16
|
+
|
|
16
17
|
from typing_extensions import override
|
|
17
18
|
|
|
18
|
-
from julee.
|
|
19
|
-
from julee.services.knowledge_service.factory import (
|
|
20
|
-
ConfigurableKnowledgeService,
|
|
21
|
-
)
|
|
22
|
-
from julee.domain.repositories.document import DocumentRepository
|
|
19
|
+
from julee.domain.models.document import Document
|
|
23
20
|
from julee.domain.models.knowledge_service_config import (
|
|
24
21
|
KnowledgeServiceConfig,
|
|
25
22
|
)
|
|
26
|
-
from julee.domain.
|
|
27
|
-
from
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
from julee.domain.repositories.document import DocumentRepository
|
|
24
|
+
from julee.services.knowledge_service.factory import (
|
|
25
|
+
ConfigurableKnowledgeService,
|
|
26
|
+
)
|
|
30
27
|
from julee.services.temporal.activity_names import (
|
|
31
28
|
KNOWLEDGE_SERVICE_ACTIVITY_BASE,
|
|
32
29
|
)
|
|
30
|
+
from julee.util.temporal.decorators import temporal_activity_registration
|
|
31
|
+
|
|
32
|
+
from ..knowledge_service import FileRegistrationResult
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
@temporal_activity_registration(KNOWLEDGE_SERVICE_ACTIVITY_BASE)
|
|
@@ -79,7 +79,7 @@ class TemporalKnowledgeService(ConfigurableKnowledgeService):
|
|
|
79
79
|
return await super().register_file(config, document)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
# Export the temporal service
|
|
82
|
+
# Export the temporal service classes for use in worker.py
|
|
83
83
|
__all__ = [
|
|
84
84
|
"TemporalKnowledgeService",
|
|
85
85
|
"KNOWLEDGE_SERVICE_ACTIVITY_BASE",
|
|
@@ -11,13 +11,13 @@ workflow.execute_activity() with the appropriate activity names, timeouts,
|
|
|
11
11
|
and retry policies.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
from julee.util.temporal.decorators import temporal_workflow_proxy
|
|
15
14
|
from julee.services.knowledge_service import KnowledgeService
|
|
16
15
|
|
|
17
16
|
# Import activity name bases from shared module
|
|
18
17
|
from julee.services.temporal.activity_names import (
|
|
19
18
|
KNOWLEDGE_SERVICE_ACTIVITY_BASE,
|
|
20
19
|
)
|
|
20
|
+
from julee.util.temporal.decorators import temporal_workflow_proxy
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
@temporal_workflow_proxy(
|
|
@@ -35,7 +35,7 @@ class WorkflowKnowledgeServiceProxy(KnowledgeService):
|
|
|
35
35
|
pass
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
# Export the workflow proxy
|
|
38
|
+
# Export the workflow proxy classes
|
|
39
39
|
__all__ = [
|
|
40
40
|
"WorkflowKnowledgeServiceProxy",
|
|
41
41
|
]
|
julee/util/domain.py
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
|
|
1
3
|
from pydantic import (
|
|
2
4
|
BaseModel,
|
|
3
5
|
Field,
|
|
4
6
|
field_validator,
|
|
5
7
|
)
|
|
6
|
-
from typing import Optional, Dict
|
|
7
|
-
from datetime import datetime, timezone
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class FileMetadata(BaseModel):
|
|
11
11
|
"""Metadata about a stored file."""
|
|
12
12
|
|
|
13
13
|
file_id: str
|
|
14
|
-
filename:
|
|
15
|
-
content_type:
|
|
16
|
-
size_bytes:
|
|
14
|
+
filename: str | None = None
|
|
15
|
+
content_type: str | None = None
|
|
16
|
+
size_bytes: int | None = None
|
|
17
17
|
uploaded_at: str = Field(
|
|
18
18
|
default_factory=lambda: datetime.now(timezone.utc).isoformat()
|
|
19
19
|
)
|
|
20
|
-
metadata:
|
|
20
|
+
metadata: dict[str, str] = Field(default_factory=dict)
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class FileUploadArgs(BaseModel):
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
-
from typing import Optional
|
|
5
4
|
|
|
6
5
|
from minio import Minio # type: ignore[import-untyped]
|
|
7
6
|
from minio.error import S3Error # type: ignore[import-untyped]
|
|
@@ -20,11 +19,11 @@ class MinioFileStorageRepository(FileStorageRepository):
|
|
|
20
19
|
|
|
21
20
|
def __init__(
|
|
22
21
|
self,
|
|
23
|
-
endpoint:
|
|
24
|
-
access_key:
|
|
25
|
-
secret_key:
|
|
22
|
+
endpoint: str | None = None,
|
|
23
|
+
access_key: str | None = None,
|
|
24
|
+
secret_key: str | None = None,
|
|
26
25
|
secure: bool = False,
|
|
27
|
-
bucket_name:
|
|
26
|
+
bucket_name: str | None = None,
|
|
28
27
|
):
|
|
29
28
|
self._endpoint = (
|
|
30
29
|
endpoint
|
|
@@ -48,7 +47,7 @@ class MinioFileStorageRepository(FileStorageRepository):
|
|
|
48
47
|
else os.environ.get("MINIO_BUCKET_NAME", "file-storage")
|
|
49
48
|
)
|
|
50
49
|
|
|
51
|
-
self._client:
|
|
50
|
+
self._client: Minio | None = None
|
|
52
51
|
logger.debug(
|
|
53
52
|
"MinioFileStorageRepository initialized",
|
|
54
53
|
extra={
|
|
@@ -134,7 +133,7 @@ class MinioFileStorageRepository(FileStorageRepository):
|
|
|
134
133
|
)
|
|
135
134
|
raise
|
|
136
135
|
|
|
137
|
-
async def download_file(self, file_id: str) ->
|
|
136
|
+
async def download_file(self, file_id: str) -> bytes | None:
|
|
138
137
|
"""Download a file from Minio storage by its ID."""
|
|
139
138
|
client = await self._get_client()
|
|
140
139
|
logger.info(
|
|
@@ -161,7 +160,7 @@ class MinioFileStorageRepository(FileStorageRepository):
|
|
|
161
160
|
)
|
|
162
161
|
raise
|
|
163
162
|
|
|
164
|
-
async def get_file_metadata(self, file_id: str) ->
|
|
163
|
+
async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
|
|
165
164
|
"""Retrieve metadata for a stored file from Minio."""
|
|
166
165
|
client = await self._get_client()
|
|
167
166
|
logger.info(
|
|
@@ -178,7 +177,7 @@ class MinioFileStorageRepository(FileStorageRepository):
|
|
|
178
177
|
"content_type": stat.content_type,
|
|
179
178
|
},
|
|
180
179
|
)
|
|
181
|
-
uploaded_at_str:
|
|
180
|
+
uploaded_at_str: str | None = (
|
|
182
181
|
stat.last_modified.isoformat() if stat.last_modified else None
|
|
183
182
|
)
|
|
184
183
|
# Extract filename and metadata more explicitly
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from temporalio.client import Client
|
|
5
4
|
|
|
@@ -19,7 +18,7 @@ class TemporalFileStorageRepository(FileStorageRepository):
|
|
|
19
18
|
def __init__(
|
|
20
19
|
self,
|
|
21
20
|
client: Client,
|
|
22
|
-
concrete_repo:
|
|
21
|
+
concrete_repo: FileStorageRepository | None = None,
|
|
23
22
|
):
|
|
24
23
|
self.client = client
|
|
25
24
|
self.concrete_repo = concrete_repo
|
|
@@ -39,7 +38,7 @@ class TemporalFileStorageRepository(FileStorageRepository):
|
|
|
39
38
|
result = await handle.result()
|
|
40
39
|
return result # type: ignore[no-any-return]
|
|
41
40
|
|
|
42
|
-
async def download_file(self, file_id: str) ->
|
|
41
|
+
async def download_file(self, file_id: str) -> bytes | None:
|
|
43
42
|
"""Download a file via Temporal activity."""
|
|
44
43
|
logger.debug(f"Client calling activity to download file: {file_id}")
|
|
45
44
|
|
|
@@ -53,7 +52,7 @@ class TemporalFileStorageRepository(FileStorageRepository):
|
|
|
53
52
|
result = await handle.result()
|
|
54
53
|
return result # type: ignore[no-any-return]
|
|
55
54
|
|
|
56
|
-
async def get_file_metadata(self, file_id: str) ->
|
|
55
|
+
async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
|
|
57
56
|
"""Retrieve file metadata via Temporal activity."""
|
|
58
57
|
logger.debug(f"Client calling activity to get file metadata: {file_id}")
|
|
59
58
|
|
|
@@ -7,20 +7,20 @@ This allows domain models to implement context-aware validation that can
|
|
|
7
7
|
be more permissive during Temporal serialization/deserialization.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from typing import Any
|
|
10
|
+
from typing import Any
|
|
11
11
|
|
|
12
|
+
import temporalio.api.common.v1
|
|
12
13
|
from pydantic import TypeAdapter
|
|
13
14
|
from temporalio.contrib.pydantic import (
|
|
14
15
|
PydanticJSONPlainPayloadConverter,
|
|
15
16
|
ToJsonOptions,
|
|
16
17
|
)
|
|
17
18
|
from temporalio.converter import (
|
|
18
|
-
DataConverter,
|
|
19
19
|
CompositePayloadConverter,
|
|
20
|
+
DataConverter,
|
|
20
21
|
DefaultPayloadConverter,
|
|
21
22
|
JSONPlainPayloadConverter,
|
|
22
23
|
)
|
|
23
|
-
import temporalio.api.common.v1
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class TemporalValidationPydanticConverter(PydanticJSONPlainPayloadConverter):
|
|
@@ -36,7 +36,7 @@ class TemporalValidationPydanticConverter(PydanticJSONPlainPayloadConverter):
|
|
|
36
36
|
def from_payload(
|
|
37
37
|
self,
|
|
38
38
|
payload: temporalio.api.common.v1.Payload,
|
|
39
|
-
type_hint:
|
|
39
|
+
type_hint: type | None = None,
|
|
40
40
|
) -> Any:
|
|
41
41
|
"""Deserialize payload with temporal_validation context.
|
|
42
42
|
|
|
@@ -69,7 +69,7 @@ class TemporalValidationPayloadConverter(CompositePayloadConverter):
|
|
|
69
69
|
ensuring all Pydantic models get temporal_validation context.
|
|
70
70
|
"""
|
|
71
71
|
|
|
72
|
-
def __init__(self, to_json_options:
|
|
72
|
+
def __init__(self, to_json_options: ToJsonOptions | None = None) -> None:
|
|
73
73
|
"""Initialize with custom JSON converter adding temporal context."""
|
|
74
74
|
# Create our custom JSON converter with temporal validation
|
|
75
75
|
json_payload_converter = TemporalValidationPydanticConverter(to_json_options)
|
|
@@ -89,7 +89,7 @@ class TemporalValidationPayloadConverter(CompositePayloadConverter):
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
def create_temporal_data_converter(
|
|
92
|
-
to_json_options:
|
|
92
|
+
to_json_options: ToJsonOptions | None = None,
|
|
93
93
|
) -> DataConverter:
|
|
94
94
|
"""Create a data converter with temporal validation support.
|
|
95
95
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
from julee.util.temporal.decorators import temporal_activity_registration
|
|
2
1
|
from julee.util.repos.minio.file_storage import MinioFileStorageRepository
|
|
2
|
+
from julee.util.temporal.decorators import temporal_activity_registration
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@temporal_activity_registration("util.file_storage.minio")
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from temporalio import workflow
|
|
5
4
|
|
|
@@ -35,7 +34,7 @@ class WorkflowFileStorageRepositoryProxy(FileStorageRepository):
|
|
|
35
34
|
)
|
|
36
35
|
return FileMetadata.model_validate(result)
|
|
37
36
|
|
|
38
|
-
async def download_file(self, file_id: str) ->
|
|
37
|
+
async def download_file(self, file_id: str) -> bytes | None:
|
|
39
38
|
"""Download a file from storage via Temporal activity."""
|
|
40
39
|
logger.debug(f"Workflow calling activity to download file: {file_id}")
|
|
41
40
|
result = await workflow.execute_activity(
|
|
@@ -45,7 +44,7 @@ class WorkflowFileStorageRepositoryProxy(FileStorageRepository):
|
|
|
45
44
|
)
|
|
46
45
|
return result # type: ignore[no-any-return]
|
|
47
46
|
|
|
48
|
-
async def get_file_metadata(self, file_id: str) ->
|
|
47
|
+
async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
|
|
49
48
|
"""Retrieve file metadata via Temporal activity."""
|
|
50
49
|
logger.debug(f"Workflow calling activity to get file metadata: {file_id}")
|
|
51
50
|
result = await workflow.execute_activity(
|
julee/util/repositories.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
from typing import Protocol,
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
|
2
|
+
|
|
2
3
|
from julee.util.domain import FileMetadata, FileUploadArgs
|
|
3
4
|
|
|
4
5
|
|
|
@@ -30,7 +31,7 @@ class FileStorageRepository(Protocol):
|
|
|
30
31
|
"""
|
|
31
32
|
...
|
|
32
33
|
|
|
33
|
-
async def download_file(self, file_id: str) ->
|
|
34
|
+
async def download_file(self, file_id: str) -> bytes | None:
|
|
34
35
|
"""Download a file from storage by its ID.
|
|
35
36
|
|
|
36
37
|
Args:
|
|
@@ -41,7 +42,7 @@ class FileStorageRepository(Protocol):
|
|
|
41
42
|
"""
|
|
42
43
|
...
|
|
43
44
|
|
|
44
|
-
async def get_file_metadata(self, file_id: str) ->
|
|
45
|
+
async def get_file_metadata(self, file_id: str) -> FileMetadata | None:
|
|
45
46
|
"""Retrieve metadata for a stored file.
|
|
46
47
|
|
|
47
48
|
Args:
|
|
@@ -10,12 +10,10 @@ Both reduce boilerplate and ensure consistent patterns.
|
|
|
10
10
|
import functools
|
|
11
11
|
import inspect
|
|
12
12
|
import logging
|
|
13
|
+
from collections.abc import Callable
|
|
13
14
|
from datetime import timedelta
|
|
14
15
|
from typing import (
|
|
15
16
|
Any,
|
|
16
|
-
Callable,
|
|
17
|
-
Optional,
|
|
18
|
-
Type,
|
|
19
17
|
TypeVar,
|
|
20
18
|
get_args,
|
|
21
19
|
get_origin,
|
|
@@ -34,7 +32,7 @@ logger = logging.getLogger(__name__)
|
|
|
34
32
|
T = TypeVar("T")
|
|
35
33
|
|
|
36
34
|
|
|
37
|
-
def _extract_concrete_type_from_base(cls: type) ->
|
|
35
|
+
def _extract_concrete_type_from_base(cls: type) -> type | None:
|
|
38
36
|
"""
|
|
39
37
|
Extract the concrete type argument from a generic base class.
|
|
40
38
|
|
|
@@ -113,7 +111,7 @@ def _substitute_typevar_with_concrete(annotation: Any, concrete_type: type) -> A
|
|
|
113
111
|
|
|
114
112
|
def temporal_activity_registration(
|
|
115
113
|
activity_prefix: str,
|
|
116
|
-
) -> Callable[[
|
|
114
|
+
) -> Callable[[type[T]], type[T]]:
|
|
117
115
|
"""
|
|
118
116
|
Class decorator that wraps async protocol methods as Temporal activities.
|
|
119
117
|
|
|
@@ -139,7 +137,7 @@ def temporal_activity_registration(
|
|
|
139
137
|
# - refund_payment -> "sample.payment_repo.minio.refund_payment"
|
|
140
138
|
"""
|
|
141
139
|
|
|
142
|
-
def decorator(cls:
|
|
140
|
+
def decorator(cls: type[T]) -> type[T]:
|
|
143
141
|
logger.debug(
|
|
144
142
|
f"Applying temporal_activity_registration decorator to {cls.__name__}"
|
|
145
143
|
)
|
|
@@ -180,8 +178,9 @@ def temporal_activity_registration(
|
|
|
180
178
|
return wrapper_method
|
|
181
179
|
|
|
182
180
|
# Create the wrapper and apply activity decorator
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
wrapped_method = activity.defn(name=activity_name)(
|
|
182
|
+
create_wrapper_method(method, name)
|
|
183
|
+
)
|
|
185
184
|
|
|
186
185
|
# Replace the method on the class with the wrapped version
|
|
187
186
|
setattr(cls, name, wrapped_method)
|
|
@@ -204,8 +203,8 @@ def temporal_activity_registration(
|
|
|
204
203
|
def temporal_workflow_proxy(
|
|
205
204
|
activity_base: str,
|
|
206
205
|
default_timeout_seconds: int = 30,
|
|
207
|
-
retry_methods:
|
|
208
|
-
) -> Callable[[
|
|
206
|
+
retry_methods: list[str] | None = None,
|
|
207
|
+
) -> Callable[[type[T]], type[T]]:
|
|
209
208
|
"""
|
|
210
209
|
Class decorator that automatically creates workflow proxy methods that
|
|
211
210
|
delegate to Temporal activities.
|
|
@@ -238,7 +237,7 @@ def temporal_workflow_proxy(
|
|
|
238
237
|
# - generate_id() -> calls "julee.document_repo.minio.generate_id" with retry
|
|
239
238
|
"""
|
|
240
239
|
|
|
241
|
-
def decorator(cls:
|
|
240
|
+
def decorator(cls: type[T]) -> type[T]:
|
|
242
241
|
logger.debug(f"Applying temporal_workflow_proxy decorator to {cls.__name__}")
|
|
243
242
|
|
|
244
243
|
retry_methods_set = set(retry_methods or [])
|
|
@@ -390,14 +389,17 @@ def temporal_workflow_proxy(
|
|
|
390
389
|
return workflow_method
|
|
391
390
|
|
|
392
391
|
# Create and set the method on the class
|
|
393
|
-
|
|
392
|
+
setattr(
|
|
393
|
+
cls,
|
|
394
394
|
method_name,
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
create_workflow_method(
|
|
396
|
+
method_name,
|
|
397
|
+
needs_validation,
|
|
398
|
+
is_optional,
|
|
399
|
+
inner_type,
|
|
400
|
+
original_method,
|
|
401
|
+
),
|
|
399
402
|
)
|
|
400
|
-
setattr(cls, method_name, workflow_method)
|
|
401
403
|
wrapped_methods.append(method_name)
|
|
402
404
|
|
|
403
405
|
# Always generate __init__ that calls super() for consistent init
|
|
@@ -409,7 +411,7 @@ def temporal_workflow_proxy(
|
|
|
409
411
|
proxy_self.activity_fail_fast_retry_policy = fail_fast_retry_policy
|
|
410
412
|
logger.debug(f"Initialized {cls.__name__}")
|
|
411
413
|
|
|
412
|
-
|
|
414
|
+
cls.__init__ = __init__
|
|
413
415
|
|
|
414
416
|
logger.info(
|
|
415
417
|
f"Temporal workflow proxy decorator applied to {cls.__name__}",
|