roar-cli 0.2.2__tar.gz → 0.2.4__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 (182) hide show
  1. {roar_cli-0.2.2/roar_cli.egg-info → roar_cli-0.2.4}/PKG-INFO +1 -1
  2. {roar_cli-0.2.2 → roar_cli-0.2.4}/pyproject.toml +1 -1
  3. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/bin/roar-tracer +0 -0
  4. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/registration.py +9 -0
  5. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/glaas_client.py +3 -4
  6. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/registration/coordinator.py +14 -29
  7. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/registration/job.py +122 -0
  8. {roar_cli-0.2.2 → roar_cli-0.2.4/roar_cli.egg-info}/PKG-INFO +1 -1
  9. {roar_cli-0.2.2 → roar_cli-0.2.4}/LICENSE +0 -0
  10. {roar_cli-0.2.2 → roar_cli-0.2.4}/README.md +0 -0
  11. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/__init__.py +0 -0
  12. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/__main__.py +0 -0
  13. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/analyzers/__init__.py +0 -0
  14. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/analyzers/base.py +0 -0
  15. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/analyzers/experiment_trackers.py +0 -0
  16. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/__init__.py +0 -0
  17. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/__init__.py +0 -0
  18. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/_execution.py +0 -0
  19. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/auth.py +0 -0
  20. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/build.py +0 -0
  21. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/config.py +0 -0
  22. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/dag.py +0 -0
  23. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/env.py +0 -0
  24. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/get.py +0 -0
  25. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/init.py +0 -0
  26. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/lineage.py +0 -0
  27. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/log.py +0 -0
  28. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/pop.py +0 -0
  29. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/put.py +0 -0
  30. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/register.py +0 -0
  31. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/reproduce.py +0 -0
  32. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/reset.py +0 -0
  33. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/run.py +0 -0
  34. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/show.py +0 -0
  35. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/commands/status.py +0 -0
  36. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/context.py +0 -0
  37. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/cli/decorators.py +0 -0
  38. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/config.py +0 -0
  39. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/__init__.py +0 -0
  40. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/bootstrap.py +0 -0
  41. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/container.py +0 -0
  42. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/di.py +0 -0
  43. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/dto/__init__.py +0 -0
  44. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/dto/registration.py +0 -0
  45. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/exceptions.py +0 -0
  46. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/__init__.py +0 -0
  47. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/cloud.py +0 -0
  48. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/command.py +0 -0
  49. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/config.py +0 -0
  50. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/logger.py +0 -0
  51. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/presenter.py +0 -0
  52. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/provenance.py +0 -0
  53. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/repositories.py +0 -0
  54. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/reproduction.py +0 -0
  55. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/run.py +0 -0
  56. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/services.py +0 -0
  57. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/telemetry.py +0 -0
  58. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/upload.py +0 -0
  59. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/interfaces/vcs.py +0 -0
  60. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/logging.py +0 -0
  61. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/__init__.py +0 -0
  62. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/artifact.py +0 -0
  63. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/base.py +0 -0
  64. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/command.py +0 -0
  65. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/config.py +0 -0
  66. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/dag.py +0 -0
  67. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/glaas.py +0 -0
  68. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/job.py +0 -0
  69. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/lineage.py +0 -0
  70. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/provenance.py +0 -0
  71. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/run.py +0 -0
  72. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/session.py +0 -0
  73. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/telemetry.py +0 -0
  74. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/models/vcs.py +0 -0
  75. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/registry.py +0 -0
  76. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/settings.py +0 -0
  77. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/core/validation.py +0 -0
  78. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/__init__.py +0 -0
  79. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/context.py +0 -0
  80. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/engine.py +0 -0
  81. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/hashing/__init__.py +0 -0
  82. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/hashing/registry.py +0 -0
  83. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/hashing/strategies.py +0 -0
  84. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/models.py +0 -0
  85. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/repositories/__init__.py +0 -0
  86. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/repositories/artifact.py +0 -0
  87. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/repositories/collection.py +0 -0
  88. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/repositories/hash_cache.py +0 -0
  89. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/repositories/job.py +0 -0
  90. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/repositories/session.py +0 -0
  91. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/schema.py +0 -0
  92. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/services/__init__.py +0 -0
  93. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/services/hashing.py +0 -0
  94. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/services/job_recording.py +0 -0
  95. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/services/lineage.py +0 -0
  96. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/services/session.py +0 -0
  97. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/db/storage.py +0 -0
  98. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/filters/__init__.py +0 -0
  99. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/filters/files.py +0 -0
  100. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/filters/omit.py +0 -0
  101. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/__init__.py +0 -0
  102. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/cloud/__init__.py +0 -0
  103. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/cloud/base.py +0 -0
  104. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/telemetry/__init__.py +0 -0
  105. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/telemetry/base.py +0 -0
  106. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/telemetry/wandb.py +0 -0
  107. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/vcs/__init__.py +0 -0
  108. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/vcs/base.py +0 -0
  109. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/plugins/vcs/git.py +0 -0
  110. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/presenters/__init__.py +0 -0
  111. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/presenters/console.py +0 -0
  112. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/presenters/dag_renderer.py +0 -0
  113. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/presenters/formatting.py +0 -0
  114. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/presenters/run_report.py +0 -0
  115. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/__init__.py +0 -0
  116. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/__init__.py +0 -0
  117. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/args.py +0 -0
  118. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/coordinator.py +0 -0
  119. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/dag_resolver.py +0 -0
  120. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/execution_service.py +0 -0
  121. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/inject/__init__.py +0 -0
  122. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/inject/sitecustomize.py +0 -0
  123. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/__init__.py +0 -0
  124. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/assembler.py +0 -0
  125. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/build_pip_collector.py +0 -0
  126. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/build_tool_collector.py +0 -0
  127. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/data_loader.py +0 -0
  128. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/file_filter.py +0 -0
  129. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/package_collector.py +0 -0
  130. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/process_summarizer.py +0 -0
  131. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/runtime_collector.py +0 -0
  132. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/provenance/service.py +0 -0
  133. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/signal_handler.py +0 -0
  134. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/execution/tracer.py +0 -0
  135. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/__init__.py +0 -0
  136. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/backends/__init__.py +0 -0
  137. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/backends/base.py +0 -0
  138. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/backends/gcs.py +0 -0
  139. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/backends/http.py +0 -0
  140. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/backends/noop.py +0 -0
  141. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/backends/s3.py +0 -0
  142. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/get/service.py +0 -0
  143. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/logging.py +0 -0
  144. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/lookup/__init__.py +0 -0
  145. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/lookup/entity_lookup.py +0 -0
  146. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/lookup/step_parser.py +0 -0
  147. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/__init__.py +0 -0
  148. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/backends/__init__.py +0 -0
  149. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/backends/base.py +0 -0
  150. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/backends/gcs.py +0 -0
  151. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/backends/memory.py +0 -0
  152. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/backends/noop.py +0 -0
  153. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/backends/s3.py +0 -0
  154. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/git.py +0 -0
  155. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/resolver.py +0 -0
  156. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/put/service.py +0 -0
  157. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/registration/__init__.py +0 -0
  158. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/registration/artifact.py +0 -0
  159. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/registration/register_service.py +0 -0
  160. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/registration/session.py +0 -0
  161. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/reproduction/__init__.py +0 -0
  162. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/reproduction/environment_setup.py +0 -0
  163. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/reproduction/pipeline_executor.py +0 -0
  164. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/reproduction/service.py +0 -0
  165. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/secrets/__init__.py +0 -0
  166. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/secrets/filter_service.py +0 -0
  167. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/upload/__init__.py +0 -0
  168. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/upload/lineage_collector.py +0 -0
  169. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/upload/service.py +0 -0
  170. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/vcs/__init__.py +0 -0
  171. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/services/vcs/git_access.py +0 -0
  172. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/utils/__init__.py +0 -0
  173. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/utils/cloud.py +0 -0
  174. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar/utils/git_url.py +0 -0
  175. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar_cli.egg-info/SOURCES.txt +0 -0
  176. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar_cli.egg-info/dependency_links.txt +0 -0
  177. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar_cli.egg-info/entry_points.txt +0 -0
  178. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar_cli.egg-info/requires.txt +0 -0
  179. {roar_cli-0.2.2 → roar_cli-0.2.4}/roar_cli.egg-info/top_level.txt +0 -0
  180. {roar_cli-0.2.2 → roar_cli-0.2.4}/setup.cfg +0 -0
  181. {roar_cli-0.2.2 → roar_cli-0.2.4}/setup.py +0 -0
  182. {roar_cli-0.2.2 → roar_cli-0.2.4}/tests/test_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roar-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Reproducibility and provenance tracker for ML training pipelines
