← Back to 003 decomposition

001 gemini

Documentation for 001_gemini from the Ds ex repository.

Yes, absolutely. A comprehensive analysis of the DSPy codebase reveals that it is not a monolithic entity. It is, in fact, a layered system where a core, abstract framework is tightly coupled with a specific implementation of that framework’s concepts.

It is entirely possible—and architecturally desirable—to separate DSPy into a core framework (dspy-core) and a default implementation (dspy-runtime or simply dspy). This would significantly improve modularity, extensibility, and clarity for the entire ecosystem.

Let’s conduct that analysis and outline what such a separation would look like.


Architectural Analysis: Decoupling the DSPy Framework from its Implementation

Core Thesis

The current dspy library intertwines two distinct concerns:

  1. The DSPy Programming Model (The “Framework”): A set of abstract interfaces, data contracts, and philosophical principles for building and optimizing LM programs. This includes concepts like Signature, Module, Teleprompter, and Metric.
  2. A Default Runtime (The “Implementation”): A specific, opinionated implementation of these concepts. This includes ChainOfThought, BootstrapFewShot, ChatAdapter, and the LM client that uses litellm.

By separating these, we can create a more powerful and flexible ecosystem where alternative runtimes and components can be developed and plugged into the core framework.


Proposed Library Structure

The separation would result in two (or more) distinct packages:

  1. dspy-core: A minimal, dependency-light package defining the abstract interfaces and data structures. This is the “framework”.
  2. dspy (or dspy-runtime): The existing, feature-rich package that depends on dspy-core and provides the default, “batteries-included” implementation of modules, optimizers, and adapters.

Here is a breakdown of which components would belong to each package.


1. dspy-core: The Abstract Framework

Objective: To define the stable, abstract contracts of the DSPy programming model. This package would have almost zero heavy dependencies, focusing on interfaces and data structures. A user could theoretically build a complete, custom DSPy-compliant system using only dspy-core and their own implementations.

Components of dspy-core:

ComponentCurrent Location (in dspy)Role in dspy-core
Primitives & Data Contractsprimitives/example.py, primitives/prediction.pydspy_core.Example, dspy_core.Prediction: The fundamental, immutable data containers.
Module Abstractionprimitives/module.py, primitives/program.pydspy_core.BaseModule, dspy_core.Program: Abstract base classes defining the forward method, parameter management (named_parameters), and state serialization (dump_state/load_state). This is the core contract for all executable components.
Signature Abstractionsignatures/signature.py, signatures/field.pydspy_core.Signature, dspy_core.InputField, dspy_core.OutputField: The declarative system for defining I/O contracts. The SignatureMeta metaclass and the logic for creating signatures from strings or classes belong here.
Client Abstractionsclients/base_lm.pydspy_core.BaseLM, dspy_core.BaseRM: Abstract base classes defining the interface for Language Model and Retrieval Model clients (__call__, forward). They would not contain any implementation logic (like litellm calls).
Adapter Abstractionadapters/base.pydspy_core.Adapter: An abstract base class defining the format and parse methods. This establishes the contract for any component that translates between Signatures and raw LM inputs/outputs.
Optimizer Abstractionteleprompt/teleprompt.pydspy_core.Teleprompter: The abstract base class for all optimizers, defining the compile method signature.
Metric AbstractionN/A (implicit)dspy_core.Metric: Could be formalized as an abstract base class with a __call__(example, prediction, trace) method, making metrics a first-class, pluggable component.
Evaluation Abstractionevaluate/evaluate.pydspy_core.Evaluate: The interface for the evaluation harness. The core logic of iterating over a dataset and calling a metric could live here, but the parallelization (ParallelExecutor) would be part of the default runtime.

Dependencies of dspy-core:

  • pydantic (for Signature implementation)
  • That’s nearly it. It would be extremely lightweight.

2. dspy (The Default Runtime & Component Library)

Objective: To provide a rich, batteries-included implementation of the dspy-core interfaces. This is what most users would interact with directly. It would contain all the concrete modules, optimizers, and adapters that make DSPy so powerful out of the box.

Components of dspy (The Implementation):

ComponentCurrent Location (in dspy)Role in dspy (The Runtime)
Concrete Modulespredict/, retrieve/dspy.Predict, dspy.ChainOfThought, dspy.ReAct, dspy.Retrieve: These are all concrete implementations of the dspy_core.Program interface.
Concrete LM/RM Clientsclients/, retrieve/dspy.LM, dspy.ColBERTv2, dspy.WeaviateRM, etc.: These are concrete implementations of dspy_core.BaseLM and dspy_core.BaseRM, containing the actual logic for litellm calls, HTTP requests, etc.
Concrete Adaptersadapters/dspy.ChatAdapter, dspy.JSONAdapter, dspy.TwoStepAdapter: Concrete implementations of the dspy_core.Adapter interface.
Concrete Optimizersteleprompt/dspy.BootstrapFewShot, dspy.MIPROv2, dspy.BootstrapFinetune: Concrete implementations of the dspy_core.Teleprompter interface. These contain the complex logic for bootstrapping, search, and finetuning.
Core Infrastructureutils/, clients/cache.pydspy.ParallelExecutor, dspy.Cache: The default, high-performance implementations for parallelism and caching.
Standard Metricsevaluate/metrics.pydspy.evaluate.answer_exact_match, etc.: A library of common, ready-to-use metric functions.

Dependencies of dspy:

  • dspy-core (a direct dependency)
  • litellm, backoff, tenacity, diskcache, optuna, pandas, numpy, and all the other heavy dependencies from the current pyproject.toml.

3. Potential for a Multi-Framework Ecosystem

This separation opens the door for multiple, competing implementations to flourish, all targeting the same core DSPy programming model.

  1. dspy-tensorflow or dspy-jax:

    • Could provide an alternative runtime where modules and optimizers are built on TensorFlow or JAX instead of PyTorch (for concepts that might leverage them).
    • The BootstrapFinetune optimizer, for instance, could have a backend that uses Keras for training instead of Hugging Face’s transformers.
  2. dspy-lite:

    • A minimal runtime with only the most essential modules (Predict, ChainOfThought) and a simple LabeledFewShot optimizer. It would have fewer dependencies and be optimized for edge devices or environments where a full-fledged optimizer like MIPROv2 is too heavy.
  3. dspy-enterprise:

    • A hypothetical third-party package could implement the dspy-core interfaces with enterprise-grade features.
    • dspy_enterprise.LM could have built-in integration with proprietary model gardens and secret management systems.
    • dspy_enterprise.Cache could be backed by Redis or another distributed cache.
    • dspy_enterprise.MIPROv2 could use a distributed job scheduler (like Ray or Spark) for its evaluation steps instead of a local thread pool.
  4. DSPEx (Our Elixir Port):

    • In this new world, DSPEx becomes another implementation of the abstract DSPy philosophy, but for a different virtual machine (the BEAM).
    • A DSPEx.Signature would be the Elixir equivalent of a dspy_core.Signature.
    • A DSPEx.Program behaviour would be the equivalent of the dspy_core.Program abstract base class.
    • This provides a clear conceptual bridge between the two ecosystems.

Diagram: The Decoupled DSPy Ecosystem

graph TD subgraph "Core Framework" Core["`dspy-core`
(Abstract Interfaces & Data Contracts)
- BaseModule
- Signature
- BaseLM
- Teleprompter"] end subgraph "Implementations & Runtimes" Default["`dspy`
(Default Runtime)
- ChainOfThought
- MIPROv2 Optimizer
- litellm-based LM"] Lite["`dspy-lite`
(Minimal Runtime)
- Predict Only
- LabeledFewShot Only"] Enterprise["`dspy-enterprise`
(3rd Party Runtime)
- Redis Cache
- Ray-based Optimizer"] Elixir["`DSPEx`
(Elixir/BEAM Runtime)
- GenServer-based Modules
- OTP-based Optimizer"] end subgraph "User Applications" AppA["User App A"] AppB["User App B"] AppC["User App C"] end Core --> Default Core --> Lite Core --> Enterprise Default --> AppA Lite --> AppB Enterprise --> AppC note right of Elixir Conceptually, `DSPEx` also implements the principles defined in `dspy-core`, but for the Elixir ecosystem. end style Core fill:#cde,stroke:#333,stroke-width:2px

Conclusion

Yes, it is not only possible but highly beneficial to separate DSPy into a core framework and one or more implementations.

  • For DSPy developers, this separation would clarify the architecture, enforce cleaner boundaries between components, and make the system easier to maintain and test.
  • For the community, it would create a stable, abstract target (dspy-core) for innovation. New optimizers, modules, or infrastructure backends could be developed independently without needing to be merged into the main dspy repository.
  • For the broader AI ecosystem, it would position the DSPy programming model as a standard, much like how JAX-RS or JDBC are standards in the Java world, with multiple compatible implementations.

This architectural refactoring would represent a significant maturation of the DSPy project, moving it from a powerful library into a foundational and extensible framework for the future of AI programming.