tangle-cli 0.0.1a3__tar.gz → 0.1.1__tar.gz

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 (75) hide show
  1. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/PKG-INFO +8 -4
  2. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/README.md +6 -2
  3. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/__init__.py +1 -1
  4. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_run_manager.py +36 -6
  5. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_runner.py +2 -0
  6. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_runs_cli.py +17 -1
  7. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/pyproject.toml +3 -2
  8. tangle_cli-0.1.1/skills/tangent/OSS-CONVENTIONS.md +471 -0
  9. tangle_cli-0.1.1/skills/tangent/PORT-README.md +79 -0
  10. tangle_cli-0.1.1/skills/tangent/SKILL.md +124 -0
  11. tangle_cli-0.1.1/skills/tangent/agents/auth-wizard.md +150 -0
  12. tangle_cli-0.1.1/skills/tangent/agents/builder.md +252 -0
  13. tangle_cli-0.1.1/skills/tangent/agents/debugger.md +142 -0
  14. tangle_cli-0.1.1/skills/tangent/agents/reporter.md +103 -0
  15. tangle_cli-0.1.1/skills/tangent/agents/researcher.md +197 -0
  16. tangle_cli-0.1.1/skills/tangent/agents/reviewer.md +106 -0
  17. tangle_cli-0.1.1/skills/tangent/agents/scenario-builder.md +451 -0
  18. tangle_cli-0.1.1/skills/tangent/references/data-sources.md +334 -0
  19. tangle_cli-0.1.1/skills/tangent/references/event-log.md +110 -0
  20. tangle_cli-0.1.1/skills/tangent/references/example-scenarios/01-mslr-ranking.md +165 -0
  21. tangle_cli-0.1.1/skills/tangent/references/example-scenarios/02-text-classification.md +147 -0
  22. tangle_cli-0.1.1/skills/tangent/references/example-scenarios/INDEX.md +48 -0
  23. tangle_cli-0.1.1/skills/tangent/references/iterating-on-runs.md +56 -0
  24. tangle_cli-0.1.1/skills/tangent/references/knowledge-corpus.md +128 -0
  25. tangle_cli-0.1.1/skills/tangent/references/secrets.md +347 -0
  26. tangle_cli-0.1.1/skills/tangent/references/setup.md +157 -0
  27. tangle_cli-0.1.1/skills/tangent/references/step-0-initialize.md +107 -0
  28. tangle_cli-0.1.1/skills/tangent/references/step-1-analyze.md +91 -0
  29. tangle_cli-0.1.1/skills/tangent/references/step-2-hypothesize.md +42 -0
  30. tangle_cli-0.1.1/skills/tangent/references/step-3-submit.md +174 -0
  31. tangle_cli-0.1.1/skills/tangent/references/step-4-monitor.md +123 -0
  32. tangle_cli-0.1.1/skills/tangent/references/step-5-evaluate.md +53 -0
  33. tangle_cli-0.1.1/skills/tangent/references/step-6-synthesize.md +56 -0
  34. tangle_cli-0.1.1/skills/tangent/references/step-7-decide.md +79 -0
  35. tangle_cli-0.1.1/skills/tangent/references/tangle-tools.md +410 -0
  36. tangle_cli-0.1.1/skills/tangent/references/uploading-artifacts.md +229 -0
  37. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/api_cli.py +0 -0
  38. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/api_schema.py +0 -0
  39. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/api_transport.py +0 -0
  40. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/args_container.py +0 -0
  41. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/artifacts.py +0 -0
  42. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/artifacts_cli.py +0 -0
  43. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/cli.py +0 -0
  44. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/cli_helpers.py +0 -0
  45. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/cli_options.py +0 -0
  46. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/client.py +0 -0
  47. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/component_from_func.py +0 -0
  48. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/component_generator.py +0 -0
  49. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/component_inspector.py +0 -0
  50. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/component_publisher.py +0 -0
  51. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/components_cli.py +0 -0
  52. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/dynamic_discovery_client.py +0 -0
  53. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/generated_model_extensions.py +0 -0
  54. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/handler.py +0 -0
  55. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/hydration_trust.py +0 -0
  56. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/logger.py +0 -0
  57. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/models.py +0 -0
  58. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/module_bundler.py +0 -0
  59. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/openapi/__init__.py +0 -0
  60. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/openapi/codegen.py +0 -0
  61. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/openapi/parser.py +0 -0
  62. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_dehydrator.py +0 -0
  63. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_hydrator.py +0 -0
  64. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_run_annotations.py +0 -0
  65. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_run_details.py +0 -0
  66. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipeline_run_search.py +0 -0
  67. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipelines.py +0 -0
  68. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/pipelines_cli.py +0 -0
  69. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/published_components_cli.py +0 -0
  70. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/py.typed +0 -0
  71. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/quickstart.py +0 -0
  72. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/secrets.py +0 -0
  73. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/secrets_cli.py +0 -0
  74. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/utils.py +0 -0
  75. {tangle_cli-0.0.1a3 → tangle_cli-0.1.1}/packages/tangle-cli/src/tangle_cli/version_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: tangle-cli
