feat(runtime): add dspy-runtime minimal-deps PyPI package#39
feat(runtime): add dspy-runtime minimal-deps PyPI package#39isaacbmiller wants to merge 2 commits intoisaac/lazy-litellmfrom
Conversation
Greptile SummaryThis PR introduces
Confidence Score: 5/5Safe to merge; the lazy-import changes are well-isolated and the build/publish pipeline is correct end-to-end. The core lazy-import work is mechanical and low-risk: moving top-level imports inside functions, adding TYPE_CHECKING guards, and introducing two small utility helpers. The build script correctly backs up and restores pyproject.toml. The workflow correctly clears dist/ before building the runtime wheel and publishes from the right directory. .github/workflows/build_and_release.yml — missing twine check for dspy-runtime and pyproject-runtime.toml absent from the version-bump PR's add-paths. Important Files Changed
Reviews (3): Last reviewed commit: "refactor: introduce dspy.utils.lazy_impo..." | Re-trigger Greptile |
d25bb92 to
09b3812
Compare
Adds a parallel dspy-runtime build that ships the same source tree as dspy but with a minimal hard-dependency set (pydantic, orjson, cloudpickle, anyio, tqdm, diskcache, json-repair, tenacity, jsonschema, plus transitional numpy/ cachetools/requests/regex). Heavy deps (litellm, openai, gepa, optuna, mcp, langchain, weaviate, anthropic) are exposed as optional extras. To support the split, the lazy-litellm pattern is extended to gepa, openai, regex, and jiter so `import dspy` works without any of those installed. `dspy.LM(...)` and `dspy.GEPA(...).compile(...)` raise clear ImportErrors instructing the user to install the corresponding extra. Build flow: scripts/build_dspy_runtime.sh swaps pyproject-runtime.toml in place of pyproject.toml, runs python -m build, and restores on exit. The release workflow publishes dspy-runtime to PyPI alongside dspy and dspy-ai. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Replaces the ad-hoc `_require_gepa()`, `_openai()`, `_get_gepa_adapter_base()`,
`_get_proposal_fn_base()`, and inline try/except patterns with two shared
helpers:
- `require(module, *, extra, feature)`: imports a module by dotted path,
raising a uniform `ImportError` ("<top> is required to use <feature>.
Install with \`pip install dspy[<extra>]\`...") if missing.
- `optional(module, attr=None, default=None)`: returns a module/attribute
or a sentinel when missing, so classes can still inherit from optional
bases at import time.
Also pins `jiter` (and its asyncer/sniffio/xxhash transitives still on this
base branch) as hard deps in `pyproject-runtime.toml`. Streaming JSON parsing
relies on jiter; without it the partial-JSON path would silently break for
dspy-runtime users who lacked litellm/openai installed transitively.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
c9e196c to
846615d
Compare
Summary
Adds a parallel
dspy-runtimebuild that ships the same source tree asdspybut with a minimal hard-dependency set, intended for production deployments where IT/security teams audit the dependency footprint.What's in the runtime build
Hard deps (only what core code paths actually need at
import dspy):pydantic,orjson,cloudpickle,anyio,tqdm,diskcache,json-repair,tenacity,jsonschemanumpy,cachetools,requests,regexOptional extras:
litellm,openai,gepa,optuna,mcp,langchain,weaviate,anthropic, plus afullaggregate.Lazy-import work (extends the lazy-litellm pattern from this base branch)
To make the split work, the existing lazy-litellm pattern is extended to a few more code paths so
import dspydoes not transitively load any of these:gepa—dspy.teleprompt.gepa.gepa{,_utils,instruction_proposal}use TYPE_CHECKING +_get_gepa_adapter_base()/_get_proposal_fn_base()helpers that fall back toobjectwhen gepa is missing.compile()raises a clearImportError.openai—dspy/clients/openai.pyuses an_openai()lazy helper.regex—dspy/adapters/json_adapter.pyanddspy/dsp/utils/dpr.pyimport inside the methods that use it (STokenizerbecomes a lazy singleton).jiter—dspy/streaming/streaming_listener.pyimports inside the two methods that use it.litellm—get_litellm()now raises a clearImportErrorwhen litellm is missing.Build / release
scripts/build_dspy_runtime.shswapspyproject-runtime.tomlinto place ofpyproject.toml, runspython -m build, and restores on exit.dspy-runtimeto PyPI alongsidedspyanddspy-ai.Tests
tests/clients/test_lazy_imports.py(renamed fromtest_litellm_lazy.py) is parametrized overlitellm,openai,regex,jiter. All 4 pass.End-to-end validation
In a clean Python 3.11 venv:
dspy.GEPA(...).compile(...)similarly raisesImportError: gepa is required to use dspy.GEPA...when the gepa extra is not installed.