Skip to content

feat(cli): deploy code builds as code bundle uploads - #112

Merged
mrchtr merged 19 commits into
mainfrom
feat/code-bundle-sdk
Oct 30, 2026
Merged

mrchtr merged 19 commits into
mainfrom
feat/code-bundle-sdk

Conversation

@mrchtr

@mrchtr mrchtr commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Adds code builds to the CLI: instead of inlining a pipeline into YAML, a project is packed into a code bundle and uploaded to the platform.

  • _service/code_bundle.py: packs a project into a deterministic code bundle
  • pipeline_extract: extractor code mode for code builds
  • _api/deployments.py: code bundle upload endpoints, reading hub-api's flat typed error body
  • deploy is gated behind a platform capability check
  • function code builds; a directory target (including .) is treated as a code build, with the entrypoint given by --entrypoint path.py:attr
  • new CLI command to invoke deployed services

mrchtr and others added 17 commits October 26, 2026 09:45
`pipeline_extract.py --mode code` imports the entrypoint without rewriting it, detects its kind
(pipeline, agent, super_component), and returns the platform I/O mapping, a best-effort input
JSON schema and a to_dict() graph. Agents and SuperComponents are wrapped like the runtime does
(`agent` / `app`), so the mapping comes from the same name-based inference as a YAML build.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
New `_service/code_bundle.py`: finds the project root (nearest pyproject.toml/.git), lists files via
git (or a walk) minus `.deepsetignore` and always-excluded secrets/envs, and packs a deterministic
tar.gz (sorted, mtime 0, owner 0/"", normalized modes, gzip mtime 0) addressed by sha256, with a
50 MB cap. Dependencies come from [project.dependencies] pinned via uv.lock, else requirements.txt,
else the haystack pin. `build_code_bundle` ties it to the extractor's code mode and renders the
settings-only config_yaml. Adds tomli for Python 3.10.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
- DeploymentsAPI.get_code_bundle / upload_code_bundle against /code_bundles, mapping the typed
  error contract: typed 404 -> upload, typed 403 -> "not enabled for this organization", untyped
  404 -> "use --build yaml". push_revision takes an optional code_bundle field.
- DeploymentService.deploy(build="code") packs the project, uploads the bundle unless the server
  has it, and only then resolves/creates the service and pushes the settings-only config_yaml with
  code_bundle. Platform YAML validation is skipped for code builds.
- `deploy --build auto|yaml` (auto = yaml). `code` is accepted but undocumented for now; it takes a
  directory target with --entrypoint path.py:attr, and --dry-run --output writes the tar.gz.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
hub-api renders a dict detail as the whole body ({code, message}); the
nested {detail: {code, message}} shape is still accepted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
A SuperComponent spread over two modules with no LLM or secrets, to try
code builds end to end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
A deepset.yaml in the project root names the entrypoint, kind and
dependencies of a code build; with one present, `deploy --build auto`
builds as code. A plain callable entrypoint is a `function` build: fixed
payload/result mapping, input schema from its signature, and deploy
prints the /run endpoint instead of chat completions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fvxds9E4oHcvivMsoZZpLd
`invoke <service> --inputs JSON` calls a function code build's /run,
`--query TEXT` goes through OpenAI-compatible chat completions for
pipeline, agent and SuperComponent services. The platform picks the
error for the wrong route, so the CLI shows its message as-is.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vmfn8GHFL2DrPGk7XjbQvN
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vmfn8GHFL2DrPGk7XjbQvN
`Path('.')` has no name, so the io-config lookup crashed and the revision
comment and source metadata came out empty. Resolve the target first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vmfn8GHFL2DrPGk7XjbQvN
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vmfn8GHFL2DrPGk7XjbQvN
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vmfn8GHFL2DrPGk7XjbQvN
Only a code build can take a directory, so --build auto no longer needs a deepset.yaml to pick it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The entrypoint comes from --entrypoint, dependencies from the io-config
or pyproject.toml, and the kind is always detected. auto builds a
directory target as code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI forces a color terminal, and the escape codes split "--build".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mrchtr
mrchtr marked this pull request as ready for review October 28, 2026 12:40
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack_enterprise_sdk
  cli.py 1338-1339, 1397-1399
  haystack_enterprise_sdk/_api
  deployments.py 492, 513, 837-838, 854
  haystack_enterprise_sdk/_service
  code_bundle.py 30, 121-122, 126, 219-220, 286-287, 299
  deployment_service.py 345, 605-614
  pipeline_extract.py 117-118, 154-155, 217, 1744-1745, 1824, 1829, 1847-1848, 1865-1866, 1875-1876, 1886-1888, 1901-1903, 1943-1959
  pipeline_transform.py
  haystack_enterprise_sdk/workflows/async_client
  deployment_client.py 222-223, 231-232
  haystack_enterprise_sdk/workflows/sync_client
  deployment_client.py 194
Project Total  

This report was generated by python-coverage-comment-action

List `code` in `deploy --build` help as experimental instead of hiding it, and
warn on stderr whenever a deploy resolves to a code build (also via `auto` on a
directory), since hiding the value never stopped `auto` from reaching it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0154qh65U5VAn77QjCc5LH99
@mrchtr
mrchtr requested a review from volkanarisli October 29, 2026 13:05
@mrchtr
mrchtr merged commit 267a207 into main Oct 30, 2026
8 checks passed
@mrchtr
mrchtr deleted the feat/code-bundle-sdk branch October 30, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants