DSPex (DSPex v0.11.0)

Copy Markdown View Source

DSPex - DSPy for Elixir via SnakeBridge.

Minimal wrapper that provides transparent access to DSPy through SnakeBridge's Universal FFI, alongside the generated Dspy.* bindings for the full API surface.

Quick Start

DSPex.run(fn ->
  # Configure LM
  lm = DSPex.lm!("gemini/gemini-flash-lite-latest")
  DSPex.configure!(lm: lm)

  # Create predictor and run
  predict = DSPex.call!("dspy", "Predict", ["question -> answer"])
  result = DSPex.method!(predict, "forward", [], question: "What is 2+2?")

  # Get the answer
  answer = DSPex.attr!(result, "answer")
  IO.puts("Answer: #{answer}")
end)

Timeout Configuration

DSPex leverages SnakeBridge 0.13+'s timeout architecture for LLM workloads. By default, all DSPy calls use the :ml_inference profile (10 minute timeout).

Timeout Profiles

ProfileTimeoutUse Case
:default2 minStandard Python calls
:streaming30 minStreaming responses
:ml_inference10 minLLM inference (DSPex default)
:batch_job1 hourLong-running batch operations

Per-Call Timeout Override

Override timeout for individual calls using __runtime__ option:

# Use a different profile
DSPex.method!(predict, "forward", [],
  question: "Complex question",
  __runtime__: [timeout_profile: :batch_job]
)

# Set exact timeout in milliseconds
DSPex.method!(predict, "forward", [],
  question: "Quick question",
  __runtime__: [timeout: 30_000]  # 30 seconds
)

Global Configuration

Configure timeouts in config/config.exs:

config :snakebridge,
  runtime: [
    library_profiles: %{"dspy" => :ml_inference},
    # Or set global default:
    # timeout_profile: :ml_inference
  ]

Architecture

DSPex uses SnakeBridge's Universal FFI to call DSPy directly:

Elixir (DSPex.call/4)
    
SnakeBridge.call/4
    
Snakepit gRPC
    
Python DSPy
    
LLM Providers

All Python lifecycle is managed automatically by Snakepit.

Summary

Functions

Get an attribute from a Python object reference.

Bang version of attr/3.

Encode binary data as Python bytes.

Call any DSPy function or class.

Bang version - raises on error, returns value directly.

Create a ChainOfThought module.

Bang version of chain_of_thought/2.

Configure DSPy global settings.

Bang version of configure/1 - raises on error.

Get a module attribute.

Bang version of get/2.

Create a DSPy language model.

Bang version of lm/2 - raises on error.

Call a method on a Python object reference.

Create a Predict module.

Bang version of predict/2.

Check if a value is a Python object reference.

Run DSPex code with automatic Python lifecycle management.

Set an attribute on a Python object reference.

Create a timeout option for exact milliseconds.

Timeout profile atoms for use with __runtime__ option.

Add timeout configuration to options.

Functions

attr(ref, attribute, opts \\ [])

Get an attribute from a Python object reference.

attr!(ref, attribute, opts \\ [])

Bang version of attr/3.

bytes(data)

Encode binary data as Python bytes.

call(module, function, args \\ [], opts \\ [])

Call any DSPy function or class.

Examples

{:ok, result} = DSPex.call("dspy", "Predict", ["question -> answer"])
{:ok, result} = DSPex.call("dspy.teleprompt", "BootstrapFewShot", [], metric: metric)

call!(module, function, args \\ [], opts \\ [])

Bang version - raises on error, returns value directly.

chain_of_thought(signature, opts \\ [])

Create a ChainOfThought module.

Examples

{:ok, cot} = DSPex.chain_of_thought("question -> answer")

chain_of_thought!(signature, opts \\ [])

Bang version of chain_of_thought/2.

configure(opts \\ [])

Configure DSPy global settings.

Examples

:ok = DSPex.configure(lm: lm)
:ok = DSPex.configure(lm: lm, rm: retriever)

configure!(opts \\ [])

Bang version of configure/1 - raises on error.

get(module, attr)

Get a module attribute.

get!(module, attr)

Bang version of get/2.

lm(model, opts \\ [])

Create a DSPy language model.

Examples

{:ok, lm} = DSPex.lm("gemini/gemini-flash-lite-latest")
{:ok, lm} = DSPex.lm("anthropic/claude-3-sonnet-20240229", temperature: 0.7)

lm!(model, opts \\ [])

Bang version of lm/2 - raises on error.

method(ref, method, args \\ [], opts \\ [])

Call a method on a Python object reference.

method!(ref, method, args \\ [], opts \\ [])

Bang version of method/4.

predict(signature, opts \\ [])

Create a Predict module.

Examples

{:ok, predict} = DSPex.predict("question -> answer")
{:ok, predict} = DSPex.predict("context, question -> answer")

predict!(signature, opts \\ [])

Bang version of predict/2.

ref?(value)

Check if a value is a Python object reference.

run(fun, opts \\ [])

Run DSPex code with automatic Python lifecycle management.

Wraps your code in Snakepit.run_as_script/2 which:

  • Starts the Python process pool
  • Runs your code
  • Cleans up on exit

Pass halt: true in opts if you need to force the BEAM to exit (for example, when running inside wrapper scripts).

DSPex restarts Snakepit by default so it owns the runtime and can close persistent resources (like DETS) cleanly. Pass restart: false to reuse an already-started Snakepit instance.

Example

DSPex.run(fn ->
  lm = DSPex.lm!("gemini/gemini-flash-lite-latest")
  DSPex.configure!(lm: lm)
  # ... your DSPy code
end)

set_attr(ref, attribute, value, opts \\ [])

Set an attribute on a Python object reference.

timeout_ms(milliseconds)

Create a timeout option for exact milliseconds.

Returns a keyword list ready to merge into call options.

Examples

DSPex.method!(predict, "forward", [],
  Keyword.merge([question: "test"], DSPex.timeout_ms(120_000))
)

timeout_profile(profile)

Timeout profile atoms for use with __runtime__ option.

Returns a keyword list ready to merge into call options.

Examples

DSPex.method!(predict, "forward", [],
  Keyword.merge([question: "test"], DSPex.timeout_profile(:batch_job))
)

with_timeout(opts, timeout_opts)

Add timeout configuration to options.

This is a convenience helper for adding __runtime__ timeout options.

Options

  • :timeout - Exact timeout in milliseconds
  • :timeout_profile - Use a predefined profile (:default, :streaming, :ml_inference, :batch_job)

Examples

# Set exact timeout
opts = DSPex.with_timeout([], timeout: 60_000)  # 1 minute
DSPex.method!(predict, "forward", [], Keyword.merge(opts, question: "..."))

# Use batch profile for long operations
opts = DSPex.with_timeout([question: "complex"], timeout_profile: :batch_job)
DSPex.method!(predict, "forward", [], opts)

# Inline usage
DSPex.method!(predict, "forward", [],
  DSPex.with_timeout([question: "test"], timeout: 30_000)
)