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.
Files changed (157) hide show
  1. julee/api/app.py +9 -8
  2. julee/api/dependencies.py +15 -15
  3. julee/api/requests.py +10 -9
  4. julee/api/responses.py +2 -1
  5. julee/api/routers/__init__.py +5 -5
  6. julee/api/routers/assembly_specifications.py +5 -4
  7. julee/api/routers/documents.py +1 -1
  8. julee/api/routers/knowledge_service_configs.py +4 -3
  9. julee/api/routers/knowledge_service_queries.py +7 -6
  10. julee/api/routers/system.py +4 -3
  11. julee/api/routers/workflows.py +4 -5
  12. julee/api/services/system_initialization.py +6 -6
  13. julee/api/tests/routers/test_assembly_specifications.py +4 -3
  14. julee/api/tests/routers/test_documents.py +5 -4
  15. julee/api/tests/routers/test_knowledge_service_configs.py +7 -6
  16. julee/api/tests/routers/test_knowledge_service_queries.py +4 -3
  17. julee/api/tests/routers/test_system.py +5 -4
  18. julee/api/tests/routers/test_workflows.py +5 -4
  19. julee/api/tests/test_app.py +5 -4
  20. julee/api/tests/test_dependencies.py +3 -2
  21. julee/api/tests/test_requests.py +2 -1
  22. julee/contrib/__init__.py +15 -0
  23. julee/contrib/polling/__init__.py +47 -0
  24. julee/contrib/polling/domain/__init__.py +17 -0
  25. julee/contrib/polling/domain/models/__init__.py +13 -0
  26. julee/contrib/polling/domain/models/polling_config.py +39 -0
  27. julee/contrib/polling/domain/services/__init__.py +11 -0
  28. julee/contrib/polling/domain/services/poller.py +39 -0
  29. julee/contrib/polling/infrastructure/__init__.py +15 -0
  30. julee/contrib/polling/infrastructure/services/__init__.py +12 -0
  31. julee/contrib/polling/infrastructure/services/polling/__init__.py +12 -0
  32. julee/contrib/polling/infrastructure/services/polling/http/__init__.py +12 -0
  33. julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +80 -0
  34. julee/contrib/polling/infrastructure/temporal/__init__.py +20 -0
  35. julee/contrib/polling/infrastructure/temporal/activities.py +42 -0
  36. julee/contrib/polling/infrastructure/temporal/activity_names.py +20 -0
  37. julee/contrib/polling/infrastructure/temporal/proxies.py +45 -0
  38. julee/contrib/polling/tests/__init__.py +6 -0
  39. julee/contrib/polling/tests/unit/__init__.py +6 -0
  40. julee/contrib/polling/tests/unit/infrastructure/__init__.py +7 -0
  41. julee/contrib/polling/tests/unit/infrastructure/services/__init__.py +6 -0
  42. julee/contrib/polling/tests/unit/infrastructure/services/polling/__init__.py +6 -0
  43. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/__init__.py +7 -0
  44. julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +163 -0
  45. julee/docs/__init__.py +5 -0
  46. julee/docs/sphinx_hcd/__init__.py +82 -0
  47. julee/docs/sphinx_hcd/accelerators.py +1078 -0
  48. julee/docs/sphinx_hcd/apps.py +499 -0
  49. julee/docs/sphinx_hcd/config.py +148 -0
  50. julee/docs/sphinx_hcd/epics.py +448 -0
  51. julee/docs/sphinx_hcd/integrations.py +306 -0
  52. julee/docs/sphinx_hcd/journeys.py +783 -0
  53. julee/docs/sphinx_hcd/personas.py +435 -0
  54. julee/docs/sphinx_hcd/stories.py +932 -0
  55. julee/docs/sphinx_hcd/utils.py +180 -0
  56. julee/domain/models/__init__.py +5 -6
  57. julee/domain/models/assembly/assembly.py +7 -7
  58. julee/domain/models/assembly/tests/factories.py +2 -1
  59. julee/domain/models/assembly/tests/test_assembly.py +16 -13
  60. julee/domain/models/assembly_specification/assembly_specification.py +11 -10
  61. julee/domain/models/assembly_specification/knowledge_service_query.py +14 -9
  62. julee/domain/models/assembly_specification/tests/factories.py +2 -1
  63. julee/domain/models/assembly_specification/tests/test_assembly_specification.py +9 -6
  64. julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +3 -1
  65. julee/domain/models/custom_fields/content_stream.py +3 -2
  66. julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -1
  67. julee/domain/models/document/document.py +12 -10
  68. julee/domain/models/document/tests/factories.py +3 -2
  69. julee/domain/models/document/tests/test_document.py +6 -3
  70. julee/domain/models/knowledge_service_config/knowledge_service_config.py +4 -4
  71. julee/domain/models/policy/__init__.py +4 -4
  72. julee/domain/models/policy/document_policy_validation.py +17 -17
  73. julee/domain/models/policy/policy.py +12 -10
  74. julee/domain/models/policy/tests/factories.py +2 -1
  75. julee/domain/models/policy/tests/test_document_policy_validation.py +3 -1
  76. julee/domain/models/policy/tests/test_policy.py +2 -1
  77. julee/domain/repositories/__init__.py +3 -3
  78. julee/domain/repositories/assembly.py +3 -1
  79. julee/domain/repositories/assembly_specification.py +2 -0
  80. julee/domain/repositories/base.py +33 -16
  81. julee/domain/repositories/document.py +3 -1
  82. julee/domain/repositories/document_policy_validation.py +3 -1
  83. julee/domain/repositories/knowledge_service_config.py +2 -0
  84. julee/domain/repositories/knowledge_service_query.py +1 -0
  85. julee/domain/repositories/policy.py +3 -1
  86. julee/domain/use_cases/decorators.py +3 -2
  87. julee/domain/use_cases/extract_assemble_data.py +23 -13
  88. julee/domain/use_cases/initialize_system_data.py +13 -13
  89. julee/domain/use_cases/tests/test_extract_assemble_data.py +10 -10
  90. julee/domain/use_cases/tests/test_initialize_system_data.py +2 -2
  91. julee/domain/use_cases/tests/test_validate_document.py +11 -11
  92. julee/domain/use_cases/validate_document.py +26 -15
  93. julee/maintenance/__init__.py +1 -0
  94. julee/maintenance/release.py +188 -0
  95. julee/repositories/__init__.py +4 -1
  96. julee/repositories/memory/assembly.py +6 -5
  97. julee/repositories/memory/assembly_specification.py +9 -9
  98. julee/repositories/memory/base.py +12 -11
  99. julee/repositories/memory/document.py +8 -7
  100. julee/repositories/memory/document_policy_validation.py +7 -6
  101. julee/repositories/memory/knowledge_service_config.py +9 -7
  102. julee/repositories/memory/knowledge_service_query.py +9 -7
  103. julee/repositories/memory/policy.py +6 -5
  104. julee/repositories/memory/tests/test_document.py +6 -4
  105. julee/repositories/memory/tests/test_document_policy_validation.py +2 -1
  106. julee/repositories/memory/tests/test_policy.py +2 -1
  107. julee/repositories/minio/assembly.py +4 -4
  108. julee/repositories/minio/assembly_specification.py +6 -8
  109. julee/repositories/minio/client.py +22 -25
  110. julee/repositories/minio/document.py +11 -11
  111. julee/repositories/minio/document_policy_validation.py +5 -5
  112. julee/repositories/minio/knowledge_service_config.py +8 -8
  113. julee/repositories/minio/knowledge_service_query.py +6 -9
  114. julee/repositories/minio/policy.py +4 -4
  115. julee/repositories/minio/tests/fake_client.py +11 -9
  116. julee/repositories/minio/tests/test_assembly.py +3 -1
  117. julee/repositories/minio/tests/test_assembly_specification.py +2 -1
  118. julee/repositories/minio/tests/test_client_protocol.py +5 -5
  119. julee/repositories/minio/tests/test_document.py +7 -6
  120. julee/repositories/minio/tests/test_document_policy_validation.py +3 -1
  121. julee/repositories/minio/tests/test_knowledge_service_config.py +4 -2
  122. julee/repositories/minio/tests/test_knowledge_service_query.py +3 -2
  123. julee/repositories/minio/tests/test_policy.py +3 -1
  124. julee/repositories/temporal/activities.py +5 -5
  125. julee/repositories/temporal/proxies.py +5 -5
  126. julee/services/knowledge_service/__init__.py +1 -2
  127. julee/services/knowledge_service/anthropic/knowledge_service.py +8 -7
  128. julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +11 -10
  129. julee/services/knowledge_service/factory.py +8 -8
  130. julee/services/knowledge_service/knowledge_service.py +22 -18
  131. julee/services/knowledge_service/memory/knowledge_service.py +13 -12
  132. julee/services/knowledge_service/memory/test_knowledge_service.py +10 -7
  133. julee/services/knowledge_service/test_factory.py +11 -10
  134. julee/services/temporal/activities.py +10 -10
  135. julee/services/temporal/proxies.py +2 -2
  136. julee/util/domain.py +6 -6
  137. julee/util/repos/minio/file_storage.py +8 -9
  138. julee/util/repos/temporal/client_proxies/file_storage.py +3 -4
  139. julee/util/repos/temporal/data_converter.py +6 -6
  140. julee/util/repos/temporal/minio_file_storage.py +1 -1
  141. julee/util/repos/temporal/proxies/file_storage.py +2 -3
  142. julee/util/repositories.py +6 -7
  143. julee/util/temporal/activities.py +1 -1
  144. julee/util/temporal/decorators.py +28 -33
  145. julee/util/tests/test_decorators.py +13 -15
  146. julee/util/validation/repository.py +3 -3
  147. julee/util/validation/type_guards.py +12 -11
  148. julee/worker.py +9 -8
  149. julee/workflows/__init__.py +2 -2
  150. julee/workflows/extract_assemble.py +2 -1
  151. julee/workflows/validate_document.py +3 -2
  152. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/METADATA +4 -1
  153. julee-0.1.3.dist-info/RECORD +197 -0
  154. julee-0.1.1.dist-info/RECORD +0 -161
  155. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/WHEEL +0 -0
  156. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/licenses/LICENSE +0 -0
  157. {julee-0.1.1.dist-info → julee-0.1.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,306 @@
1
+ """Sphinx extension for integrations.
2
+
3
+ Scans integration manifests for definitions and provides directives to render
4
+ integration information with external dependencies.
5
+
6
+ Provides directives:
7
+ - define-integration: Render integration info from YAML
8
+ - integration-index: Generate index with architecture diagram
9
+ """
10
+
11
+ import os
12
+ import yaml
13
+ from pathlib import Path
14
+ from docutils import nodes
15
+ from sphinx.util.docutils import SphinxDirective
16
+ from sphinx.util import logging
17
+
18
+ from .config import get_config
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ # Global registry populated at build init
23
+ _integration_registry: dict = {}
24
+
25
+
26
+ def get_integration_registry() -> dict:
27
+ """Get the integration registry."""
28
+ return _integration_registry
29
+
30
+
31
+ def get_documented_integrations(env) -> set:
32
+ """Get documented integrations set from env, creating if needed."""
33
+ if not hasattr(env, 'documented_integrations'):
34
+ env.documented_integrations = set()
35
+ return env.documented_integrations
36
+
37
+
38
+ def scan_integration_manifests(app):
39
+ """Scan integration manifests and build the registry."""
40
+ global _integration_registry
41
+ _integration_registry = {}
42
+
43
+ config = get_config()
44
+ integrations_dir = config.get_path('integration_manifests')
45
+
46
+ if not integrations_dir.exists():
47
+ logger.info(f"Integrations directory not found at {integrations_dir} - no integration manifests to index")
48
+ return
49
+
50
+ for int_dir in integrations_dir.iterdir():
51
+ if not int_dir.is_dir() or int_dir.name.startswith('_'):
52
+ continue
53
+
54
+ manifest_path = int_dir / "integration.yaml"
55
+ if not manifest_path.exists():
56
+ continue
57
+
58
+ module_name = int_dir.name
59
+
60
+ try:
61
+ with open(manifest_path) as f:
62
+ manifest = yaml.safe_load(f)
63
+ except Exception as e:
64
+ logger.warning(f"Could not read {manifest_path}: {e}")
65
+ continue
66
+
67
+ slug = manifest.get('slug', module_name.replace('_', '-'))
68
+ _integration_registry[slug] = {
69
+ 'slug': slug,
70
+ 'module': module_name,
71
+ 'name': manifest.get('name', slug.replace('-', ' ').title()),
72
+ 'description': manifest.get('description', '').strip(),
73
+ 'direction': manifest.get('direction', 'bidirectional'),
74
+ 'depends_on': manifest.get('depends_on', []),
75
+ 'manifest_path': str(manifest_path),
76
+ }
77
+
78
+ logger.info(f"Indexed {len(_integration_registry)} integrations from manifests")
79
+
80
+
81
+ def validate_integrations(app, env):
82
+ """Validate integration coverage after all documents are read."""
83
+ documented = get_documented_integrations(env)
84
+
85
+ # Check for integrations without documentation
86
+ for slug in _integration_registry:
87
+ if slug not in documented:
88
+ logger.warning(
89
+ f"Integration '{slug}' has no docs page. "
90
+ f"Create integrations/{slug}.rst with '.. define-integration:: {slug}'"
91
+ )
92
+
93
+ # Check for documented integrations without manifests
94
+ for slug in documented:
95
+ if slug not in _integration_registry:
96
+ logger.warning(
97
+ f"Integration '{slug}' documented but has no manifest. "
98
+ f"Create integration.yaml in the appropriate directory"
99
+ )
100
+
101
+
102
+ class DefineIntegrationDirective(SphinxDirective):
103
+ """Render integration info from YAML manifest.
104
+
105
+ Usage::
106
+
107
+ .. define-integration:: pilot-data-collection
108
+ """
109
+
110
+ required_arguments = 1
111
+
112
+ def run(self):
113
+ slug = self.arguments[0]
114
+ get_documented_integrations(self.env).add(slug)
115
+
116
+ node = DefineIntegrationPlaceholder()
117
+ node['integration_slug'] = slug
118
+ return [node]
119
+
120
+
121
+ class DefineIntegrationPlaceholder(nodes.General, nodes.Element):
122
+ """Placeholder node for define-integration, replaced at doctree-resolved."""
123
+ pass
124
+
125
+
126
+ class IntegrationIndexDirective(SphinxDirective):
127
+ """Generate integration index with architecture diagram.
128
+
129
+ Usage::
130
+
131
+ .. integration-index::
132
+ """
133
+
134
+ def run(self):
135
+ return [IntegrationIndexPlaceholder()]
136
+
137
+
138
+ class IntegrationIndexPlaceholder(nodes.General, nodes.Element):
139
+ """Placeholder node for integration-index, replaced at doctree-resolved."""
140
+ pass
141
+
142
+
143
+ def build_integration_content(slug, docname):
144
+ """Build content nodes for an integration page."""
145
+ from sphinx.addnodes import seealso
146
+
147
+ if slug not in _integration_registry:
148
+ para = nodes.paragraph()
149
+ para += nodes.problematic(text=f"Integration '{slug}' not found")
150
+ return [para]
151
+
152
+ data = _integration_registry[slug]
153
+ result_nodes = []
154
+
155
+ # Description
156
+ if data['description']:
157
+ desc_para = nodes.paragraph()
158
+ desc_para += nodes.Text(data['description'])
159
+ result_nodes.append(desc_para)
160
+
161
+ # Seealso with metadata
162
+ seealso_node = seealso()
163
+
164
+ # Direction
165
+ direction_labels = {
166
+ 'inbound': 'Inbound (data source)',
167
+ 'outbound': 'Outbound (data sink)',
168
+ 'bidirectional': 'Bidirectional',
169
+ }
170
+ dir_para = nodes.paragraph()
171
+ dir_para += nodes.strong(text="Direction: ")
172
+ dir_para += nodes.Text(direction_labels.get(data['direction'], data['direction']))
173
+ seealso_node += dir_para
174
+
175
+ # Module
176
+ mod_para = nodes.paragraph()
177
+ mod_para += nodes.strong(text="Module: ")
178
+ mod_para += nodes.literal(text=f"integrations.{data['module']}")
179
+ seealso_node += mod_para
180
+
181
+ # External dependencies
182
+ if data['depends_on']:
183
+ deps_para = nodes.paragraph()
184
+ deps_para += nodes.strong(text="Depends On: ")
185
+ for i, dep in enumerate(data['depends_on']):
186
+ if dep.get('url'):
187
+ ref = nodes.reference("", "", refuri=dep['url'])
188
+ ref += nodes.Text(dep['name'])
189
+ deps_para += ref
190
+ else:
191
+ deps_para += nodes.Text(dep['name'])
192
+ if i < len(data['depends_on']) - 1:
193
+ deps_para += nodes.Text(", ")
194
+ seealso_node += deps_para
195
+
196
+ result_nodes.append(seealso_node)
197
+ return result_nodes
198
+
199
+
200
+ def build_integration_index(docname):
201
+ """Build integration index with architecture diagram."""
202
+ from sphinxcontrib.plantuml import plantuml
203
+
204
+ result_nodes = []
205
+
206
+ if not _integration_registry:
207
+ para = nodes.paragraph()
208
+ para += nodes.emphasis(text="No integrations defined")
209
+ return [para]
210
+
211
+ # Build PlantUML diagram
212
+ lines = [
213
+ "@startuml",
214
+ "skinparam componentStyle rectangle",
215
+ "skinparam defaultTextAlignment center",
216
+ "skinparam component {",
217
+ " BackgroundColor<<integration>> LightBlue",
218
+ " BackgroundColor<<external>> LightYellow",
219
+ " BackgroundColor<<core>> LightGreen",
220
+ "}",
221
+ "",
222
+ 'title "Integration Architecture"',
223
+ "",
224
+ "' Core system",
225
+ 'component "Julee\\nSolution" as core <<core>>',
226
+ "",
227
+ "' Integrations and their external dependencies",
228
+ ]
229
+
230
+ for slug, data in sorted(_integration_registry.items()):
231
+ int_id = slug.replace("-", "_")
232
+ lines.append(f'component "{data["name"]}" as {int_id} <<integration>>')
233
+
234
+ for dep in data.get('depends_on', []):
235
+ dep_id = dep['name'].lower().replace(" ", "_").replace("-", "_")
236
+ dep_label = dep['name']
237
+ if dep.get('description'):
238
+ dep_label += f"\\n({dep['description']})"
239
+ lines.append(f'component "{dep_label}" as {dep_id} <<external>>')
240
+
241
+ lines.append("")
242
+ lines.append("' Relationships")
243
+
244
+ for slug, data in sorted(_integration_registry.items()):
245
+ int_id = slug.replace("-", "_")
246
+ direction = data.get('direction', 'bidirectional')
247
+
248
+ # Core to/from integration
249
+ if direction == 'inbound':
250
+ lines.append(f'{int_id} --> core')
251
+ elif direction == 'outbound':
252
+ lines.append(f'core --> {int_id}')
253
+ else:
254
+ lines.append(f'core <--> {int_id}')
255
+
256
+ # Integration to external dependencies
257
+ for dep in data.get('depends_on', []):
258
+ dep_id = dep['name'].lower().replace(" ", "_").replace("-", "_")
259
+ if direction == 'inbound':
260
+ lines.append(f'{dep_id} --> {int_id}')
261
+ elif direction == 'outbound':
262
+ lines.append(f'{int_id} --> {dep_id}')
263
+ else:
264
+ lines.append(f'{int_id} <--> {dep_id}')
265
+
266
+ lines.append("")
267
+ lines.append("@enduml")
268
+
269
+ puml_source = "\n".join(lines)
270
+ node = plantuml(puml_source)
271
+ node['uml'] = puml_source
272
+ node['incdir'] = os.path.dirname(docname)
273
+ node['filename'] = os.path.basename(docname) + ".rst"
274
+ result_nodes.append(node)
275
+
276
+ return result_nodes
277
+
278
+
279
+ def process_integration_placeholders(app, doctree, docname):
280
+ """Replace integration placeholders after all documents are read."""
281
+ for node in doctree.traverse(DefineIntegrationPlaceholder):
282
+ slug = node['integration_slug']
283
+ content = build_integration_content(slug, docname)
284
+ node.replace_self(content)
285
+
286
+ for node in doctree.traverse(IntegrationIndexPlaceholder):
287
+ content = build_integration_index(docname)
288
+ node.replace_self(content)
289
+
290
+
291
+ def setup(app):
292
+ app.connect("builder-inited", scan_integration_manifests)
293
+ app.connect("env-check-consistency", validate_integrations)
294
+ app.connect("doctree-resolved", process_integration_placeholders)
295
+
296
+ app.add_directive("define-integration", DefineIntegrationDirective)
297
+ app.add_directive("integration-index", IntegrationIndexDirective)
298
+
299
+ app.add_node(DefineIntegrationPlaceholder)
300
+ app.add_node(IntegrationIndexPlaceholder)
301
+
302
+ return {
303
+ "version": "1.0",
304
+ "parallel_read_safe": False,
305
+ "parallel_write_safe": True,
306
+ }