synapse-sdk 1.0.0a11__py3-none-any.whl → 2026.1.1b2__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.

Potentially problematic release.


This version of synapse-sdk might be problematic. Click here for more details.

Files changed (261) hide show
  1. synapse_sdk/__init__.py +24 -0
  2. synapse_sdk/cli/__init__.py +9 -8
  3. synapse_sdk/cli/agent/__init__.py +25 -0
  4. synapse_sdk/cli/agent/config.py +104 -0
  5. synapse_sdk/cli/agent/select.py +197 -0
  6. synapse_sdk/cli/auth.py +104 -0
  7. synapse_sdk/cli/main.py +1025 -0
  8. synapse_sdk/cli/plugin/__init__.py +58 -0
  9. synapse_sdk/cli/plugin/create.py +566 -0
  10. synapse_sdk/cli/plugin/job.py +196 -0
  11. synapse_sdk/cli/plugin/publish.py +322 -0
  12. synapse_sdk/cli/plugin/run.py +131 -0
  13. synapse_sdk/cli/plugin/test.py +200 -0
  14. synapse_sdk/clients/README.md +239 -0
  15. synapse_sdk/clients/__init__.py +5 -0
  16. synapse_sdk/clients/_template.py +266 -0
  17. synapse_sdk/clients/agent/__init__.py +84 -29
  18. synapse_sdk/clients/agent/async_ray.py +289 -0
  19. synapse_sdk/clients/agent/container.py +83 -0
  20. synapse_sdk/clients/agent/plugin.py +101 -0
  21. synapse_sdk/clients/agent/ray.py +296 -39
  22. synapse_sdk/clients/backend/__init__.py +152 -12
  23. synapse_sdk/clients/backend/annotation.py +164 -22
  24. synapse_sdk/clients/backend/core.py +101 -0
  25. synapse_sdk/clients/backend/data_collection.py +292 -0
  26. synapse_sdk/clients/backend/hitl.py +87 -0
  27. synapse_sdk/clients/backend/integration.py +374 -46
  28. synapse_sdk/clients/backend/ml.py +134 -22
  29. synapse_sdk/clients/backend/models.py +247 -0
  30. synapse_sdk/clients/base.py +538 -59
  31. synapse_sdk/clients/exceptions.py +35 -7
  32. synapse_sdk/clients/pipeline/__init__.py +5 -0
  33. synapse_sdk/clients/pipeline/client.py +636 -0
  34. synapse_sdk/clients/protocols.py +178 -0
  35. synapse_sdk/clients/utils.py +86 -8
  36. synapse_sdk/clients/validation.py +58 -0
  37. synapse_sdk/enums.py +76 -0
  38. synapse_sdk/exceptions.py +168 -0
  39. synapse_sdk/integrations/__init__.py +74 -0
  40. synapse_sdk/integrations/_base.py +119 -0
  41. synapse_sdk/integrations/_context.py +53 -0
  42. synapse_sdk/integrations/ultralytics/__init__.py +78 -0
  43. synapse_sdk/integrations/ultralytics/_callbacks.py +126 -0
  44. synapse_sdk/integrations/ultralytics/_patches.py +124 -0
  45. synapse_sdk/loggers.py +476 -95
  46. synapse_sdk/mcp/MCP.md +69 -0
  47. synapse_sdk/mcp/__init__.py +48 -0
  48. synapse_sdk/mcp/__main__.py +6 -0
  49. synapse_sdk/mcp/config.py +349 -0
  50. synapse_sdk/mcp/prompts/__init__.py +4 -0
  51. synapse_sdk/mcp/resources/__init__.py +4 -0
  52. synapse_sdk/mcp/server.py +1352 -0
  53. synapse_sdk/mcp/tools/__init__.py +6 -0
  54. synapse_sdk/plugins/__init__.py +133 -9
  55. synapse_sdk/plugins/action.py +229 -0
  56. synapse_sdk/plugins/actions/__init__.py +82 -0
  57. synapse_sdk/plugins/actions/dataset/__init__.py +37 -0
  58. synapse_sdk/plugins/actions/dataset/action.py +471 -0
  59. synapse_sdk/plugins/actions/export/__init__.py +55 -0
  60. synapse_sdk/plugins/actions/export/action.py +183 -0
  61. synapse_sdk/plugins/actions/export/context.py +59 -0
  62. synapse_sdk/plugins/actions/inference/__init__.py +84 -0
  63. synapse_sdk/plugins/actions/inference/action.py +285 -0
  64. synapse_sdk/plugins/actions/inference/context.py +81 -0
  65. synapse_sdk/plugins/actions/inference/deployment.py +322 -0
  66. synapse_sdk/plugins/actions/inference/serve.py +252 -0
  67. synapse_sdk/plugins/actions/train/__init__.py +54 -0
  68. synapse_sdk/plugins/actions/train/action.py +326 -0
  69. synapse_sdk/plugins/actions/train/context.py +57 -0
  70. synapse_sdk/plugins/actions/upload/__init__.py +49 -0
  71. synapse_sdk/plugins/actions/upload/action.py +165 -0
  72. synapse_sdk/plugins/actions/upload/context.py +61 -0
  73. synapse_sdk/plugins/config.py +98 -0
  74. synapse_sdk/plugins/context/__init__.py +109 -0
  75. synapse_sdk/plugins/context/env.py +113 -0
  76. synapse_sdk/plugins/datasets/__init__.py +113 -0
  77. synapse_sdk/plugins/datasets/converters/__init__.py +76 -0
  78. synapse_sdk/plugins/datasets/converters/base.py +347 -0
  79. synapse_sdk/plugins/datasets/converters/yolo/__init__.py +9 -0
  80. synapse_sdk/plugins/datasets/converters/yolo/from_dm.py +468 -0
  81. synapse_sdk/plugins/datasets/converters/yolo/to_dm.py +381 -0
  82. synapse_sdk/plugins/datasets/formats/__init__.py +82 -0
  83. synapse_sdk/plugins/datasets/formats/dm.py +351 -0
  84. synapse_sdk/plugins/datasets/formats/yolo.py +240 -0
  85. synapse_sdk/plugins/decorators.py +83 -0
  86. synapse_sdk/plugins/discovery.py +790 -0
  87. synapse_sdk/plugins/docs/ACTION_DEV_GUIDE.md +933 -0
  88. synapse_sdk/plugins/docs/ARCHITECTURE.md +1225 -0
  89. synapse_sdk/plugins/docs/LOGGING_SYSTEM.md +683 -0
  90. synapse_sdk/plugins/docs/OVERVIEW.md +531 -0
  91. synapse_sdk/plugins/docs/PIPELINE_GUIDE.md +145 -0
  92. synapse_sdk/plugins/docs/README.md +513 -0
  93. synapse_sdk/plugins/docs/STEP.md +656 -0
  94. synapse_sdk/plugins/enums.py +70 -10
  95. synapse_sdk/plugins/errors.py +92 -0
  96. synapse_sdk/plugins/executors/__init__.py +43 -0
  97. synapse_sdk/plugins/executors/local.py +99 -0
  98. synapse_sdk/plugins/executors/ray/__init__.py +18 -0
  99. synapse_sdk/plugins/executors/ray/base.py +282 -0
  100. synapse_sdk/plugins/executors/ray/job.py +298 -0
  101. synapse_sdk/plugins/executors/ray/jobs_api.py +511 -0
  102. synapse_sdk/plugins/executors/ray/packaging.py +137 -0
  103. synapse_sdk/plugins/executors/ray/pipeline.py +792 -0
  104. synapse_sdk/plugins/executors/ray/task.py +257 -0
  105. synapse_sdk/plugins/models/__init__.py +26 -0
  106. synapse_sdk/plugins/models/logger.py +173 -0
  107. synapse_sdk/plugins/models/pipeline.py +25 -0
  108. synapse_sdk/plugins/pipelines/__init__.py +81 -0
  109. synapse_sdk/plugins/pipelines/action_pipeline.py +417 -0
  110. synapse_sdk/plugins/pipelines/context.py +107 -0
  111. synapse_sdk/plugins/pipelines/display.py +311 -0
  112. synapse_sdk/plugins/runner.py +114 -0
  113. synapse_sdk/plugins/schemas/__init__.py +19 -0
  114. synapse_sdk/plugins/schemas/results.py +152 -0
  115. synapse_sdk/plugins/steps/__init__.py +63 -0
  116. synapse_sdk/plugins/steps/base.py +128 -0
  117. synapse_sdk/plugins/steps/context.py +90 -0
  118. synapse_sdk/plugins/steps/orchestrator.py +128 -0
  119. synapse_sdk/plugins/steps/registry.py +103 -0
  120. synapse_sdk/plugins/steps/utils/__init__.py +20 -0
  121. synapse_sdk/plugins/steps/utils/logging.py +85 -0
  122. synapse_sdk/plugins/steps/utils/timing.py +71 -0
  123. synapse_sdk/plugins/steps/utils/validation.py +68 -0
  124. synapse_sdk/plugins/templates/__init__.py +50 -0
  125. synapse_sdk/plugins/templates/base/.gitignore.j2 +26 -0
  126. synapse_sdk/plugins/templates/base/.synapseignore.j2 +11 -0
  127. synapse_sdk/plugins/templates/base/README.md.j2 +26 -0
  128. synapse_sdk/plugins/templates/base/plugin/__init__.py.j2 +1 -0
  129. synapse_sdk/plugins/templates/base/pyproject.toml.j2 +14 -0
  130. synapse_sdk/plugins/templates/base/requirements.txt.j2 +1 -0
  131. synapse_sdk/plugins/templates/custom/plugin/main.py.j2 +18 -0
  132. synapse_sdk/plugins/templates/data_validation/plugin/validate.py.j2 +32 -0
  133. synapse_sdk/plugins/templates/export/plugin/export.py.j2 +36 -0
  134. synapse_sdk/plugins/templates/neural_net/plugin/inference.py.j2 +36 -0
  135. synapse_sdk/plugins/templates/neural_net/plugin/train.py.j2 +33 -0
  136. synapse_sdk/plugins/templates/post_annotation/plugin/post_annotate.py.j2 +32 -0
  137. synapse_sdk/plugins/templates/pre_annotation/plugin/pre_annotate.py.j2 +32 -0
  138. synapse_sdk/plugins/templates/smart_tool/plugin/auto_label.py.j2 +44 -0
  139. synapse_sdk/plugins/templates/upload/plugin/upload.py.j2 +35 -0
  140. synapse_sdk/plugins/testing/__init__.py +25 -0
  141. synapse_sdk/plugins/testing/sample_actions.py +98 -0
  142. synapse_sdk/plugins/types.py +206 -0
  143. synapse_sdk/plugins/upload.py +595 -64
  144. synapse_sdk/plugins/utils.py +325 -37
  145. synapse_sdk/shared/__init__.py +25 -0
  146. synapse_sdk/utils/__init__.py +1 -0
  147. synapse_sdk/utils/auth.py +74 -0
  148. synapse_sdk/utils/file/__init__.py +58 -0
  149. synapse_sdk/utils/file/archive.py +449 -0
  150. synapse_sdk/utils/file/checksum.py +167 -0
  151. synapse_sdk/utils/file/download.py +286 -0
  152. synapse_sdk/utils/file/io.py +129 -0
  153. synapse_sdk/utils/file/requirements.py +36 -0
  154. synapse_sdk/utils/network.py +168 -0
  155. synapse_sdk/utils/storage/__init__.py +238 -0
  156. synapse_sdk/utils/storage/config.py +188 -0
  157. synapse_sdk/utils/storage/errors.py +52 -0
  158. synapse_sdk/utils/storage/providers/__init__.py +13 -0
  159. synapse_sdk/utils/storage/providers/base.py +76 -0
  160. synapse_sdk/utils/storage/providers/gcs.py +168 -0
  161. synapse_sdk/utils/storage/providers/http.py +250 -0
  162. synapse_sdk/utils/storage/providers/local.py +126 -0
  163. synapse_sdk/utils/storage/providers/s3.py +177 -0
  164. synapse_sdk/utils/storage/providers/sftp.py +208 -0
  165. synapse_sdk/utils/storage/registry.py +125 -0
  166. synapse_sdk/utils/websocket.py +99 -0
  167. synapse_sdk-2026.1.1b2.dist-info/METADATA +715 -0
  168. synapse_sdk-2026.1.1b2.dist-info/RECORD +172 -0
  169. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/WHEEL +1 -1
  170. synapse_sdk-2026.1.1b2.dist-info/licenses/LICENSE +201 -0
  171. locale/en/LC_MESSAGES/messages.mo +0 -0
  172. locale/en/LC_MESSAGES/messages.po +0 -39
  173. locale/ko/LC_MESSAGES/messages.mo +0 -0
  174. locale/ko/LC_MESSAGES/messages.po +0 -34
  175. synapse_sdk/cli/create_plugin.py +0 -10
  176. synapse_sdk/clients/agent/core.py +0 -7
  177. synapse_sdk/clients/agent/service.py +0 -15
  178. synapse_sdk/clients/backend/dataset.py +0 -51
  179. synapse_sdk/clients/ray/__init__.py +0 -6
  180. synapse_sdk/clients/ray/core.py +0 -22
  181. synapse_sdk/clients/ray/serve.py +0 -20
  182. synapse_sdk/i18n.py +0 -35
  183. synapse_sdk/plugins/categories/__init__.py +0 -0
  184. synapse_sdk/plugins/categories/base.py +0 -235
  185. synapse_sdk/plugins/categories/data_validation/__init__.py +0 -0
  186. synapse_sdk/plugins/categories/data_validation/actions/__init__.py +0 -0
  187. synapse_sdk/plugins/categories/data_validation/actions/validation.py +0 -10
  188. synapse_sdk/plugins/categories/data_validation/templates/config.yaml +0 -3
  189. synapse_sdk/plugins/categories/data_validation/templates/plugin/__init__.py +0 -0
  190. synapse_sdk/plugins/categories/data_validation/templates/plugin/validation.py +0 -5
  191. synapse_sdk/plugins/categories/decorators.py +0 -13
  192. synapse_sdk/plugins/categories/export/__init__.py +0 -0
  193. synapse_sdk/plugins/categories/export/actions/__init__.py +0 -0
  194. synapse_sdk/plugins/categories/export/actions/export.py +0 -10
  195. synapse_sdk/plugins/categories/import/__init__.py +0 -0
  196. synapse_sdk/plugins/categories/import/actions/__init__.py +0 -0
  197. synapse_sdk/plugins/categories/import/actions/import.py +0 -10
  198. synapse_sdk/plugins/categories/neural_net/__init__.py +0 -0
  199. synapse_sdk/plugins/categories/neural_net/actions/__init__.py +0 -0
  200. synapse_sdk/plugins/categories/neural_net/actions/deployment.py +0 -45
  201. synapse_sdk/plugins/categories/neural_net/actions/inference.py +0 -18
  202. synapse_sdk/plugins/categories/neural_net/actions/test.py +0 -10
  203. synapse_sdk/plugins/categories/neural_net/actions/train.py +0 -143
  204. synapse_sdk/plugins/categories/neural_net/templates/config.yaml +0 -12
  205. synapse_sdk/plugins/categories/neural_net/templates/plugin/__init__.py +0 -0
  206. synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +0 -4
  207. synapse_sdk/plugins/categories/neural_net/templates/plugin/test.py +0 -2
  208. synapse_sdk/plugins/categories/neural_net/templates/plugin/train.py +0 -14
  209. synapse_sdk/plugins/categories/post_annotation/__init__.py +0 -0
  210. synapse_sdk/plugins/categories/post_annotation/actions/__init__.py +0 -0
  211. synapse_sdk/plugins/categories/post_annotation/actions/post_annotation.py +0 -10
  212. synapse_sdk/plugins/categories/post_annotation/templates/config.yaml +0 -3
  213. synapse_sdk/plugins/categories/post_annotation/templates/plugin/__init__.py +0 -0
  214. synapse_sdk/plugins/categories/post_annotation/templates/plugin/post_annotation.py +0 -3
  215. synapse_sdk/plugins/categories/pre_annotation/__init__.py +0 -0
  216. synapse_sdk/plugins/categories/pre_annotation/actions/__init__.py +0 -0
  217. synapse_sdk/plugins/categories/pre_annotation/actions/pre_annotation.py +0 -10
  218. synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +0 -3
  219. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/__init__.py +0 -0
  220. synapse_sdk/plugins/categories/pre_annotation/templates/plugin/pre_annotation.py +0 -3
  221. synapse_sdk/plugins/categories/registry.py +0 -16
  222. synapse_sdk/plugins/categories/smart_tool/__init__.py +0 -0
  223. synapse_sdk/plugins/categories/smart_tool/actions/__init__.py +0 -0
  224. synapse_sdk/plugins/categories/smart_tool/actions/auto_label.py +0 -37
  225. synapse_sdk/plugins/categories/smart_tool/templates/config.yaml +0 -7
  226. synapse_sdk/plugins/categories/smart_tool/templates/plugin/__init__.py +0 -0
  227. synapse_sdk/plugins/categories/smart_tool/templates/plugin/auto_label.py +0 -11
  228. synapse_sdk/plugins/categories/templates.py +0 -32
  229. synapse_sdk/plugins/cli/__init__.py +0 -21
  230. synapse_sdk/plugins/cli/publish.py +0 -37
  231. synapse_sdk/plugins/cli/run.py +0 -67
  232. synapse_sdk/plugins/exceptions.py +0 -22
  233. synapse_sdk/plugins/models.py +0 -121
  234. synapse_sdk/plugins/templates/cookiecutter.json +0 -11
  235. synapse_sdk/plugins/templates/hooks/post_gen_project.py +0 -3
  236. synapse_sdk/plugins/templates/hooks/pre_prompt.py +0 -21
  237. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env +0 -24
  238. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.env.dist +0 -24
  239. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.gitignore +0 -27
  240. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/.pre-commit-config.yaml +0 -7
  241. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/README.md +0 -5
  242. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/config.yaml +0 -6
  243. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/main.py +0 -4
  244. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/plugin/__init__.py +0 -0
  245. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/pyproject.toml +0 -13
  246. synapse_sdk/plugins/templates/synapse-{{cookiecutter.plugin_code}}-plugin/requirements.txt +0 -1
  247. synapse_sdk/shared/enums.py +0 -8
  248. synapse_sdk/utils/debug.py +0 -5
  249. synapse_sdk/utils/file.py +0 -87
  250. synapse_sdk/utils/module_loading.py +0 -29
  251. synapse_sdk/utils/pydantic/__init__.py +0 -0
  252. synapse_sdk/utils/pydantic/config.py +0 -4
  253. synapse_sdk/utils/pydantic/errors.py +0 -33
  254. synapse_sdk/utils/pydantic/validators.py +0 -7
  255. synapse_sdk/utils/storage.py +0 -91
  256. synapse_sdk/utils/string.py +0 -11
  257. synapse_sdk-1.0.0a11.dist-info/LICENSE +0 -21
  258. synapse_sdk-1.0.0a11.dist-info/METADATA +0 -43
  259. synapse_sdk-1.0.0a11.dist-info/RECORD +0 -111
  260. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/entry_points.txt +0 -0
  261. {synapse_sdk-1.0.0a11.dist-info → synapse_sdk-2026.1.1b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,636 @@
1
+ """Pipeline Service Client for communicating with dev-api.
2
+
3
+ This client provides methods to interact with the pipeline orchestration
4
+ backend for registering pipelines, creating runs, and reporting progress.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import logging
11
+ from collections.abc import AsyncIterator, Iterator
12
+ from datetime import datetime
13
+ from typing import Any
14
+
15
+ import httpx
16
+
17
+ from synapse_sdk.plugins.models.logger import (
18
+ ActionProgress,
19
+ LogEntry,
20
+ PipelineProgress,
21
+ )
22
+ from synapse_sdk.plugins.models.pipeline import RunStatus
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class PipelineServiceClient:
28
+ """Client for the Pipeline Service API.
29
+
30
+ Provides methods to manage pipelines, runs, progress, checkpoints, and logs.
31
+
32
+ Attributes:
33
+ base_url: Base URL of the pipeline service.
34
+ timeout: Request timeout in seconds.
35
+
36
+ Example:
37
+ >>> client = PipelineServiceClient("http://localhost:8100")
38
+ >>> pipeline = client.create_pipeline(
39
+ ... name="YOLO Training",
40
+ ... actions=[{"name": "download", "entrypoint": "plugin.download.DownloadAction"}]
41
+ ... )
42
+ >>> run = client.create_run(pipeline["id"], params={"dataset_id": 123})
43
+ >>> client.report_progress(run["id"], current_action="download", status="running")
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ base_url: str = 'http://localhost:8100',
49
+ timeout: float = 30.0,
50
+ ):
51
+ """Initialize the pipeline service client.
52
+
53
+ Args:
54
+ base_url: Base URL of the pipeline service.
55
+ timeout: Request timeout in seconds.
56
+ """
57
+ self.base_url = base_url.rstrip('/')
58
+ self.timeout = timeout
59
+ self._client: httpx.Client | None = None
60
+
61
+ @property
62
+ def client(self) -> httpx.Client:
63
+ """Get or create the HTTP client."""
64
+ if self._client is None:
65
+ self._client = httpx.Client(
66
+ base_url=self.base_url,
67
+ timeout=self.timeout,
68
+ headers={'Content-Type': 'application/json'},
69
+ )
70
+ return self._client
71
+
72
+ def close(self) -> None:
73
+ """Close the HTTP client."""
74
+ if self._client is not None:
75
+ self._client.close()
76
+ self._client = None
77
+
78
+ def __enter__(self) -> 'PipelineServiceClient':
79
+ return self
80
+
81
+ def __exit__(self, *args: Any) -> None:
82
+ self.close()
83
+
84
+ # -------------------------------------------------------------------------
85
+ # Pipeline CRUD
86
+ # -------------------------------------------------------------------------
87
+
88
+ def create_pipeline(
89
+ self,
90
+ name: str,
91
+ actions: list[dict[str, Any]],
92
+ description: str | None = None,
93
+ ) -> dict[str, Any]:
94
+ """Create a new pipeline definition.
95
+
96
+ Args:
97
+ name: Pipeline name.
98
+ actions: List of action definitions, each with 'name' and 'entrypoint'.
99
+ description: Optional pipeline description.
100
+
101
+ Returns:
102
+ Created pipeline data including 'id'.
103
+ """
104
+ payload = {
105
+ 'name': name,
106
+ 'actions': actions,
107
+ 'description': description,
108
+ }
109
+ response = self.client.post('/api/v1/pipelines/', json=payload)
110
+ response.raise_for_status()
111
+ return response.json()
112
+
113
+ def get_pipeline(self, pipeline_id: str) -> dict[str, Any]:
114
+ """Get a pipeline by ID.
115
+
116
+ Args:
117
+ pipeline_id: Pipeline identifier.
118
+
119
+ Returns:
120
+ Pipeline data.
121
+ """
122
+ response = self.client.get(f'/api/v1/pipelines/{pipeline_id}')
123
+ response.raise_for_status()
124
+ return response.json()
125
+
126
+ def list_pipelines(self, skip: int = 0, limit: int = 100) -> list[dict[str, Any]]:
127
+ """List all pipelines.
128
+
129
+ Args:
130
+ skip: Number of records to skip.
131
+ limit: Maximum number of records to return.
132
+
133
+ Returns:
134
+ List of pipeline data.
135
+ """
136
+ response = self.client.get('/api/v1/pipelines/', params={'skip': skip, 'limit': limit})
137
+ response.raise_for_status()
138
+ return response.json()
139
+
140
+ def delete_pipeline(self, pipeline_id: str) -> None:
141
+ """Delete a pipeline.
142
+
143
+ Args:
144
+ pipeline_id: Pipeline identifier.
145
+ """
146
+ response = self.client.delete(f'/api/v1/pipelines/{pipeline_id}')
147
+ response.raise_for_status()
148
+
149
+ # -------------------------------------------------------------------------
150
+ # Run Management
151
+ # -------------------------------------------------------------------------
152
+
153
+ def create_run(
154
+ self,
155
+ pipeline_id: str,
156
+ params: dict[str, Any] | None = None,
157
+ work_dir: str | None = None,
158
+ ) -> dict[str, Any]:
159
+ """Create a new run for a pipeline.
160
+
161
+ Args:
162
+ pipeline_id: Pipeline to run.
163
+ params: Initial parameters for the run.
164
+ work_dir: Working directory path.
165
+
166
+ Returns:
167
+ Created run data including 'id' and initial progress.
168
+ """
169
+ payload: dict[str, Any] = {}
170
+ if params is not None:
171
+ payload['params'] = params
172
+ if work_dir is not None:
173
+ payload['work_dir'] = work_dir
174
+
175
+ response = self.client.post(f'/api/v1/pipelines/{pipeline_id}/runs/', json=payload)
176
+ response.raise_for_status()
177
+ return response.json()
178
+
179
+ def get_run(self, run_id: str) -> dict[str, Any]:
180
+ """Get a run by ID.
181
+
182
+ Args:
183
+ run_id: Run identifier.
184
+
185
+ Returns:
186
+ Run data including status and progress.
187
+ """
188
+ response = self.client.get(f'/api/v1/runs/{run_id}')
189
+ response.raise_for_status()
190
+ return response.json()
191
+
192
+ def list_runs(
193
+ self,
194
+ status: str | None = None,
195
+ skip: int = 0,
196
+ limit: int = 100,
197
+ ) -> list[dict[str, Any]]:
198
+ """List all runs, optionally filtered by status.
199
+
200
+ Args:
201
+ status: Filter by run status.
202
+ skip: Number of records to skip.
203
+ limit: Maximum number of records to return.
204
+
205
+ Returns:
206
+ List of run data.
207
+ """
208
+ params: dict[str, Any] = {'skip': skip, 'limit': limit}
209
+ if status:
210
+ params['status_filter'] = status
211
+ response = self.client.get('/api/v1/runs/', params=params)
212
+ response.raise_for_status()
213
+ return response.json()
214
+
215
+ def update_run(
216
+ self,
217
+ run_id: str,
218
+ status: str | None = None,
219
+ result: dict[str, Any] | None = None,
220
+ error: str | None = None,
221
+ ) -> dict[str, Any]:
222
+ """Update a run's status or result.
223
+
224
+ Args:
225
+ run_id: Run identifier.
226
+ status: New status.
227
+ result: Final result data.
228
+ error: Error message if failed.
229
+
230
+ Returns:
231
+ Updated run data.
232
+ """
233
+ payload: dict[str, Any] = {}
234
+ if status is not None:
235
+ payload['status'] = status
236
+ if result is not None:
237
+ payload['result'] = result
238
+ if error is not None:
239
+ payload['error'] = error
240
+
241
+ response = self.client.patch(f'/api/v1/runs/{run_id}', json=payload)
242
+ response.raise_for_status()
243
+ return response.json()
244
+
245
+ def delete_run(self, run_id: str) -> None:
246
+ """Delete a run.
247
+
248
+ Args:
249
+ run_id: Run identifier.
250
+ """
251
+ response = self.client.delete(f'/api/v1/runs/{run_id}')
252
+ response.raise_for_status()
253
+
254
+ # -------------------------------------------------------------------------
255
+ # Progress Reporting
256
+ # -------------------------------------------------------------------------
257
+
258
+ def report_progress(
259
+ self,
260
+ run_id: str,
261
+ current_action: str | None = None,
262
+ current_action_index: int | None = None,
263
+ status: str | None = None,
264
+ action_progress: ActionProgress | dict[str, Any] | None = None,
265
+ error: str | None = None,
266
+ ) -> dict[str, Any]:
267
+ """Report progress update for a run.
268
+
269
+ Args:
270
+ run_id: Run identifier.
271
+ current_action: Name of current action.
272
+ current_action_index: Index of current action.
273
+ status: Overall run status.
274
+ action_progress: Progress for the current action.
275
+ error: Error message if any.
276
+
277
+ Returns:
278
+ Updated run data.
279
+ """
280
+ payload: dict[str, Any] = {}
281
+ if current_action is not None:
282
+ payload['current_action'] = current_action
283
+ if current_action_index is not None:
284
+ payload['current_action_index'] = current_action_index
285
+ if status is not None:
286
+ payload['status'] = status
287
+ if action_progress is not None:
288
+ if isinstance(action_progress, ActionProgress):
289
+ payload['action_progress'] = action_progress.to_dict()
290
+ else:
291
+ payload['action_progress'] = action_progress
292
+ if error is not None:
293
+ payload['error'] = error
294
+
295
+ response = self.client.post(f'/api/v1/runs/{run_id}/progress', json=payload)
296
+ response.raise_for_status()
297
+ return response.json()
298
+
299
+ def get_progress(self, run_id: str) -> PipelineProgress:
300
+ """Get current progress for a run.
301
+
302
+ Args:
303
+ run_id: Run identifier.
304
+
305
+ Returns:
306
+ PipelineProgress object with current state.
307
+ """
308
+ response = self.client.get(f'/api/v1/runs/{run_id}/progress')
309
+ response.raise_for_status()
310
+ data = response.json()
311
+
312
+ # Map API response to PipelineProgress
313
+ return PipelineProgress(
314
+ run_id=data['run_id'],
315
+ pipeline_id=data.get('pipeline_id', ''),
316
+ status=RunStatus(data.get('status', 'pending')),
317
+ current_action=data.get('current_action'),
318
+ current_action_index=data.get('current_action_index', 0),
319
+ actions=[ActionProgress.from_dict(a) for a in data.get('progress', [])],
320
+ started_at=datetime.fromisoformat(data['started_at']) if data.get('started_at') else None,
321
+ completed_at=datetime.fromisoformat(data['completed_at']) if data.get('completed_at') else None,
322
+ error=data.get('error'),
323
+ )
324
+
325
+ # -------------------------------------------------------------------------
326
+ # Checkpoints
327
+ # -------------------------------------------------------------------------
328
+
329
+ def create_checkpoint(
330
+ self,
331
+ run_id: str,
332
+ action_name: str,
333
+ action_index: int,
334
+ status: str,
335
+ params_snapshot: dict[str, Any] | None = None,
336
+ result: dict[str, Any] | None = None,
337
+ artifacts_path: str | None = None,
338
+ ) -> dict[str, Any]:
339
+ """Create a checkpoint for a run.
340
+
341
+ Args:
342
+ run_id: Run identifier.
343
+ action_name: Name of the action.
344
+ action_index: Index of the action.
345
+ status: Action status at checkpoint.
346
+ params_snapshot: Parameters at time of checkpoint.
347
+ result: Result from the action if completed.
348
+ artifacts_path: Path to saved artifacts.
349
+
350
+ Returns:
351
+ Created checkpoint data.
352
+ """
353
+ payload = {
354
+ 'action_name': action_name,
355
+ 'action_index': action_index,
356
+ 'status': status,
357
+ 'params_snapshot': params_snapshot,
358
+ 'result': result,
359
+ 'artifacts_path': artifacts_path,
360
+ }
361
+ response = self.client.post(f'/api/v1/runs/{run_id}/checkpoints/', json=payload)
362
+ response.raise_for_status()
363
+ return response.json()
364
+
365
+ def get_checkpoints(self, run_id: str) -> list[dict[str, Any]]:
366
+ """Get all checkpoints for a run.
367
+
368
+ Args:
369
+ run_id: Run identifier.
370
+
371
+ Returns:
372
+ List of checkpoint data.
373
+ """
374
+ response = self.client.get(f'/api/v1/runs/{run_id}/checkpoints/')
375
+ response.raise_for_status()
376
+ return response.json()
377
+
378
+ def get_latest_checkpoint(self, run_id: str) -> dict[str, Any] | None:
379
+ """Get the latest checkpoint for a run.
380
+
381
+ Args:
382
+ run_id: Run identifier.
383
+
384
+ Returns:
385
+ Latest checkpoint data or None if no checkpoints.
386
+ """
387
+ try:
388
+ response = self.client.get(f'/api/v1/runs/{run_id}/checkpoints/latest')
389
+ response.raise_for_status()
390
+ return response.json()
391
+ except httpx.HTTPStatusError as e:
392
+ if e.response.status_code == 404:
393
+ return None
394
+ raise
395
+
396
+ def get_checkpoint_by_action(self, run_id: str, action_name: str) -> dict[str, Any] | None:
397
+ """Get checkpoint for a specific action.
398
+
399
+ Args:
400
+ run_id: Run identifier.
401
+ action_name: Action name.
402
+
403
+ Returns:
404
+ Checkpoint data or None if not found.
405
+ """
406
+ try:
407
+ response = self.client.get(f'/api/v1/runs/{run_id}/checkpoints/{action_name}')
408
+ response.raise_for_status()
409
+ return response.json()
410
+ except httpx.HTTPStatusError as e:
411
+ if e.response.status_code == 404:
412
+ return None
413
+ raise
414
+
415
+ # -------------------------------------------------------------------------
416
+ # Logs
417
+ # -------------------------------------------------------------------------
418
+
419
+ def append_logs(
420
+ self,
421
+ run_id: str,
422
+ entries: list[LogEntry] | list[dict[str, Any]],
423
+ ) -> list[dict[str, Any]]:
424
+ """Append log entries for a run.
425
+
426
+ Args:
427
+ run_id: Run identifier.
428
+ entries: List of log entries.
429
+
430
+ Returns:
431
+ Created log entries.
432
+ """
433
+ # Convert LogEntry objects to dicts
434
+ entry_dicts = []
435
+ for entry in entries:
436
+ if isinstance(entry, LogEntry):
437
+ entry_dicts.append({
438
+ 'message': entry.message,
439
+ 'level': entry.level.value,
440
+ 'action_name': entry.action_name,
441
+ })
442
+ else:
443
+ entry_dicts.append(entry)
444
+
445
+ payload = {'entries': entry_dicts}
446
+ response = self.client.post(f'/api/v1/runs/{run_id}/logs/', json=payload)
447
+ response.raise_for_status()
448
+ return response.json()
449
+
450
+ def get_logs(
451
+ self,
452
+ run_id: str,
453
+ action_name: str | None = None,
454
+ level: str | None = None,
455
+ since: datetime | None = None,
456
+ limit: int = 1000,
457
+ ) -> list[dict[str, Any]]:
458
+ """Get logs for a run with optional filters.
459
+
460
+ Args:
461
+ run_id: Run identifier.
462
+ action_name: Filter by action name.
463
+ level: Filter by log level.
464
+ since: Only return logs after this time.
465
+ limit: Maximum number of logs to return.
466
+
467
+ Returns:
468
+ List of log entries.
469
+ """
470
+ params: dict[str, Any] = {'limit': limit}
471
+ if action_name:
472
+ params['action_name'] = action_name
473
+ if level:
474
+ params['level'] = level
475
+ if since:
476
+ params['since'] = since.isoformat()
477
+
478
+ response = self.client.get(f'/api/v1/runs/{run_id}/logs/', params=params)
479
+ response.raise_for_status()
480
+ return response.json()
481
+
482
+ # -------------------------------------------------------------------------
483
+ # Progress Streaming
484
+ # -------------------------------------------------------------------------
485
+
486
+ def stream_progress(
487
+ self,
488
+ run_id: str,
489
+ timeout: float = 3600.0,
490
+ ) -> 'Iterator[PipelineProgress]':
491
+ """Stream progress updates via SSE.
492
+
493
+ Yields PipelineProgress objects as updates are received from the server.
494
+ Continues until the run completes, fails, or is cancelled.
495
+
496
+ Args:
497
+ run_id: Run identifier.
498
+ timeout: Maximum time to stream in seconds.
499
+
500
+ Yields:
501
+ PipelineProgress objects with current state.
502
+
503
+ Example:
504
+ >>> for progress in client.stream_progress(run_id):
505
+ ... print(f"Status: {progress.status}, Action: {progress.current_action}")
506
+ """
507
+ url = f'{self.base_url}/api/v1/runs/{run_id}/progress/stream'
508
+
509
+ with httpx.stream('GET', url, timeout=timeout) as response:
510
+ response.raise_for_status()
511
+
512
+ event_type = None
513
+ data_buffer = []
514
+
515
+ for line in response.iter_lines():
516
+ line = line.strip()
517
+
518
+ if line.startswith('event:'):
519
+ event_type = line[6:].strip()
520
+ elif line.startswith('data:'):
521
+ data_buffer.append(line[5:].strip())
522
+ elif line == '' and data_buffer:
523
+ # End of event - process it
524
+ data_str = ''.join(data_buffer)
525
+ data_buffer = []
526
+
527
+ try:
528
+ data = json.loads(data_str)
529
+ except json.JSONDecodeError:
530
+ continue
531
+
532
+ # Convert to PipelineProgress
533
+ progress = PipelineProgress(
534
+ run_id=data['run_id'],
535
+ pipeline_id=data.get('pipeline_id', ''),
536
+ status=RunStatus(data.get('status', 'pending')),
537
+ current_action=data.get('current_action'),
538
+ current_action_index=data.get('current_action_index', 0),
539
+ actions=[ActionProgress.from_dict(a) for a in data.get('progress', []) or []],
540
+ started_at=datetime.fromisoformat(data['started_at']) if data.get('started_at') else None,
541
+ completed_at=datetime.fromisoformat(data['completed_at']) if data.get('completed_at') else None,
542
+ error=data.get('error'),
543
+ )
544
+
545
+ yield progress
546
+
547
+ # Stop on terminal events
548
+ if event_type in ('completed', 'failed', 'cancelled', 'error'):
549
+ return
550
+
551
+ async def stream_progress_async(
552
+ self,
553
+ run_id: str,
554
+ timeout: float = 3600.0,
555
+ ) -> AsyncIterator[PipelineProgress]:
556
+ """Stream progress updates via SSE (async version).
557
+
558
+ Yields PipelineProgress objects as updates are received from the server.
559
+ Continues until the run completes, fails, or is cancelled.
560
+
561
+ Args:
562
+ run_id: Run identifier.
563
+ timeout: Maximum time to stream in seconds.
564
+
565
+ Yields:
566
+ PipelineProgress objects with current state.
567
+
568
+ Example:
569
+ >>> async for progress in client.stream_progress_async(run_id):
570
+ ... print(f"Status: {progress.status}, Action: {progress.current_action}")
571
+ """
572
+ url = f'{self.base_url}/api/v1/runs/{run_id}/progress/stream'
573
+
574
+ async with httpx.AsyncClient(timeout=timeout) as async_client:
575
+ async with async_client.stream('GET', url) as response:
576
+ response.raise_for_status()
577
+
578
+ event_type = None
579
+ data_buffer: list[str] = []
580
+
581
+ async for line in response.aiter_lines():
582
+ line = line.strip()
583
+
584
+ if line.startswith('event:'):
585
+ event_type = line[6:].strip()
586
+ elif line.startswith('data:'):
587
+ data_buffer.append(line[5:].strip())
588
+ elif line == '' and data_buffer:
589
+ # End of event - process it
590
+ data_str = ''.join(data_buffer)
591
+ data_buffer = []
592
+
593
+ try:
594
+ data = json.loads(data_str)
595
+ except json.JSONDecodeError:
596
+ continue
597
+
598
+ # Convert to PipelineProgress
599
+ started = data.get('started_at')
600
+ completed = data.get('completed_at')
601
+ progress = PipelineProgress(
602
+ run_id=data['run_id'],
603
+ pipeline_id=data.get('pipeline_id', ''),
604
+ status=RunStatus(data.get('status', 'pending')),
605
+ current_action=data.get('current_action'),
606
+ current_action_index=data.get('current_action_index', 0),
607
+ actions=[ActionProgress.from_dict(a) for a in data.get('progress', []) or []],
608
+ started_at=datetime.fromisoformat(started) if started else None,
609
+ completed_at=datetime.fromisoformat(completed) if completed else None,
610
+ error=data.get('error'),
611
+ )
612
+
613
+ yield progress
614
+
615
+ # Stop on terminal events
616
+ if event_type in ('completed', 'failed', 'cancelled', 'error'):
617
+ return
618
+
619
+ # -------------------------------------------------------------------------
620
+ # Health Check
621
+ # -------------------------------------------------------------------------
622
+
623
+ def health_check(self) -> bool:
624
+ """Check if the pipeline service is healthy.
625
+
626
+ Returns:
627
+ True if healthy, False otherwise.
628
+ """
629
+ try:
630
+ response = self.client.get('/health')
631
+ return response.status_code == 200
632
+ except Exception:
633
+ return False
634
+
635
+
636
+ __all__ = ['PipelineServiceClient']