AIR CLI: send dependencies inline, stop uploading requirements.yaml - #6077
AIR CLI: send dependencies inline, stop uploading requirements.yaml#6077riddhibhagwat-db wants to merge 4 commits into
Conversation
`air run` now carries the user's declared dependencies — an inline list, or the dependencies read from a requirements.yaml file — on the submission's environments[].spec.dependencies, and no longer uploads a requirements.yaml artifact at all. The AI Runtime backend installs the inline deps via --deps-config and treats a missing co-located requirements.yaml as "no requirements" (databricks-eng/universe#2297011), so the upload is unnecessary. This removes the vestigial empty requirements.yaml that a no-dependency run used to upload just to satisfy the launcher's derived path. When no dependencies are declared, spec.dependencies is omitted and the payload is unchanged. A -r/--requirement include in a requirements file is rejected, since the referenced file is never uploaded with the run. Ports databricks-eng/universe#2178617 and aligns with the backend skip in #2297011. Co-authored-by: Isaac
Add a run-submit-deps acceptance test that submits with inline dependencies and asserts the golden payload carries them on environments[].spec.dependencies and that only command.sh and training_config.yaml are uploaded (no requirements.yaml). Consolidate the dependency unit tests: fold the missing-file case into TestEnvironmentDependencies (it exercises runsubmit.go, not runupload.go) and trim the comments to state each test's intent. Co-authored-by: Isaac
The run-submit-deps acceptance test uploads run.yaml's contents verbatim as training_config.yaml, so a Windows CRLF checkout changed the recorded payload and failed the golden. Pin run.yaml to eol=lf via a scoped .gitattributes. Also drop two over-explanatory comments in runupload.go. Co-authored-by: Isaac
Integration test reportCommit: e87da09
18 interesting tests: 8 FAIL, 4 KNOWN, 4 SKIP, 2 flaky
|
| } | ||
| } | ||
|
|
||
| deps, err := environmentDependencies(cfg, configPath) |
There was a problem hiding this comment.
Can you do the validation before uploadArtifacts?
Move environmentDependencies resolution ahead of the workspace uploads so a bad requirements file (e.g. a -r/--requirement include) fails fast without leaving orphaned artifacts in the workspace. Covered by a new guard subtest asserting no import-file request is made when dependency resolution fails. Co-authored-by: Isaac
|
|
||
| // Carry the user's declared deps inline on spec.dependencies; the AI Runtime | ||
| // backend installs them via --deps-config. The SDK marshaler drops nil and empty | ||
| // slices, so a no-deps run omits the key. Deps are no longer uploaded as a |
There was a problem hiding this comment.
I think mentioning "Deps are no longer uploaded as a requirements.yaml" is confusing for anyone who reads this later without the historical context — it references impl that no longer exists. Suggest just describing what the code does now (deps ride on spec.dependencies), dropping the "no longer" framing.
| return nil, fmt.Errorf("requirements file dependency %q uses a requirements-file include (-r/--requirement), which is not supported; list the dependencies directly instead", dep) | ||
| } | ||
| } | ||
| return doc.Dependencies, nil |
There was a problem hiding this comment.
File-form deps lose their version. readRequirementsDependencies returns only doc.Dependencies and drops doc.Version; the wire version comes solely from top-level environment.version — which validation rejects for file-form ("set the version inside that file"). So dependencies: ./reqs.yaml with version: 5 inside silently gets the default image, and the error message points users at the field we ignore. Python reads it (_parse_requirements_yaml), so this also diverges from the Python CLI.
Fix: return the file's version (the requirementsDoc.Version you already decode — currently dead) and use it when top-level version is unset. Worth an acceptance case: file-form with in-file version: → assert environment_version on the wire.
| // (resolved against the config's directory). Returns nil when none are declared. | ||
| func environmentDependencies(cfg *runConfig, configPath string) ([]string, error) { | ||
| if deps, ok := cfg.inlineDependencies(); ok { | ||
| return deps, nil |
There was a problem hiding this comment.
Minor: the -r/--requirement rejection only runs on file-form; inline dependencies: ["-r foo.txt"] passes straight to spec.dependencies. Python has the same gap so it's not a divergence — flagging in case we close it in both. Non-blocking.
Changes & Why
air runnow carries the user's declared dependencies (which may be an inline list, or read from a requirements.yaml file) on the submission's environments[].spec.dependencies, and no longer uploads a requirements.yaml artifact at all. This is the follow up PR to https://github.com/databricks-eng/universe/pull/2178617?timeline_per_page=5 (implementing this method in the python CLI) and https://github.com/databricks-eng/universe/pull/2297011?timeline_per_page=5 (follow up backend changes to unblock the new path; installs the inline deps via --deps-config and treats a missing co-located requirements.yaml as "no requirements").This removes the vestigial empty requirements.yaml that a no-dependency run used to upload just to satisfy the launcher's derived path. When no dependencies are declared, spec.dependencies is omitted and the payload is unchanged. A -r/--requirement include in a requirements file is rejected, since the referenced file is never uploaded with the run.
Tests
Unit tests:
��
Acceptance tests:
acceptance/experimental/air/run-submit-deps/-> submits with inline deps, golden asserts:spec.dependencies: [numpy, torch==2.3.0]on the runs/submit wire ��command.sh+training_config.yamluploaded no requirements.yaml �Can verify tests using:
Manual verification:

Instantiates a run succesfully with/without req.yaml dependencies declared: