julee 0.1.3__py3-none-any.whl → 0.1.5__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/__init__.py +1 -1
- julee/api/tests/routers/test_assembly_specifications.py +2 -0
- julee/api/tests/routers/test_documents.py +8 -6
- julee/api/tests/routers/test_knowledge_service_configs.py +2 -0
- julee/api/tests/routers/test_knowledge_service_queries.py +2 -0
- julee/api/tests/routers/test_system.py +2 -0
- julee/api/tests/routers/test_workflows.py +2 -0
- julee/api/tests/test_app.py +2 -0
- julee/api/tests/test_dependencies.py +2 -0
- julee/api/tests/test_requests.py +2 -0
- julee/contrib/polling/__init__.py +22 -19
- julee/contrib/polling/apps/__init__.py +17 -0
- julee/contrib/polling/apps/worker/__init__.py +17 -0
- julee/contrib/polling/apps/worker/pipelines.py +288 -0
- julee/contrib/polling/domain/__init__.py +7 -9
- julee/contrib/polling/domain/models/__init__.py +6 -7
- julee/contrib/polling/domain/models/polling_config.py +18 -1
- julee/contrib/polling/domain/services/__init__.py +6 -5
- julee/contrib/polling/domain/services/poller.py +1 -1
- julee/contrib/polling/infrastructure/__init__.py +9 -8
- julee/contrib/polling/infrastructure/services/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/http/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +5 -2
- julee/contrib/polling/infrastructure/temporal/__init__.py +12 -12
- julee/contrib/polling/infrastructure/temporal/activities.py +1 -1
- julee/contrib/polling/infrastructure/temporal/manager.py +291 -0
- julee/contrib/polling/infrastructure/temporal/proxies.py +1 -1
- julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +580 -0
- julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +40 -2
- julee/contrib/polling/tests/unit/infrastructure/temporal/__init__.py +7 -0
- julee/contrib/polling/tests/unit/infrastructure/temporal/test_manager.py +475 -0
- julee/docs/sphinx_hcd/__init__.py +4 -10
- julee/docs/sphinx_hcd/accelerators.py +277 -180
- julee/docs/sphinx_hcd/apps.py +78 -59
- julee/docs/sphinx_hcd/config.py +16 -16
- julee/docs/sphinx_hcd/epics.py +47 -42
- julee/docs/sphinx_hcd/integrations.py +53 -49
- julee/docs/sphinx_hcd/journeys.py +124 -110
- julee/docs/sphinx_hcd/personas.py +75 -53
- julee/docs/sphinx_hcd/stories.py +99 -71
- julee/docs/sphinx_hcd/utils.py +23 -18
- julee/domain/models/assembly/tests/test_assembly.py +2 -0
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +2 -0
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +2 -0
- julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -0
- julee/domain/models/document/document.py +12 -21
- julee/domain/models/document/tests/test_document.py +16 -34
- julee/domain/models/policy/tests/test_document_policy_validation.py +2 -0
- julee/domain/models/policy/tests/test_policy.py +2 -0
- julee/domain/use_cases/extract_assemble_data.py +1 -1
- julee/domain/use_cases/initialize_system_data.py +75 -21
- julee/domain/use_cases/tests/test_extract_assemble_data.py +2 -0
- julee/domain/use_cases/tests/test_initialize_system_data.py +2 -0
- julee/domain/use_cases/tests/test_validate_document.py +2 -0
- julee/fixtures/documents.yaml +4 -43
- julee/fixtures/knowledge_service_queries.yaml +9 -0
- julee/maintenance/release.py +90 -30
- julee/repositories/memory/document.py +19 -13
- julee/repositories/memory/tests/test_document.py +20 -18
- julee/repositories/memory/tests/test_document_policy_validation.py +2 -0
- julee/repositories/memory/tests/test_policy.py +2 -0
- julee/repositories/minio/document.py +25 -22
- julee/repositories/minio/tests/test_assembly.py +2 -0
- julee/repositories/minio/tests/test_assembly_specification.py +2 -0
- julee/repositories/minio/tests/test_client_protocol.py +3 -0
- julee/repositories/minio/tests/test_document.py +18 -16
- julee/repositories/minio/tests/test_document_policy_validation.py +2 -0
- julee/repositories/minio/tests/test_knowledge_service_config.py +2 -0
- julee/repositories/minio/tests/test_knowledge_service_query.py +2 -0
- julee/repositories/minio/tests/test_policy.py +2 -0
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +2 -0
- julee/services/knowledge_service/memory/test_knowledge_service.py +2 -0
- julee/services/knowledge_service/test_factory.py +2 -0
- julee/util/tests/test_decorators.py +2 -0
- julee-0.1.5.dist-info/METADATA +103 -0
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/RECORD +80 -74
- julee/fixtures/assembly_specifications.yaml +0 -70
- julee-0.1.3.dist-info/METADATA +0 -198
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/WHEEL +0 -0
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/top_level.txt +0 -0
|
@@ -60,46 +60,52 @@ class MemoryDocumentRepository(DocumentRepository, MemoryRepositoryMixin[Documen
|
|
|
60
60
|
async def save(self, document: Document) -> None:
|
|
61
61
|
"""Save a document with its content and metadata.
|
|
62
62
|
|
|
63
|
-
If the document has
|
|
64
|
-
|
|
63
|
+
If the document has content_bytes, it will be normalized to bytes
|
|
64
|
+
(encoding str as UTF-8), converted to a ContentStream and the
|
|
65
|
+
content hash will be calculated automatically.
|
|
65
66
|
|
|
66
67
|
Args:
|
|
67
68
|
document: Document object to save
|
|
68
69
|
|
|
69
70
|
Raises:
|
|
70
|
-
ValueError: If document has no content or
|
|
71
|
+
ValueError: If document has no content or content_bytes
|
|
72
|
+
TypeError: If content_bytes is not bytes or str
|
|
71
73
|
"""
|
|
72
74
|
# Handle content_string conversion (only if no content provided)
|
|
73
|
-
if document.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
if document.content_bytes is not None:
|
|
76
|
+
if isinstance(document.content_bytes, str):
|
|
77
|
+
raw_bytes = document.content_bytes.encode("utf-8")
|
|
78
|
+
elif isinstance(document.content_bytes, bytes):
|
|
79
|
+
raw_bytes = document.content_bytes
|
|
80
|
+
else:
|
|
81
|
+
raise TypeError("content_bytes must be of type 'bytes' or 'str'.")
|
|
82
|
+
|
|
83
|
+
content_stream = ContentStream(io.BytesIO(raw_bytes))
|
|
78
84
|
|
|
79
85
|
# Calculate content hash
|
|
80
|
-
content_hash = hashlib.sha256(
|
|
86
|
+
content_hash = hashlib.sha256(raw_bytes).hexdigest()
|
|
81
87
|
|
|
82
88
|
# Create new document with ContentStream and calculated hash
|
|
83
89
|
document = document.model_copy(
|
|
84
90
|
update={
|
|
85
91
|
"content": content_stream,
|
|
86
92
|
"content_multihash": content_hash,
|
|
87
|
-
"size_bytes": len(
|
|
93
|
+
"size_bytes": len(raw_bytes),
|
|
88
94
|
}
|
|
89
95
|
)
|
|
90
96
|
|
|
91
97
|
self.logger.debug(
|
|
92
|
-
"Converted
|
|
98
|
+
"Converted content_bytes to ContentStream for document save",
|
|
93
99
|
extra={
|
|
94
100
|
"document_id": document.document_id,
|
|
95
101
|
"content_hash": content_hash,
|
|
96
|
-
"content_length": len(
|
|
102
|
+
"content_length": len(raw_bytes),
|
|
97
103
|
},
|
|
98
104
|
)
|
|
99
105
|
|
|
100
106
|
# Create a copy without content_string (content saved
|
|
101
107
|
# in separate content-addressable storage)
|
|
102
|
-
document_for_storage = document.model_copy(update={"
|
|
108
|
+
document_for_storage = document.model_copy(update={"content_bytes": None})
|
|
103
109
|
self.save_entity(document_for_storage, "document_id")
|
|
104
110
|
|
|
105
111
|
async def generate_id(self) -> str:
|
|
@@ -3,7 +3,7 @@ Unit tests for MemoryDocumentRepository.
|
|
|
3
3
|
|
|
4
4
|
These tests verify the memory implementation logic without requiring external
|
|
5
5
|
dependencies. They follow the Clean Architecture testing patterns and verify
|
|
6
|
-
idempotency, error handling, and content operations including
|
|
6
|
+
idempotency, error handling, and content operations including content_bytes.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import io
|
|
@@ -18,6 +18,8 @@ from julee.repositories.memory.document import (
|
|
|
18
18
|
MemoryDocumentRepository,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
pytestmark = pytest.mark.unit
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
@pytest.fixture
|
|
23
25
|
def repository() -> MemoryDocumentRepository:
|
|
@@ -46,16 +48,16 @@ def sample_document(sample_content: ContentStream) -> Document:
|
|
|
46
48
|
)
|
|
47
49
|
|
|
48
50
|
|
|
49
|
-
class
|
|
50
|
-
"""Test
|
|
51
|
+
class TestMemoryDocumentRepositoryContentBytes:
|
|
52
|
+
"""Test content_bytes functionality."""
|
|
51
53
|
|
|
52
|
-
async def
|
|
54
|
+
async def test_save_document_with_content_bytes(
|
|
53
55
|
self, repository: MemoryDocumentRepository
|
|
54
56
|
) -> None:
|
|
55
|
-
"""Test saving document with
|
|
57
|
+
"""Test saving document with content_bytes."""
|
|
56
58
|
content = '{"assembled": "document", "data": "test"}'
|
|
57
59
|
|
|
58
|
-
# Create document with
|
|
60
|
+
# Create document with content_bytes
|
|
59
61
|
document = Document(
|
|
60
62
|
document_id="test-doc-content-string",
|
|
61
63
|
original_filename="assembled.json",
|
|
@@ -63,10 +65,10 @@ class TestMemoryDocumentRepositoryContentString:
|
|
|
63
65
|
size_bytes=100, # Will be updated automatically
|
|
64
66
|
content_multihash="placeholder", # Will be updated automatically
|
|
65
67
|
status=DocumentStatus.CAPTURED,
|
|
66
|
-
|
|
68
|
+
content_bytes=content,
|
|
67
69
|
)
|
|
68
70
|
|
|
69
|
-
# Act - save should convert
|
|
71
|
+
# Act - save should convert content_bytes to ContentStream
|
|
70
72
|
await repository.save(document)
|
|
71
73
|
|
|
72
74
|
# Assert document was saved successfully
|
|
@@ -80,10 +82,10 @@ class TestMemoryDocumentRepositoryContentString:
|
|
|
80
82
|
retrieved_content = retrieved.content.read().decode("utf-8")
|
|
81
83
|
assert retrieved_content == content
|
|
82
84
|
|
|
83
|
-
async def
|
|
85
|
+
async def test_save_document_with_content_bytes_unicode(
|
|
84
86
|
self, repository: MemoryDocumentRepository
|
|
85
87
|
) -> None:
|
|
86
|
-
"""Test saving document with unicode
|
|
88
|
+
"""Test saving document with unicode content_bytes."""
|
|
87
89
|
content = '{"title": "测试文档", "emoji": "🚀", "content": "éñ"}'
|
|
88
90
|
|
|
89
91
|
document = Document(
|
|
@@ -93,7 +95,7 @@ class TestMemoryDocumentRepositoryContentString:
|
|
|
93
95
|
size_bytes=100,
|
|
94
96
|
content_multihash="placeholder",
|
|
95
97
|
status=DocumentStatus.CAPTURED,
|
|
96
|
-
|
|
98
|
+
content_bytes=content,
|
|
97
99
|
)
|
|
98
100
|
|
|
99
101
|
await repository.save(document)
|
|
@@ -107,10 +109,10 @@ class TestMemoryDocumentRepositoryContentString:
|
|
|
107
109
|
# Note: Empty content test removed because domain model requires
|
|
108
110
|
# size_bytes > 0
|
|
109
111
|
|
|
110
|
-
async def
|
|
112
|
+
async def test_save_excludes_content_bytes_from_storage(
|
|
111
113
|
self, repository: MemoryDocumentRepository
|
|
112
114
|
) -> None:
|
|
113
|
-
"""Test that
|
|
115
|
+
"""Test that content_bytes is not stored in memory storage."""
|
|
114
116
|
content = '{"test": "data that should not be in storage"}'
|
|
115
117
|
|
|
116
118
|
document = Document(
|
|
@@ -120,7 +122,7 @@ class TestMemoryDocumentRepositoryContentString:
|
|
|
120
122
|
size_bytes=100,
|
|
121
123
|
content_multihash="placeholder",
|
|
122
124
|
status=DocumentStatus.CAPTURED,
|
|
123
|
-
|
|
125
|
+
content_bytes=content,
|
|
124
126
|
)
|
|
125
127
|
|
|
126
128
|
await repository.save(document)
|
|
@@ -129,8 +131,8 @@ class TestMemoryDocumentRepositoryContentString:
|
|
|
129
131
|
stored_document = repository.storage_dict.get("test-storage-exclusion")
|
|
130
132
|
assert stored_document is not None
|
|
131
133
|
|
|
132
|
-
# Verify
|
|
133
|
-
assert stored_document.
|
|
134
|
+
# Verify content_bytes is not in stored document
|
|
135
|
+
assert stored_document.content_bytes is None
|
|
134
136
|
|
|
135
137
|
# Verify essential fields are still present
|
|
136
138
|
assert stored_document.document_id == "test-storage-exclusion"
|
|
@@ -195,7 +197,7 @@ class TestMemoryDocumentRepositoryErrorHandling:
|
|
|
195
197
|
size_bytes=100,
|
|
196
198
|
content_multihash="test_hash",
|
|
197
199
|
status=DocumentStatus.CAPTURED,
|
|
198
|
-
|
|
200
|
+
content_bytes="test content",
|
|
199
201
|
)
|
|
200
202
|
|
|
201
203
|
async def test_save_handles_empty_filename(
|
|
@@ -210,5 +212,5 @@ class TestMemoryDocumentRepositoryErrorHandling:
|
|
|
210
212
|
size_bytes=100,
|
|
211
213
|
content_multihash="test_hash",
|
|
212
214
|
status=DocumentStatus.CAPTURED,
|
|
213
|
-
|
|
215
|
+
content_bytes="test content",
|
|
214
216
|
)
|
|
@@ -175,27 +175,7 @@ class MinioDocumentRepository(DocumentRepository, MinioRepositoryMixin):
|
|
|
175
175
|
|
|
176
176
|
try:
|
|
177
177
|
# Handle content_string conversion (only if no content provided)
|
|
178
|
-
|
|
179
|
-
# Convert content_string to ContentStream
|
|
180
|
-
assert document.content_string is not None # For MyPy
|
|
181
|
-
content_bytes = document.content_string.encode("utf-8")
|
|
182
|
-
content_stream = ContentStream(io.BytesIO(content_bytes))
|
|
183
|
-
|
|
184
|
-
# Create new document with ContentStream
|
|
185
|
-
document = document.model_copy(
|
|
186
|
-
update={
|
|
187
|
-
"content": content_stream,
|
|
188
|
-
"size_bytes": len(content_bytes),
|
|
189
|
-
}
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
self.logger.debug(
|
|
193
|
-
"Converted content_string to ContentStream",
|
|
194
|
-
extra={
|
|
195
|
-
"document_id": document.document_id,
|
|
196
|
-
"content_length": len(content_bytes),
|
|
197
|
-
},
|
|
198
|
-
)
|
|
178
|
+
document = self._normalize_document_content(document)
|
|
199
179
|
|
|
200
180
|
# Store content first and get calculated multihash
|
|
201
181
|
calculated_multihash = await self._store_content(document)
|
|
@@ -449,6 +429,29 @@ class MinioDocumentRepository(DocumentRepository, MinioRepositoryMixin):
|
|
|
449
429
|
)
|
|
450
430
|
raise
|
|
451
431
|
|
|
432
|
+
def _normalize_document_content(self, document: Document) -> Document:
|
|
433
|
+
"""Ensure document has a ContentStream in content"""
|
|
434
|
+
if document.content is not None:
|
|
435
|
+
return document
|
|
436
|
+
|
|
437
|
+
content_bytes = document.content_bytes
|
|
438
|
+
if content_bytes is not None:
|
|
439
|
+
if isinstance(content_bytes, str):
|
|
440
|
+
content_bytes = content_bytes.encode("utf-8")
|
|
441
|
+
|
|
442
|
+
stream = ContentStream(io.BytesIO(content_bytes))
|
|
443
|
+
size_bytes = len(content_bytes)
|
|
444
|
+
return document.model_copy(
|
|
445
|
+
update={
|
|
446
|
+
"content": stream,
|
|
447
|
+
"size_bytes": size_bytes,
|
|
448
|
+
}
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
raise ValueError(
|
|
452
|
+
f"Document {document.document_id} has no content, content_bytes"
|
|
453
|
+
)
|
|
454
|
+
|
|
452
455
|
def _calculate_multihash_from_stream(self, content_stream: ContentStream) -> str:
|
|
453
456
|
"""Calculate multihash from content stream."""
|
|
454
457
|
if not content_stream:
|
|
@@ -471,7 +474,7 @@ class MinioDocumentRepository(DocumentRepository, MinioRepositoryMixin):
|
|
|
471
474
|
|
|
472
475
|
# Serialize metadata (content stream and content_string excluded)
|
|
473
476
|
metadata_json = document.model_dump_json(
|
|
474
|
-
exclude={"content", "content_string"}
|
|
477
|
+
exclude={"content", "content_string", "content_bytes"}
|
|
475
478
|
).encode("utf-8")
|
|
476
479
|
|
|
477
480
|
try:
|
|
@@ -6,10 +6,13 @@ MinioClient protocol, ensuring that our protocol definition matches the
|
|
|
6
6
|
actual interface.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import pytest
|
|
9
10
|
from minio import Minio
|
|
10
11
|
|
|
11
12
|
from ..client import MinioClient
|
|
12
13
|
|
|
14
|
+
pytestmark = pytest.mark.unit
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
class TestMinioClientProtocol:
|
|
15
18
|
"""Test that the real Minio client implements our protocol."""
|
|
@@ -23,6 +23,8 @@ from julee.repositories.minio.document import MinioDocumentRepository
|
|
|
23
23
|
|
|
24
24
|
from .fake_client import FakeMinioClient
|
|
25
25
|
|
|
26
|
+
pytestmark = pytest.mark.unit
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
@pytest.fixture
|
|
28
30
|
def fake_minio_client() -> FakeMinioClient:
|
|
@@ -429,16 +431,16 @@ class TestMinioDocumentRepositoryMultihash:
|
|
|
429
431
|
assert len(multihash_result) > 0
|
|
430
432
|
|
|
431
433
|
|
|
432
|
-
class
|
|
433
|
-
"""Test
|
|
434
|
+
class TestMinioDocumentRepositoryContentBytes:
|
|
435
|
+
"""Test content_bytes functionality."""
|
|
434
436
|
|
|
435
|
-
async def
|
|
437
|
+
async def test_save_document_with_content_bytes(
|
|
436
438
|
self, repository: MinioDocumentRepository
|
|
437
439
|
) -> None:
|
|
438
|
-
"""Test saving document with
|
|
440
|
+
"""Test saving document with content_bytes (small content)."""
|
|
439
441
|
content = '{"assembled": "document", "data": "test"}'
|
|
440
442
|
|
|
441
|
-
# Create document with
|
|
443
|
+
# Create document with content_bytes
|
|
442
444
|
document = Document(
|
|
443
445
|
document_id="test-doc-content-string",
|
|
444
446
|
original_filename="assembled.json",
|
|
@@ -446,27 +448,27 @@ class TestMinioDocumentRepositoryContentString:
|
|
|
446
448
|
size_bytes=100, # Will be updated automatically
|
|
447
449
|
content_multihash="placeholder", # Will be updated automatically
|
|
448
450
|
status=DocumentStatus.CAPTURED,
|
|
449
|
-
|
|
451
|
+
content_bytes=content,
|
|
450
452
|
)
|
|
451
453
|
|
|
452
|
-
# Act - save should convert
|
|
454
|
+
# Act - save should convert content_bytes to ContentStream
|
|
453
455
|
await repository.save(document)
|
|
454
456
|
|
|
455
457
|
# Assert document was saved successfully
|
|
456
458
|
retrieved = await repository.get(document.document_id)
|
|
457
459
|
assert retrieved is not None
|
|
458
460
|
assert retrieved.content_multihash != "placeholder" # Hash was calculated
|
|
459
|
-
assert retrieved.size_bytes == len(content
|
|
461
|
+
assert retrieved.size_bytes == len(content)
|
|
460
462
|
|
|
461
463
|
# Verify content can be read
|
|
462
464
|
assert retrieved.content is not None
|
|
463
465
|
retrieved_content = retrieved.content.read().decode("utf-8")
|
|
464
466
|
assert retrieved_content == content
|
|
465
467
|
|
|
466
|
-
async def
|
|
468
|
+
async def test_save_document_with_content_bytes_unicode(
|
|
467
469
|
self, repository: MinioDocumentRepository
|
|
468
470
|
) -> None:
|
|
469
|
-
"""Test saving document with unicode
|
|
471
|
+
"""Test saving document with unicode content_bytes."""
|
|
470
472
|
content = '{"title": "测试文档", "emoji": "🚀", "content": "éñ"}'
|
|
471
473
|
|
|
472
474
|
document = Document(
|
|
@@ -476,7 +478,7 @@ class TestMinioDocumentRepositoryContentString:
|
|
|
476
478
|
size_bytes=100,
|
|
477
479
|
content_multihash="placeholder",
|
|
478
480
|
status=DocumentStatus.CAPTURED,
|
|
479
|
-
|
|
481
|
+
content_bytes=content,
|
|
480
482
|
)
|
|
481
483
|
|
|
482
484
|
await repository.save(document)
|
|
@@ -490,12 +492,12 @@ class TestMinioDocumentRepositoryContentString:
|
|
|
490
492
|
# Note: Empty content test removed because domain model requires
|
|
491
493
|
# size_bytes > 0
|
|
492
494
|
|
|
493
|
-
async def
|
|
495
|
+
async def test_save_excludes_content_bytes_from_metadata(
|
|
494
496
|
self,
|
|
495
497
|
repository: MinioDocumentRepository,
|
|
496
498
|
fake_minio_client: FakeMinioClient,
|
|
497
499
|
) -> None:
|
|
498
|
-
"""Test that
|
|
500
|
+
"""Test that content_bytes is not stored in metadata."""
|
|
499
501
|
content = '{"test": "data that should not be in metadata"}'
|
|
500
502
|
|
|
501
503
|
document = Document(
|
|
@@ -505,7 +507,7 @@ class TestMinioDocumentRepositoryContentString:
|
|
|
505
507
|
size_bytes=100,
|
|
506
508
|
content_multihash="placeholder",
|
|
507
509
|
status=DocumentStatus.CAPTURED,
|
|
508
|
-
|
|
510
|
+
content_bytes=content,
|
|
509
511
|
)
|
|
510
512
|
|
|
511
513
|
await repository.save(document)
|
|
@@ -521,8 +523,8 @@ class TestMinioDocumentRepositoryContentString:
|
|
|
521
523
|
|
|
522
524
|
metadata_dict = json.loads(metadata_json)
|
|
523
525
|
|
|
524
|
-
# Verify
|
|
525
|
-
assert "
|
|
526
|
+
# Verify content_bytes is not in stored metadata
|
|
527
|
+
assert "content_bytes" not in metadata_dict
|
|
526
528
|
assert "content" not in metadata_dict
|
|
527
529
|
|
|
528
530
|
# Verify essential fields are still present
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: julee
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: Julee - Clean architecture for accountable and transparent digital supply chains
|
|
5
|
+
Author-email: Pyx Industries <chris@pyx.io>
|
|
6
|
+
License: GPL-3.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/pyx-industries/julee
|
|
8
|
+
Project-URL: Repository, https://github.com/pyx-industries/julee
|
|
9
|
+
Project-URL: Documentation, https://github.com/pyx-industries/julee#readme
|
|
10
|
+
Project-URL: Issues, https://github.com/pyx-industries/julee/issues
|
|
11
|
+
Keywords: temporal,workflow,document-processing,ai,supply-chain
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: fastapi>=0.100.0
|
|
23
|
+
Requires-Dist: uvicorn>=0.20.0
|
|
24
|
+
Requires-Dist: python-multipart
|
|
25
|
+
Requires-Dist: fastapi-pagination>=0.12.0
|
|
26
|
+
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Requires-Dist: temporalio[pydantic]>=1.3.0
|
|
28
|
+
Requires-Dist: minio>=7.0.0
|
|
29
|
+
Requires-Dist: anthropic>=0.66.0
|
|
30
|
+
Requires-Dist: click>=0.8.0
|
|
31
|
+
Requires-Dist: Jinja2>=3.0.0
|
|
32
|
+
Requires-Dist: PyYAML>=6.0.0
|
|
33
|
+
Requires-Dist: python-magic>=0.4.27
|
|
34
|
+
Requires-Dist: multihash>=0.1.1
|
|
35
|
+
Requires-Dist: six>=1.16.0
|
|
36
|
+
Requires-Dist: jsonschema>=4.0.0
|
|
37
|
+
Requires-Dist: jsonpointer>=3.0.0
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-asyncio>=1.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-xdist>=3.5.0; extra == "dev"
|
|
43
|
+
Requires-Dist: hypothesis>=6.0.0; extra == "dev"
|
|
44
|
+
Requires-Dist: factory-boy>=3.2.0; extra == "dev"
|
|
45
|
+
Requires-Dist: mypy>=1.7.0; extra == "dev"
|
|
46
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
47
|
+
Requires-Dist: types-jsonschema; extra == "dev"
|
|
48
|
+
Requires-Dist: types-python-dateutil; extra == "dev"
|
|
49
|
+
Requires-Dist: black>=24.0.0; extra == "dev"
|
|
50
|
+
Requires-Dist: ruff>=0.5.0; extra == "dev"
|
|
51
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
52
|
+
Requires-Dist: bandit; extra == "dev"
|
|
53
|
+
Requires-Dist: pip-tools>=7.0.0; extra == "dev"
|
|
54
|
+
Requires-Dist: asyncpg; extra == "dev"
|
|
55
|
+
Provides-Extra: docs
|
|
56
|
+
Requires-Dist: sphinx>=7.0.0; extra == "docs"
|
|
57
|
+
Requires-Dist: sphinx-autobuild>=2021.3.14; extra == "docs"
|
|
58
|
+
Requires-Dist: sphinx-rtd-theme>=2.0.0; extra == "docs"
|
|
59
|
+
Requires-Dist: furo>=2023.9.10; extra == "docs"
|
|
60
|
+
Requires-Dist: sphinx-autodoc-typehints>=1.25.0; extra == "docs"
|
|
61
|
+
Requires-Dist: sphinxcontrib-mermaid>=0.9.2; extra == "docs"
|
|
62
|
+
Requires-Dist: sphinxcontrib-plantuml>=0.25; extra == "docs"
|
|
63
|
+
Requires-Dist: sphinx-autoapi>=3.0.0; extra == "docs"
|
|
64
|
+
Dynamic: license-file
|
|
65
|
+
|
|
66
|
+
# Julee
|
|
67
|
+
|
|
68
|
+
Clean architecture for accountable and transparent digital supply chains.
|
|
69
|
+
|
|
70
|
+
Julee is a Python framework for building resilient, auditable business processes using Temporal workflows. Solutions are organized around your business domain—your bounded contexts become "accelerators" that speak your business language, not framework jargon.
|
|
71
|
+
|
|
72
|
+
**Use Julee when:** processes must be done correctly, may be complex or long-running, need compliance audit trails (responsible AI, algorithmic due-diligence), or depend on unreliable services that may fail, timeout, or be rate-limited.
|
|
73
|
+
|
|
74
|
+
**Core concepts:** Accelerators are collections of pipelines that automate a business area. Pipelines are use cases wrapped with Temporal, providing durability (survives crashes), reliability (automatic retries), observability (complete execution history), and supply chain provenance (audit trails that become "digital product passports").
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install julee
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Runtime Dependencies
|
|
83
|
+
|
|
84
|
+
Julee applications require: [Temporal](https://temporal.io/) (workflow orchestration), S3-compatible object storage (e.g. MinIO), PostgreSQL (for Temporal).
|
|
85
|
+
|
|
86
|
+
## Documentation
|
|
87
|
+
|
|
88
|
+
Full documentation at [julee.readthedocs.io](https://julee.readthedocs.io), package on [PyPI](https://pypi.org/project/julee/).
|
|
89
|
+
|
|
90
|
+
## Example
|
|
91
|
+
|
|
92
|
+
This repository includes a Docker Compose example demonstrating a meeting minutes extraction system:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
cp .env.example .env # Add your ANTHROPIC_API_KEY
|
|
96
|
+
docker compose up --build
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
See the `demo-ui/` directory for the UI source.
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
GPL-3.0 — see [LICENSE](LICENSE) for details.
|