3
- Version: 0.0.1a3
3
+ Version: 0.1.1
4
4
  Summary: CLI for Tangle, the open-source ML pipeline orchestration platform
5
5
  Author: Alexey Volkov, Tangle authors
6
6
  Author-email: Alexey Volkov <alexey.volkov@ark-kun.com>
@@ -13,7 +13,7 @@ Requires-Dist: platformdirs>=4.10.0
13
13
  Requires-Dist: pydantic>=2.0
14
14
  Requires-Dist: pyyaml>=6.0
15
15
  Requires-Dist: requests>=2.32.0
16
- Requires-Dist: tangle-api==0.0.1a3
16
+ Requires-Dist: tangle-api==0.1.0
17
17
  Requires-Dist: tomli>=2.0 ; python_full_version < '3.11'
18
18
  Requires-Python: >=3.10
19
19
  Project-URL: Homepage, https://tangleml.com
@@ -126,7 +126,11 @@ uv run tangle api --help
126
126
  uv run tangle sdk pipelines validate pipeline.yaml
127
127
  ```
128
128
 
129
- Custom API/codegen users can still run codegen from the fully capable install; generating bindings does not require removing the official `tangle-api` package. For project-local generated APIs, generate into a local source tree such as `src/tangle_api/generated` (and `src/tangle_api/schema/openapi.json` when you want `tangle api --schema-source official`) and run from that project so local `src/tangle_api` shadows site-packages. For packaged custom APIs, publish/provide a distribution named `tangle-api` with a version compatible with this `tangle-cli` release (for example `0.0.1a3+yourorg` for a `tangle-cli` dependency on `tangle-api==0.0.1a3`) via a private index, `--find-links`, or uv sources. As an expert escape hatch, `--no-deps` installs only `tangle-cli` and skips all dependencies, so that environment must manually provide every required runtime dependency plus its generated/custom `tangle_api`; this is acceptable for controlled codegen/custom scenarios but not normal UX.
129
+ Custom API/codegen users can still run codegen from the fully capable install; generating bindings does not require removing the official `tangle-api` package. For project-local generated APIs, generate into a local source tree such as `src/tangle_api/generated` (and `src/tangle_api/schema/openapi.json` when you want `tangle api --schema-source official`) and run from that project so local `src/tangle_api` shadows site-packages. For packaged custom APIs, publish/provide a distribution named `tangle-api` with a version compatible with this `tangle-cli` release (for example `0.1.0+yourorg` for a `tangle-cli` dependency on `tangle-api==0.1.0`) via a private index, `--find-links`, or uv sources. As an expert escape hatch, `--no-deps` installs only `tangle-cli` and skips all dependencies, so that environment must manually provide every required runtime dependency plus its generated/custom `tangle_api`; this is acceptable for controlled codegen/custom scenarios but not normal UX.
130
+
131
+ ## Agent skills
132
+
133
+ This repo includes the Tangent agent-skill bundle under `skills/tangent/`. The bundle was ported from `tangle-cli-lab` and now treats this repository as the canonical source. It drives the public `tangle` / `tangle-cli` command surface, assumes the default `tangle-cli` install includes `tangle-api`, and keeps relative references (`agents/*.md`, `references/*.md`) self-contained for Pi-style skill loaders. The source distribution includes `skills/**` so downstream source-based consumers can inspect or vendor the skill docs with the release.
130
134
 
131
135
  ## Quick command examples
132
136
 
@@ -371,7 +375,7 @@ uv run python -m tangle_cli.openapi.codegen \
371
375
  --out src/tangle_api/generated
372
376
  ```
373
377
 
374
- That project-local `tangle_api` package can be an editable/package source tree. If you ship the custom API bindings as a wheel or source distribution, use the distribution name `tangle-api` and a compatible version for the `tangle-cli` release you are using. A PEP 440 local version such as `0.0.1a3+yourorg` can satisfy a public `==0.0.1a3` dependency while distinguishing your private build. Provide that package through your private index, `--find-links`, or uv source configuration so the resolver chooses it instead of the public official package.
378
+ That project-local `tangle_api` package can be an editable/package source tree. If you ship the custom API bindings as a wheel or source distribution, use the distribution name `tangle-api` and a compatible version for the `tangle-cli` release you are using. A PEP 440 local version such as `0.1.0+yourorg` can satisfy a public `==0.1.0` dependency while distinguishing your private build. Provide that package through your private index, `--find-links`, or uv source configuration so the resolver chooses it instead of the public official package.
375
379
 
376
380
  Generate from a backend checkout explicitly:
377
381
 
@@ -101,7 +101,11 @@ uv run tangle api --help
101
101
  uv run tangle sdk pipelines validate pipeline.yaml
102
102
  ```
103
103
 
104
- Custom API/codegen users can still run codegen from the fully capable install; generating bindings does not require removing the official `tangle-api` package. For project-local generated APIs, generate into a local source tree such as `src/tangle_api/generated` (and `src/tangle_api/schema/openapi.json` when you want `tangle api --schema-source official`) and run from that project so local `src/tangle_api` shadows site-packages. For packaged custom APIs, publish/provide a distribution named `tangle-api` with a version compatible with this `tangle-cli` release (for example `0.0.1a3+yourorg` for a `tangle-cli` dependency on `tangle-api==0.0.1a3`) via a private index, `--find-links`, or uv sources. As an expert escape hatch, `--no-deps` installs only `tangle-cli` and skips all dependencies, so that environment must manually provide every required runtime dependency plus its generated/custom `tangle_api`; this is acceptable for controlled codegen/custom scenarios but not normal UX.
104
+ Custom API/codegen users can still run codegen from the fully capable install; generating bindings does not require removing the official `tangle-api` package. For project-local generated APIs, generate into a local source tree such as `src/tangle_api/generated` (and `src/tangle_api/schema/openapi.json` when you want `tangle api --schema-source official`) and run from that project so local `src/tangle_api` shadows site-packages. For packaged custom APIs, publish/provide a distribution named `tangle-api` with a version compatible with this `tangle-cli` release (for example `0.1.0+yourorg` for a `tangle-cli` dependency on `tangle-api==0.1.0`) via a private index, `--find-links`, or uv sources. As an expert escape hatch, `--no-deps` installs only `tangle-cli` and skips all dependencies, so that environment must manually provide every required runtime dependency plus its generated/custom `tangle_api`; this is acceptable for controlled codegen/custom scenarios but not normal UX.
105
+
106
+ ## Agent skills
107
+
108
+ This repo includes the Tangent agent-skill bundle under `skills/tangent/`. The bundle was ported from `tangle-cli-lab` and now treats this repository as the canonical source. It drives the public `tangle` / `tangle-cli` command surface, assumes the default `tangle-cli` install includes `tangle-api`, and keeps relative references (`agents/*.md`, `references/*.md`) self-contained for Pi-style skill loaders. The source distribution includes `skills/**` so downstream source-based consumers can inspect or vendor the skill docs with the release.
105
109
 
106
110
  ## Quick command examples
107
111
 
@@ -346,7 +350,7 @@ uv run python -m tangle_cli.openapi.codegen \
346
350
  --out src/tangle_api/generated
347
351
  ```
348
352
 
349
- That project-local `tangle_api` package can be an editable/package source tree. If you ship the custom API bindings as a wheel or source distribution, use the distribution name `tangle-api` and a compatible version for the `tangle-cli` release you are using. A PEP 440 local version such as `0.0.1a3+yourorg` can satisfy a public `==0.0.1a3` dependency while distinguishing your private build. Provide that package through your private index, `--find-links`, or uv source configuration so the resolver chooses it instead of the public official package.
353
+ That project-local `tangle_api` package can be an editable/package source tree. If you ship the custom API bindings as a wheel or source distribution, use the distribution name `tangle-api` and a compatible version for the `tangle-cli` release you are using. A PEP 440 local version such as `0.1.0+yourorg` can satisfy a public `==0.1.0` dependency while distinguishing your private build. Provide that package through your private index, `--find-links`, or uv source configuration so the resolver chooses it instead of the public official package.
350
354
 
351
355
  Generate from a backend checkout explicitly:
352
356
 
@@ -14,6 +14,6 @@ from tangle_cli.dynamic_discovery_client import TangleDynamicDiscoveryClient
14
14
  try:
15
15
  __version__ = metadata_version("tangle-cli")
16
16
  except PackageNotFoundError:
17
- __version__ = "0.1.0"
17
+ __version__ = "0.1.1"
18
18
 
19
19
  __all__ = ["TangleDynamicDiscoveryClient", "__version__"]
@@ -37,8 +37,8 @@ _FAILURE_EARLY_EXIT_STATUSES = ("FAILED", "SYSTEM_ERROR")
37
37
  _EXECUTION_STATE_TIMINGS_METADATA_KEY = "execution_state_timings"
38
38
  _EXECUTION_STATE_TIMING_MONOTONIC_METADATA_KEY = "_execution_state_timing_monotonic"
39
39
  _SUBMISSION_ID_ANNOTATION_KEY = "tangle-cli/submission-id"
40
- _SUBMIT_RECOVERY_LOOKUP_ATTEMPTS = 2
41
- _SUBMIT_RECOVERY_LOOKUP_DELAY_SECONDS = 0.1
40
+ _SUBMIT_RECOVERY_BACKOFF_SECONDS = (0.5, 1.0, 2.0, 4.0, 8.0, 16.0)
41
+ _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS = 2
42
42
 
43
43
 
44
44
  class PipelineRunError(RuntimeError):
@@ -1529,6 +1529,11 @@ class PipelineRunManager(TangleCliHandler):
1529
1529
  submission_id = annotations.get(_SUBMISSION_ID_ANNOTATION_KEY)
1530
1530
  return str(submission_id) if submission_id else None
1531
1531
 
1532
+ @staticmethod
1533
+ def _submit_recovery_backoff_seconds(submit_recovery_attempts: int) -> tuple[float, ...]:
1534
+ attempt_count = max(0, min(int(submit_recovery_attempts), len(_SUBMIT_RECOVERY_BACKOFF_SECONDS)))
1535
+ return _SUBMIT_RECOVERY_BACKOFF_SECONDS[:attempt_count]
1536
+
1532
1537
  def _submitted_runs_for_submission_id(self, submission_id: str) -> list[dict[str, Any]]:
1533
1538
  query = {
1534
1539
  "and": [
@@ -1554,14 +1559,32 @@ class PipelineRunManager(TangleCliHandler):
1554
1559
  self,
1555
1560
  *,
1556
1561
  submission_id: str | None,
1562
+ submit_recovery_attempts: int = _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
1557
1563
  ) -> dict[str, Any] | None:
1558
1564
  if not submission_id:
1559
1565
  return None
1560
- for lookup_attempt in range(1, _SUBMIT_RECOVERY_LOOKUP_ATTEMPTS + 1):
1566
+ backoff_seconds = self._submit_recovery_backoff_seconds(submit_recovery_attempts)
1567
+ total_lookup_attempts = len(backoff_seconds)
1568
+ if total_lookup_attempts == 0:
1569
+ self.logger.warn(
1570
+ "Submit recovery lookup disabled "
1571
+ f"({_SUBMISSION_ID_ANNOTATION_KEY}={submission_id}, "
1572
+ f"submit_recovery_attempts={submit_recovery_attempts}); "
1573
+ "resubmitting the same frozen body with preserved inputs."
1574
+ )
1575
+ return None
1576
+ for lookup_attempt, delay_seconds in enumerate(backoff_seconds, start=1):
1577
+ self.logger.info(
1578
+ "Waiting "
1579
+ f"{delay_seconds:g}s before checking whether failed submit already created a pipeline run "
1580
+ f"({_SUBMISSION_ID_ANNOTATION_KEY}={submission_id}, "
1581
+ f"lookup_attempt={lookup_attempt}/{total_lookup_attempts})"
1582
+ )
1583
+ time.sleep(delay_seconds)
1561
1584
  self.logger.info(
1562
1585
  "Checking whether failed submit already created a pipeline run "
1563
1586
  f"({_SUBMISSION_ID_ANNOTATION_KEY}={submission_id}, "
1564
- f"lookup_attempt={lookup_attempt}/{_SUBMIT_RECOVERY_LOOKUP_ATTEMPTS})"
1587
+ f"lookup_attempt={lookup_attempt}/{total_lookup_attempts})"
1565
1588
  )
1566
1589
  try:
1567
1590
  matches = self._submitted_runs_for_submission_id(submission_id)
@@ -1598,8 +1621,6 @@ class PipelineRunManager(TangleCliHandler):
1598
1621
  f"{_SUBMISSION_ID_ANNOTATION_KEY}={submission_id}: {', '.join(run_ids) or matches!r}. "
1599
1622
  "Refusing to submit a duplicate."
1600
1623
  )
1601
- if lookup_attempt < _SUBMIT_RECOVERY_LOOKUP_ATTEMPTS:
1602
- time.sleep(_SUBMIT_RECOVERY_LOOKUP_DELAY_SECONDS)
1603
1624
  self.logger.warn(
1604
1625
  "No existing pipeline run found after submit failure "
1605
1626
  f"({_SUBMISSION_ID_ANNOTATION_KEY}={submission_id}); "
@@ -1647,6 +1668,7 @@ class PipelineRunManager(TangleCliHandler):
1647
1668
  timeout_clock: str = "monotonic",
1648
1669
  exit_on_first_failure: bool = False,
1649
1670
  metadata: dict[str, Any] | None = None,
1671
+ submit_recovery_attempts: int = _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
1650
1672
  metadata_factory: Callable[
1651
1673
  [int, PipelineRunContext | None, Exception | None], dict[str, Any]
1652
1674
  ] | None = None,
@@ -1723,6 +1745,7 @@ class PipelineRunManager(TangleCliHandler):
1723
1745
  if reused_after_submit_failure:
1724
1746
  recovered_response = self._recover_submitted_run_after_submit_error(
1725
1747
  submission_id=self._submission_id_from_body(body),
1748
+ submit_recovery_attempts=submit_recovery_attempts,
1726
1749
  )
1727
1750
  if recovered_response is not None:
1728
1751
  response = self._adopt_submitted_run(
@@ -1754,6 +1777,7 @@ class PipelineRunManager(TangleCliHandler):
1754
1777
  )
1755
1778
  recovered_response = self._recover_submitted_run_after_submit_error(
1756
1779
  submission_id=submission_id_for_recovery,
1780
+ submit_recovery_attempts=submit_recovery_attempts,
1757
1781
  )
1758
1782
  if recovered_response is None:
1759
1783
  self.hooks.on_submit_error(submit_exc, context=context)
@@ -1834,6 +1858,7 @@ class PipelineRunManager(TangleCliHandler):
1834
1858
  timeout_clock: str = "monotonic",
1835
1859
  exit_on_first_failure: bool = False,
1836
1860
  metadata: dict[str, Any] | None = None,
1861
+ submit_recovery_attempts: int = _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
1837
1862
  ) -> dict[str, Any]:
1838
1863
  """Submit/wait/retry an already prepared submit body.
1839
1864
 
@@ -1862,6 +1887,7 @@ class PipelineRunManager(TangleCliHandler):
1862
1887
  timeout_clock=timeout_clock,
1863
1888
  exit_on_first_failure=exit_on_first_failure,
1864
1889
  metadata=metadata,
1890
+ submit_recovery_attempts=submit_recovery_attempts,
1865
1891
  )
1866
1892
 
1867
1893
  def run_pipeline_spec(
@@ -1882,6 +1908,7 @@ class PipelineRunManager(TangleCliHandler):
1882
1908
  timeout_clock: str = "monotonic",
1883
1909
  exit_on_first_failure: bool = False,
1884
1910
  metadata: dict[str, Any] | None = None,
1911
+ submit_recovery_attempts: int = _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
1885
1912
  ) -> dict[str, Any]:
1886
1913
  """Submit/wait/retry an already hydrated/validated in-memory spec."""
1887
1914
 
@@ -1911,6 +1938,7 @@ class PipelineRunManager(TangleCliHandler):
1911
1938
  timeout_clock=timeout_clock,
1912
1939
  exit_on_first_failure=exit_on_first_failure,
1913
1940
  metadata=metadata,
1941
+ submit_recovery_attempts=submit_recovery_attempts,
1914
1942
  )
1915
1943
 
1916
1944
  def run_pipeline(
@@ -1931,6 +1959,7 @@ class PipelineRunManager(TangleCliHandler):
1931
1959
  timeout_clock: str = "monotonic",
1932
1960
  exit_on_first_failure: bool = False,
1933
1961
  metadata: dict[str, Any] | None = None,
1962
+ submit_recovery_attempts: int = _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
1934
1963
  ) -> dict[str, Any]:
1935
1964
  """Submit (and optionally wait for) a pipeline with lifecycle hooks.
1936
1965
 
@@ -1965,6 +1994,7 @@ class PipelineRunManager(TangleCliHandler):
1965
1994
  timeout_clock=timeout_clock,
1966
1995
  exit_on_first_failure=exit_on_first_failure,
1967
1996
  metadata=metadata,
1997
+ submit_recovery_attempts=submit_recovery_attempts,
1968
1998
  )
1969
1999
 
1970
2000
 
@@ -507,6 +507,7 @@ class PipelineRunner(PipelineRunnerHooks, PipelineRunManager):
507
507
  open_browser: bool = False,
508
508
  include_next_steps: bool = False,
509
509
  metadata: dict[str, Any] | None = None,
510
+ submit_recovery_attempts: int = 2,
510
511
  ) -> dict[str, Any]:
511
512
  """Run a pipeline path through generic preparation + lifecycle hooks.
512
513
 
@@ -603,6 +604,7 @@ class PipelineRunner(PipelineRunnerHooks, PipelineRunManager):
603
604
  timeout_clock=timeout_clock,
604
605
  exit_on_first_failure=exit_on_first_failure,
605
606
  metadata_factory=metadata_factory,
607
+ submit_recovery_attempts=submit_recovery_attempts,
606
608
  )
607
609
  context = result.get("context")
608
610
  attempt = context.attempt if isinstance(context, PipelineRunContext) else max(preparations)
@@ -28,6 +28,7 @@ from .cli_options import (
28
28
  from .logger import Logger, logger_for_log_type
29
29
  from .pipeline_run_annotations import AnnotationManager
30
30
  from .pipeline_run_manager import (
31
+ _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
31
32
  PipelineRunError,
32
33
  PipelineRunHooks,
33
34
  PipelineRunManager,
@@ -166,6 +167,15 @@ def pipeline_runs_submit(
166
167
  auth_header: AuthHeaderOption = None,
167
168
  header: HeaderOption = None,
168
169
  config: ConfigOption = None,
170
+ submit_recovery_attempts: Annotated[
171
+ int,
172
+ Parameter(
173
+ help=(
174
+ "Number of post-failed-submit recovery lookups before resubmitting; "
175
+ "higher values wait longer for delayed run registration."
176
+ )
177
+ ),
178
+ ] = _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS,
169
179
  log_type: LogTypeOption = "console",
170
180
  ) -> None:
171
181
  """Hydrate and submit a local pipeline YAML file as a run."""
@@ -181,6 +191,7 @@ def pipeline_runs_submit(
181
191
  "run_as": (run_as, None),
182
192
  "trusted_source": (trusted_source, None),
183
193
  "trusted_hydration_cli": ("trusted_hydration_cli", trusted_hydration, None, False),
194
+ "submit_recovery_attempts": (submit_recovery_attempts, _DEFAULT_SUBMIT_RECOVERY_ATTEMPTS),
184
195
  "log_type": (log_type, "console"),
185
196
  **api_arg_specs(base_url=base_url, token=token, auth_header=auth_header, header=header),
186
197
  }
@@ -194,7 +205,12 @@ def pipeline_runs_submit(
194
205
  }
195
206
  if args.dry_run:
196
207
  return manager.build_submit_body(args.pipeline_path, **kwargs)
197
- return manager.submit_pipeline(args.pipeline_path, **kwargs)
208
+ result = manager.run_pipeline(
209
+ args.pipeline_path,
210
+ **kwargs,
211
+ submit_recovery_attempts=args.submit_recovery_attempts,
212
+ )
213
+ return result["response"]
198
214
 
199
215
  _run_manager_action(config, base_url, specs, action)
200
216
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tangle-cli"
3
- version = "0.0.1a3"
3
+ version = "0.1.1"
4
4
  description = "CLI for Tangle, the open-source ML pipeline orchestration platform"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -18,7 +18,7 @@ dependencies = [
18
18
  "pydantic>=2.0",
19
19
  "pyyaml>=6.0",
20
20
  "requests>=2.32.0",
21
- "tangle-api==0.0.1a3",
21
+ "tangle-api==0.1.0",
22
22
  "tomli>=2.0; python_version < '3.11'",
23
23
  ]
24
24
 
@@ -42,6 +42,7 @@ build-backend = "uv_build"
42
42
 
43
43
  [tool.uv.build-backend]
44
44
  module-root = "packages/tangle-cli/src"
45
+ source-include = ["skills/**"]
45
46
 
46
47
  [tool.uv.sources]
47
48
  cloud-pipelines-backend = { git = "https://github.com/TangleML/tangle", rev = "stable_cli" }