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.
Files changed (82) hide show
  1. julee/__init__.py +1 -1
  2. julee/api/tests/routers/test_assembly_specifications.py +2 -0
  3. julee/api/tests/routers/test_documents.py +8 -6
  4. julee/api/tests/routers/test_knowledge_service_configs.py +2 -0
  5. julee/api/tests/routers/test_knowledge_service_queries.py +2 -0
  6. julee/api/tests/routers/test_system.py +2 -0
  7. julee/api/tests/routers/test_workflows.py +2 -0
  8. julee/api/tests/test_app.py +2 -0
  9. julee/api/tests/test_dependencies.py +2 -0
  10. julee/api/tests/test_requests.py +2 -0
  11. julee/contrib/polling/__init__.py +22 -19
  12. julee/contrib/polling/apps/__init__.py +17 -0
  13. julee/contrib/polling/apps/worker/__init__.py +17 -0
  14. julee/contrib/polling/apps/worker/pipelines.py +288 -0
  15. julee/contrib/polling/domain/__init__.py +7 -9
  16. julee/contrib/polling/domain/models/__init__.py +6 -7
  17. julee/contrib/polling/domain/models/polling_config.py +18 -1
  18. julee/contrib/polling/domain/services/__init__.py +6 -5
  19. julee/contrib/polling/domain/services/poller.py +1 -1
  20. julee/contrib/polling/infrastructure/__init__.py +9 -8
  21. julee/contrib/polling/infrastructure/services/__init__.py +6 -5
  22. julee/contrib/polling/infrastructure/services/polling/__init__.py +6 -5
  23. julee/contrib/polling/infrastructure/services/polling/http/__init__.py +6 -5
  24. julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +5 -2
  25. julee/contrib/polling/infrastructure/temporal/__init__.py +12 -12
  26. julee/contrib/polling/infrastructure/temporal/activities.py +1 -1
  27. julee/contrib/polling/infrastructure/temporal/manager.py +291 -0
  28. julee/contrib/polling/infrastructure/temporal/proxies.py +1 -1
  29. julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +580 -0
  30. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +40 -2
  31. julee/contrib/polling/tests/unit/infrastructure/temporal/__init__.py +7 -0
  32. julee/contrib/polling/tests/unit/infrastructure/temporal/test_manager.py +475 -0
  33. julee/docs/sphinx_hcd/__init__.py +4 -10
  34. julee/docs/sphinx_hcd/accelerators.py +277 -180
  35. julee/docs/sphinx_hcd/apps.py +78 -59
  36. julee/docs/sphinx_hcd/config.py +16 -16
  37. julee/docs/sphinx_hcd/epics.py +47 -42
  38. julee/docs/sphinx_hcd/integrations.py +53 -49
  39. julee/docs/sphinx_hcd/journeys.py +124 -110
  40. julee/docs/sphinx_hcd/personas.py +75 -53
  41. julee/docs/sphinx_hcd/stories.py +99 -71
  42. julee/docs/sphinx_hcd/utils.py +23 -18
  43. julee/domain/models/assembly/tests/test_assembly.py +2 -0
  44. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +2 -0
  45. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +2 -0
  46. julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -0
  47. julee/domain/models/document/document.py +12 -21
  48. julee/domain/models/document/tests/test_document.py +16 -34
  49. julee/domain/models/policy/tests/test_document_policy_validation.py +2 -0
  50. julee/domain/models/policy/tests/test_policy.py +2 -0
  51. julee/domain/use_cases/extract_assemble_data.py +1 -1
  52. julee/domain/use_cases/initialize_system_data.py +75 -21
  53. julee/domain/use_cases/tests/test_extract_assemble_data.py +2 -0
  54. julee/domain/use_cases/tests/test_initialize_system_data.py +2 -0
  55. julee/domain/use_cases/tests/test_validate_document.py +2 -0
  56. julee/fixtures/documents.yaml +4 -43
  57. julee/fixtures/knowledge_service_queries.yaml +9 -0
  58. julee/maintenance/release.py +90 -30
  59. julee/repositories/memory/document.py +19 -13
  60. julee/repositories/memory/tests/test_document.py +20 -18
  61. julee/repositories/memory/tests/test_document_policy_validation.py +2 -0
  62. julee/repositories/memory/tests/test_policy.py +2 -0
  63. julee/repositories/minio/document.py +25 -22
  64. julee/repositories/minio/tests/test_assembly.py +2 -0
  65. julee/repositories/minio/tests/test_assembly_specification.py +2 -0
  66. julee/repositories/minio/tests/test_client_protocol.py +3 -0
  67. julee/repositories/minio/tests/test_document.py +18 -16
  68. julee/repositories/minio/tests/test_document_policy_validation.py +2 -0
  69. julee/repositories/minio/tests/test_knowledge_service_config.py +2 -0
  70. julee/repositories/minio/tests/test_knowledge_service_query.py +2 -0
  71. julee/repositories/minio/tests/test_policy.py +2 -0
  72. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +2 -0
  73. julee/services/knowledge_service/memory/test_knowledge_service.py +2 -0
  74. julee/services/knowledge_service/test_factory.py +2 -0
  75. julee/util/tests/test_decorators.py +2 -0
  76. julee-0.1.5.dist-info/METADATA +103 -0
  77. {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/RECORD +80 -74
  78. julee/fixtures/assembly_specifications.yaml +0 -70
  79. julee-0.1.3.dist-info/METADATA +0 -198
  80. {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/WHEEL +0 -0
  81. {julee-0.1.3.dist-info → julee-0.1.5.dist-info}/licenses/LICENSE +0 -0
  82. {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 content_string, it will be converted to a
64
- ContentStream and the content hash will be calculated automatically.
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 content_string
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.content_string is not None:
74
- # Convert content_string to ContentStream
75
- assert document.content_string is not None # For MyPy
76
- content_bytes = document.content_string.encode("utf-8")
77
- content_stream = ContentStream(io.BytesIO(content_bytes))
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(content_bytes).hexdigest()
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(content_bytes),
93
+ "size_bytes": len(raw_bytes),
88
94
  }
89
95
  )
90
96
 
91
97
  self.logger.debug(
92
- "Converted content_string to ContentStream for document save",
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(content_bytes),
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={"content_string": None})
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 content_string.
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 TestMemoryDocumentRepositoryContentString:
50
- """Test content_string functionality."""
51
+ class TestMemoryDocumentRepositoryContentBytes:
52
+ """Test content_bytes functionality."""
51
53
 
52
- async def test_save_document_with_content_string(
54
+ async def test_save_document_with_content_bytes(
53
55
  self, repository: MemoryDocumentRepository
54
56
  ) -> None:
55
- """Test saving document with content_string (small content)."""
57
+ """Test saving document with content_bytes."""
56
58
  content = '{"assembled": "document", "data": "test"}'
57
59
 
58
- # Create document with content_string
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
- content_string=content,
68
+ content_bytes=content,
67
69
  )
68
70
 
69
- # Act - save should convert content_string to ContentStream
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 test_save_document_with_content_string_unicode(
85
+ async def test_save_document_with_content_bytes_unicode(
84
86
  self, repository: MemoryDocumentRepository
85
87
  ) -> None:
86
- """Test saving document with unicode content_string."""
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
- content_string=content,
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 test_save_excludes_content_string_from_storage(
112
+ async def test_save_excludes_content_bytes_from_storage(
111
113
  self, repository: MemoryDocumentRepository
112
114
  ) -> None:
113
- """Test that content_string is not stored in memory storage."""
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
- content_string=content,
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 content_string is not in stored document
133
- assert stored_document.content_string is None
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
- content_string="test content",
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
- content_string="test content",
215
+ content_bytes="test content",
214
216
  )
@@ -19,6 +19,8 @@ from julee.repositories.memory.document_policy_validation import (
19
19
  MemoryDocumentPolicyValidationRepository,
20
20
  )
21
21
 
22
+ pytestmark = pytest.mark.unit
23
+
22
24
 
23
25
  @pytest.fixture
24
26
  def validation_repo() -> MemoryDocumentPolicyValidationRepository:
@@ -13,6 +13,8 @@ import pytest
13
13
  from julee.domain.models.policy import Policy, PolicyStatus
14
14
  from julee.repositories.memory.policy import MemoryPolicyRepository
15
15
 
16
+ pytestmark = pytest.mark.unit
17
+
16
18
 
17
19
  @pytest.fixture
18
20
  def policy_repo() -> MemoryPolicyRepository:
@@ -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
- if document.content_string is not None:
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:
@@ -15,6 +15,8 @@ from julee.repositories.minio.assembly import MinioAssemblyRepository
15
15
 
16
16
  from .fake_client import FakeMinioClient
17
17
 
18
+ pytestmark = pytest.mark.unit
19
+
18
20
 
19
21
  @pytest.fixture
20
22
  def fake_client() -> FakeMinioClient:
@@ -20,6 +20,8 @@ from julee.repositories.minio.assembly_specification import (
20
20
 
21
21
  from .fake_client import FakeMinioClient
22
22
 
23
+ pytestmark = pytest.mark.unit
24
+
23
25
 
24
26
  @pytest.fixture
25
27
  def fake_client() -> FakeMinioClient:
@@ -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 TestMinioDocumentRepositoryContentString:
433
- """Test content_string functionality."""
434
+ class TestMinioDocumentRepositoryContentBytes:
435
+ """Test content_bytes functionality."""
434
436
 
435
- async def test_save_document_with_content_string(
437
+ async def test_save_document_with_content_bytes(
436
438
  self, repository: MinioDocumentRepository
437
439
  ) -> None:
438
- """Test saving document with content_string (small content)."""
440
+ """Test saving document with content_bytes (small content)."""
439
441
  content = '{"assembled": "document", "data": "test"}'
440
442
 
441
- # Create document with content_string
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
- content_string=content,
451
+ content_bytes=content,
450
452
  )
451
453
 
452
- # Act - save should convert content_string to ContentStream
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.encode("utf-8"))
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 test_save_document_with_content_string_unicode(
468
+ async def test_save_document_with_content_bytes_unicode(
467
469
  self, repository: MinioDocumentRepository
468
470
  ) -> None:
469
- """Test saving document with unicode content_string."""
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
- content_string=content,
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 test_save_excludes_content_string_from_metadata(
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 content_string is not stored in metadata."""
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
- content_string=content,
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 content_string is not in stored metadata
525
- assert "content_string" not in metadata_dict
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
@@ -21,6 +21,8 @@ from julee.repositories.minio.document_policy_validation import (
21
21
 
22
22
  from .fake_client import FakeMinioClient
23
23
 
24
+ pytestmark = pytest.mark.unit
25
+
24
26
 
25
27
  @pytest.fixture
26
28
  def fake_client() -> FakeMinioClient:
@@ -20,6 +20,8 @@ from julee.repositories.minio.knowledge_service_config import (
20
20
 
21
21
  from .fake_client import FakeMinioClient
22
22
 
23
+ pytestmark = pytest.mark.unit
24
+
23
25
 
24
26
  @pytest.fixture
25
27
  def fake_client() -> FakeMinioClient:
@@ -19,6 +19,8 @@ from julee.repositories.minio.knowledge_service_query import (
19
19
 
20
20
  from .fake_client import FakeMinioClient
21
21
 
22
+ pytestmark = pytest.mark.unit
23
+
22
24
 
23
25
  @pytest.fixture
24
26
  def fake_client() -> FakeMinioClient:
@@ -15,6 +15,8 @@ from julee.repositories.minio.policy import MinioPolicyRepository
15
15
 
16
16
  from .fake_client import FakeMinioClient
17
17
 
18
+ pytestmark = pytest.mark.unit
19
+
18
20
 
19
21
  @pytest.fixture
20
22
  def fake_client() -> FakeMinioClient:
@@ -27,6 +27,8 @@ from julee.services.knowledge_service.anthropic import (
27
27
  knowledge_service as anthropic_ks_module,
28
28
  )
29
29
 
30
+ pytestmark = pytest.mark.unit
31
+
30
32
 
31
33
  @pytest.fixture
32
34
  def test_document() -> Document:
@@ -23,6 +23,8 @@ from julee.domain.models.knowledge_service_config import (
23
23
  from ..knowledge_service import QueryResult
24
24
  from .knowledge_service import MemoryKnowledgeService
25
25
 
26
+ pytestmark = pytest.mark.unit
27
+
26
28
 
27
29
  @pytest.fixture
28
30
  def test_document() -> Document:
@@ -26,6 +26,8 @@ from julee.services.knowledge_service.factory import (
26
26
  knowledge_service_factory,
27
27
  )
28
28
 
29
+ pytestmark = pytest.mark.unit
30
+
29
31
 
30
32
  @pytest.fixture
31
33
  def test_document() -> Document:
@@ -34,6 +34,8 @@ from julee.util.temporal.decorators import (
34
34
  temporal_workflow_proxy,
35
35
  )
36
36
 
37
+ pytestmark = pytest.mark.unit
38
+
37
39
 
38
40
  @runtime_checkable
39
41
  class MockBaseRepositoryProtocol(Protocol):
@@ -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.