Skip to content

feat: bundle local (non-pip) modules for serverless endpoints#352

Open
deanq wants to merge 13 commits into
mainfrom
deanquinanola/sls-360-local-module-bundling
Open

feat: bundle local (non-pip) modules for serverless endpoints#352
deanq wants to merge 13 commits into
mainfrom
deanquinanola/sls-360-local-module-bundling

Conversation

@deanq

@deanq deanq commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Flash could not reliably include local (non-pip) Python modules an endpoint imports — a sibling utils.py, or a helpers/ package next to the endpoint, imported via import utils / from helpers import x. This adds first-class support across both code paths that ship user code to a worker.

Resolves SLS-360.

  • Live serverless (@Endpoint(...).run(...), flash dev LB dispatch): the endpoint's transitive local-import closure is now shipped inline alongside the function source.
  • Build/deploy (flash build / flash deploy): local modules an endpoint imports are force-included in the artifact even when the ignore filter would drop them, and the build fails loudly (clean error, not a traceback) if an endpoint's local import can't be resolved.

What changed

  • FunctionRequest.modules: dict[str, str] — additive protocol field (POSIX relative path → source text), default empty. Backward compatible: old workers ignore it, old SDKs send nothing. Shared contract with the worker repo — see the companion worker PR: feat: import shipped local modules before executing user code runpod-workers/flash#100.
  • stubs/local_modules.py — a shared, pure resolve_local_modules(...) that walks the transitive local-import closure (absolute, relative, and importlib.import_module("literal") imports, at module level and inside function bodies), classifies local vs stdlib/installed, pulls in package __init__.py, terminates on cycles, warns on non-literal dynamic imports, and raises LocalModuleResolutionError on unresolved relative imports / out-of-root files.
  • Live sendersstubs/live_serverless.py and stubs/load_balancer_sls.py populate modules from the resolved closure, with an 8 MiB inline cap raising LocalModulePayloadTooLargeError (points users to flash deploy for larger local dependencies).
  • Worker side (this repo)runtime/module_loader.py::materialized_modules writes shipped modules to a temp dir on sys.path and cleans up; wired into runtime/lb_handler.py's /execute so the temp dir stays live through the function call (not just exec).
  • Build pathaugment_with_local_modules in cli/commands/build.py force-includes resolved local files past the ignore filter. Strictness is scoped to endpoint files (@Endpoint/@remote): an endpoint with an unresolved local import fails the build loudly; an incidental non-endpoint file that fails resolution is warned and skipped (no regression on unrelated files).

Test plan

  • Unit: resolver (transitive closure, relative/dynamic/star imports, cycles, package inits, stdlib/external classification), materializer (writes files, restores sys.path on exception, no-op when empty), both live senders populate modules, LB /execute def-now/call-later import regression, build force-include + endpoint-only-strict A/B.
  • make quality-check green (86%+ coverage).
  • Deploy path verified end-to-end with real flash build: an ignored test_*.py sibling imported by an endpoint is force-included into artifact.tar.gz; an endpoint with an unresolvable relative import fails the build with a clean error (exit 1, no traceback).

Coordinated release

The worker-side materializer requires the companion worker PR (runpod-workers/flash#100) to be released for the live inline-shipping path to work end-to-end. The deploy path is independent (bundled files sit on the worker filesystem regardless of image version). Live-path E2E against real endpoints is a gating step once the worker image is rebuilt/published.

Follow-ups (non-blocking)

  • Live path silently omits parent-directory absolute imports (only relative imports fail loudly) — documented constraint; endpoint should sit at/above its local deps or use flash deploy.
  • sys.path materialization assumes one invocation at a time per worker; isolate per-invocation if concurrent execution is enabled.
  • Endpoint detection in the build path handles decorator forms; the ep = Endpoint(...) assignment form is not yet detected (fails safe toward warn).

@capy-ai

capy-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Capy auto-review is paused for this organization because the usage-cycle auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@promptless

promptless Bot commented Jul 6, 2026

Copy link
Copy Markdown

Promptless prepared a documentation update related to this change.

Triggered by PR #352: feat: bundle local (non-pip) modules for serverless endpoints

Documents Flash's new first-class support for bundling local (non-pip) Python modules that endpoints import: a new "Import local modules" section in the Flash endpoints guide (supported import forms, transitive resolution, the 8 MiB live-execution cap, and the parent-directory absolute-import constraint), a force-include note in the build CLI reference, and two troubleshooting entries for the new resolution and payload-size errors.

Review: Document Flash local (non-pip) module bundling for endpoints

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.

1 participant