5
5
  Author-email: TReqs Team <info@treqs.ai>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "roar-cli"
7
- version = "0.2.2"
7
+ version = "0.2.4"
8
8
  description = "Reproducibility and provenance tracker for ML training pipelines"
9
9
  authors = [
10
10
  { name="TReqs Team", email="info@treqs.ai" }
@@ -144,6 +144,15 @@ class IJobRegistrar(Protocol):
144
144
  """Create a job WITHOUT artifact links."""
145
145
  ...
146
146
 
147
+ def create_jobs_batch(
148
+ self,
149
+ jobs: list[dict],
150
+ session_hash: str,
151
+ git_context: "GitContext",
152
+ ) -> list[JobRegistrationResult]:
153
+ """Create multiple jobs in batch WITHOUT artifact links."""
154
+ ...
155
+
147
156
  def link_job_artifacts(
148
157
  self,
149
158
  session_hash: str,
@@ -378,10 +378,8 @@ class GlaasClient:
378
378
  len(body_bytes) if body_bytes else 0,
379
379
  )
380
380
 
381
- # Create auth header
381
+ # Create auth header (None if no SSH keys available)
382
382
  auth_header = make_auth_header(method, path, body_bytes)
383
- if not auth_header:
384
- return None, "Failed to create authentication signature"
385
383
 
386
384
  # Build request
387
385
  req = urllib.request.Request(
@@ -389,7 +387,8 @@ class GlaasClient:
389
387
  data=body_bytes,
390
388
  method=method,
391
389
  )
392
- req.add_header("Authorization", auth_header)
390
+ if auth_header:
391
+ req.add_header("Authorization", auth_header)
393
392
  if body_bytes:
394
393
  req.add_header("Content-Type", "application/json")
395
394
 
@@ -113,39 +113,24 @@ class RegistrationCoordinator(IRegistrationCoordinator):
113
113
  len(artifacts),
114
114
  )
115
115
 
116
- # Phase 2: Create all jobs WITHOUT artifact links
117
- self._logger.debug("Phase 2: Creating %d jobs without artifact links", len(jobs))
116
+ # Phase 2: Create all jobs WITHOUT artifact links (batch)
117
+ self._logger.debug("Phase 2: Batch-creating %d jobs without artifact links", len(jobs))
118
118
  job_uids_created = []
119
119
 
120
- for job in jobs:
121
- job_uid = job.get("job_uid")
122
- if not job_uid:
123
- self._logger.warning("Skipping job without job_uid")
124
- jobs_failed += 1
125
- errors.append("Job missing job_uid")
126
- continue
127
-
128
- result = self.job_service.create_job(
129
- command=job.get("command", ""),
130
- timestamp=job.get("timestamp", 0.0),
120
+ if jobs:
121
+ batch_results = self.job_service.create_jobs_batch(
122
+ jobs=jobs,
131
123
  session_hash=session_hash,
132
- job_uid=job_uid,
133
- git_commit=job.get("git_commit") or git_context.commit or "",
134
- git_branch=job.get("git_branch") or git_context.branch or "",
135
- duration_seconds=job.get("duration_seconds", 0.0),
136
- exit_code=job.get("exit_code", 0),
137
- job_type=job.get("job_type") or "run", # Normalize None to "run"
138
- step_number=job.get("step_number", 0),
139
- metadata=job.get("metadata"),
124
+ git_context=git_context,
140
125
  )
141
-
142
- if result.success:
143
- jobs_created += 1
144
- job_uids_created.append(job_uid)
145
- else:
146
- jobs_failed += 1
147
- if result.error:
148
- errors.append(f"Job {job_uid}: {result.error}")
126
+ for result in batch_results:
127
+ if result.success:
128
+ jobs_created += 1
129
+ job_uids_created.append(result.job_uid)
130
+ else:
131
+ jobs_failed += 1
132
+ if result.error:
133
+ errors.append(f"Job {result.job_uid}: {result.error}")
149
134
 
150
135
  self._logger.debug(
151
136
  "Phase 2 complete: %d jobs created, %d failed",
@@ -9,6 +9,7 @@ from functools import cached_property
9
9
 
10
10
  from ...core.interfaces.logger import ILogger
11
11
  from ...core.interfaces.registration import (
12
+ GitContext,
12
13
  IJobRegistrar,
13
14
  ISecretFilter,
14
15
  JobLinkResult,
@@ -193,6 +194,127 @@ class JobRegistrationService(IJobRegistrar):
193
194
  job_id=str(job_id) if job_id else None,
194
195
  )
195
196
 
197
+ def create_jobs_batch(
198
+ self,
199
+ jobs: list[dict],
200
+ session_hash: str,
201
+ git_context: GitContext,
202
+ ) -> list[JobRegistrationResult]:
203
+ """
204
+ Create multiple jobs in a single batch request WITHOUT artifact links.
205
+
206
+ Validates and filters each job, then sends all valid jobs in one
207
+ ``register_jobs_batch`` call. Jobs that fail validation are returned
208
+ as individual failure results without hitting the server.
209
+
210
+ Args:
211
+ jobs: List of job dicts (same format as ``register_lineage`` jobs).
212
+ session_hash: Session these jobs belong to.
213
+ git_context: Fallback git context for jobs missing git fields.
214
+
215
+ Returns:
216
+ One ``JobRegistrationResult`` per input job, in the same order.
217
+ """
218
+ if not jobs:
219
+ return []
220
+
221
+ results: list[JobRegistrationResult | None] = [None] * len(jobs)
222
+ payloads: list[dict] = []
223
+ payload_indices: list[int] = [] # maps payload position → original index
224
+
225
+ for i, job in enumerate(jobs):
226
+ job_uid = job.get("job_uid")
227
+ if not job_uid:
228
+ self._logger.warning("Skipping job without job_uid")
229
+ results[i] = JobRegistrationResult(
230
+ success=False, job_uid="", error="Job missing job_uid"
231
+ )
232
+ continue
233
+
234
+ command = job.get("command", "")
235
+ git_commit = job.get("git_commit") or git_context.commit or ""
236
+ git_branch = job.get("git_branch") or git_context.branch or ""
237
+ metadata = job.get("metadata")
238
+
239
+ # Filter sensitive data
240
+ filtered_command, _, filtered_metadata = self._filter_job_data(command, None, metadata)
241
+
242
+ # Validate
243
+ validation = validate_job_registration(
244
+ command=filtered_command,
245
+ timestamp=job.get("timestamp", 0.0),
246
+ session_hash=session_hash,
247
+ job_uid=job_uid,
248
+ git_commit=git_commit,
249
+ git_branch=git_branch,
250
+ job_type=job.get("job_type"),
251
+ step_number=job.get("step_number", 0),
252
+ )
253
+ if not validation:
254
+ error_msg = "; ".join(validation.errors)
255
+ self._logger.warning("Job validation failed for %s: %s", job_uid, error_msg)
256
+ results[i] = JobRegistrationResult(success=False, job_uid=job_uid, error=error_msg)
257
+ continue
258
+
259
+ payload: dict = {
260
+ "command": filtered_command,
261
+ "timestamp": job.get("timestamp", 0.0),
262
+ "job_uid": job_uid,
263
+ "git_commit": git_commit,
264
+ "git_branch": git_branch,
265
+ "duration_seconds": job.get("duration_seconds", 0.0),
266
+ "exit_code": job.get("exit_code", 0),
267
+ "job_type": job.get("job_type") or "run",
268
+ "step_number": job.get("step_number", 0),
269
+ }
270
+ if filtered_metadata:
271
+ payload["metadata"] = filtered_metadata
272
+
273
+ payloads.append(payload)
274
+ payload_indices.append(i)
275
+
276
+ if not payloads:
277
+ return [r for r in results if r is not None]
278
+
279
+ self._logger.debug(
280
+ "Batch registering %d jobs under session %s", len(payloads), session_hash[:12]
281
+ )
282
+
283
+ job_ids, errors, overall_error = self.client.register_jobs_batch(
284
+ session_hash=session_hash,
285
+ jobs=payloads,
286
+ )
287
+
288
+ if overall_error:
289
+ # Entire batch failed — mark all payload jobs as failed
290
+ for idx in payload_indices:
291
+ job_uid = jobs[idx].get("job_uid", "")
292
+ results[idx] = JobRegistrationResult(
293
+ success=False, job_uid=job_uid, error=overall_error
294
+ )
295
+ else:
296
+ for pos, idx in enumerate(payload_indices):
297
+ job_uid = payloads[pos]["job_uid"]
298
+ if pos < len(errors) and errors[pos]:
299
+ results[idx] = JobRegistrationResult(
300
+ success=False, job_uid=job_uid, error=errors[pos]
301
+ )
302
+ else:
303
+ job_id = job_ids[pos] if pos < len(job_ids) else None
304
+ results[idx] = JobRegistrationResult(
305
+ success=True,
306
+ job_uid=job_uid,
307
+ job_id=str(job_id) if job_id else None,
308
+ )
309
+
310
+ self._logger.debug(
311
+ "Batch job registration complete: %d succeeded, %d failed",
312
+ sum(1 for r in results if r and r.success),
313
+ sum(1 for r in results if r and not r.success),
314
+ )
315
+
316
+ return [r for r in results if r is not None]
317
+
196
318
  def link_job_artifacts(
197
319
  self,
198
320
  session_hash: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: roar-cli
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Reproducibility and provenance tracker for ML training pipelines
5
5
  Author-email: TReqs Team <info@treqs.ai>
6
6
  License-Expression: Apache-2.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes