dspy-ruby_llm 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5131c4c5545dc867c284d046a0f43cf8bb109f9f0417f4154cf9cfad4f73b55b
4
- data.tar.gz: c10d6e0614e3bd3ae92d88ef93197e86370d25e9b49d7a4fb946928b3039f5cd
3
+ metadata.gz: 954de47d2c188958e0f761dc8d4aa62d6c33feacc049f38eb9d3ddd31ba48f18
4
+ data.tar.gz: 2ccfa1e9a88a273627f717d9b5af87a64e04b478e6e2407e32e2b6a5c71ea50c
5
5
  SHA512:
6
- metadata.gz: 781466444aae497b51952ec0e2914dc551ef8f6008449bc95676e4fc84a3aec2bebf27f1b5673901c9d8f19c3f86f0f692477ddd98a2ef1bf3faaa55788a0576
7
- data.tar.gz: 10b9cdb52396c6f089cdd32c40f41363f463f1e1acc19fcaa741cbf2668e79b8af81f898170afcadfdb0e05aa2dff0b601fa22e67cd2397a448ee9e0ed2e6ec6
6
+ metadata.gz: 5816b736e227cf2661923d57485b4e1c75d447234523daa07890a1455464590846957505e43be7f3baefecbfc89ac3a910b83b5b984ac0fae5a4b98784e6ee10
7
+ data.tar.gz: 1b1787a237e8184298635afe70f5b9ba2e99151e3826a715d8805c246996967c0ed4e9cef2a341347d7c5e29b4579ad3e54aa5413452d6fe280bc373c468856a
data/README.md CHANGED
@@ -6,96 +6,16 @@
6
6
  [![Documentation](https://img.shields.io/badge/docs-oss.vicente.services%2Fdspy.rb-blue)](https://oss.vicente.services/dspy.rb/)
7
7
  [![Discord](https://img.shields.io/discord/1161519468141355160?label=discord&logo=discord&logoColor=white)](https://discord.gg/zWBhrMqn)
8
8
 
9
- **Build reliable LLM applications in idiomatic Ruby using composable, type-safe modules.**
9
+ **Build typed agents and model-backed programs in Ruby.**
10
10
 
11
- DSPy.rb is the Ruby port of Stanford's [DSPy](https://dspy.ai). Instead of wrestling with brittle prompt strings, you define typed signatures and let the framework handle the rest. Prompts become functions. LLM calls become predictable.
12
- The `1.x` line is the stable release track for production Ruby LLM applications.
11
+ DSPy.rb brings [DSPy](https://dspy.ai)'s signature, module, agent, and optimizer model to Ruby, with Sorbet types and Ruby-native integrations. A signature declares a task as typed inputs and outputs. A module chooses how to execute it. Ruby composes modules into programs, while `ReAct` adds a bounded loop when the model should choose the next action. DSPy.rb builds the provider request and validates the returned shape.
13
12
 
14
- ```ruby
15
- require 'dspy'
16
-
17
- DSPy.configure do |c|
18
- c.lm = DSPy::LM.new('openai/gpt-4o-mini', api_key: ENV['OPENAI_API_KEY'])
19
- end
20
-
21
- class Summarize < DSPy::Signature
22
- description "Summarize the given text in one sentence."
23
-
24
- input do
25
- const :text, String
26
- end
27
-
28
- output do
29
- const :summary, String
30
- end
31
- end
32
-
33
- summarizer = DSPy::Predict.new(Summarize)
34
- result = summarizer.call(text: "DSPy.rb brings structured LLM programming to Ruby...")
35
- puts result.summary
36
- ```
37
-
38
- That's it. No prompt templates. No JSON parsing. No prayer-based error handling.
39
-
40
- ## Installation
41
-
42
- ```ruby
43
- # Gemfile
44
- gem 'dspy'
45
- gem 'dspy-openai' # For OpenAI, OpenRouter, or Ollama
46
- # gem 'dspy-anthropic' # For Claude
47
- # gem 'dspy-gemini' # For Gemini
48
- # gem 'dspy-ruby_llm' # For 12+ providers via RubyLLM
49
- ```
50
-
51
- ```bash
52
- bundle install
53
- ```
54
-
55
- ## Quick Start
56
-
57
- ### Configure Your LLM
58
-
59
- ```ruby
60
- # OpenAI
61
- DSPy.configure do |c|
62
- c.lm = DSPy::LM.new('openai/gpt-4o-mini',
63
- api_key: ENV['OPENAI_API_KEY'],
64
- structured_outputs: true)
65
- end
66
-
67
- # Anthropic Claude
68
- DSPy.configure do |c|
69
- c.lm = DSPy::LM.new('anthropic/claude-sonnet-4-20250514',
70
- api_key: ENV['ANTHROPIC_API_KEY'])
71
- end
72
-
73
- # Google Gemini
74
- DSPy.configure do |c|
75
- c.lm = DSPy::LM.new('gemini/gemini-2.5-flash',
76
- api_key: ENV['GEMINI_API_KEY'])
77
- end
78
-
79
- # Ollama (local, free)
80
- DSPy.configure do |c|
81
- c.lm = DSPy::LM.new('ollama/llama3.2')
82
- end
83
-
84
- # OpenRouter (200+ models)
85
- DSPy.configure do |c|
86
- c.lm = DSPy::LM.new('openrouter/deepseek/deepseek-chat-v3.1:free',
87
- api_key: ENV['OPENROUTER_API_KEY'])
88
- end
89
- ```
90
-
91
- ### Define a Signature
13
+ The `1.x` series is the current stable release line.
92
14
 
93
- Signatures are typed contracts for LLM operations. Define inputs, outputs, and let DSPy handle the prompt:
15
+ In a configured application, the task contract and call look like this:
94
16
 
95
17
  ```ruby
96
18
  class Classify < DSPy::Signature
97
- description "Classify sentiment of a given sentence."
98
-
99
19
  class Sentiment < T::Enum
100
20
  enums do
101
21
  Positive = new('positive')
@@ -105,7 +25,7 @@ class Classify < DSPy::Signature
105
25
  end
106
26
 
107
27
  input do
108
- const :sentence, String, description: 'The sentence to analyze'
28
+ const :sentence, String
109
29
  end
110
30
 
111
31
  output do
@@ -115,107 +35,39 @@ class Classify < DSPy::Signature
115
35
  end
116
36
 
117
37
  classifier = DSPy::Predict.new(Classify)
118
- result = classifier.call(sentence: "This book was super fun to read!")
119
-
120
- result.sentiment # => #<Sentiment::Positive>
121
- result.confidence # => 0.92
38
+ result = classifier.call(sentence: "This book was fun to read!")
122
39
  ```
123
40
 
124
- ### Chain of Thought
125
-
126
- For complex reasoning, use `ChainOfThought` to get step-by-step explanations:
127
-
128
- ```ruby
129
- solver = DSPy::ChainOfThought.new(MathProblem)
130
- result = solver.call(problem: "If a train travels 120km in 2 hours, what's its speed?")
131
-
132
- result.reasoning # => "Speed = Distance / Time = 120km / 2h = 60km/h"
133
- result.answer # => "60 km/h"
134
- ```
41
+ Define a typed task contract instead of maintaining an output template. Receive validated Ruby values instead of parsing provider JSON. Handle configuration, transport, and validation errors explicitly.
135
42
 
136
- ### ReAct Agents
137
-
138
- Build agents that use tools to accomplish tasks:
139
-
140
- ```ruby
141
- class SearchTool < DSPy::Tools::Base
142
- tool_name "search"
143
- tool_description "Search for information"
144
-
145
- sig { params(query: String).returns(String) }
146
- def call(query:)
147
- # Your search implementation
148
- "Result 1, Result 2"
149
- end
150
- end
151
-
152
- agent = DSPy::ReAct.new(ResearchTask, tools: [SearchTool.new], max_iterations: 5)
153
- result = agent.call(question: "What's the latest on Ruby 3.4?")
154
- ```
43
+ ## Start Here
155
44
 
156
- ## What's Included
45
+ The [Quick Start](https://oss.vicente.services/dspy.rb/getting-started/quick-start/) is the complete supported path: install the core and provider gems, configure a key, save a program, and run it.
157
46
 
158
- **Core Modules**: Predict, ChainOfThought, ReAct agents, and composable pipelines.
47
+ Use [Installation](https://oss.vicente.services/dspy.rb/getting-started/installation/) to choose a provider. The [package and capability matrix](https://oss.vicente.services/dspy.rb/getting-started/packages/) records exact gem names, require behavior, support labels, and model or SDK boundaries.
159
48
 
160
- **Type Safety**: Sorbet-based runtime validation. Enums, unions, nested structs—all work.
49
+ ## Mental Model
161
50
 
162
- **Multimodal**: Image analysis with `DSPy::Image` for vision-capable models.
51
+ - A `DSPy::Signature` defines the task contract.
52
+ - `DSPy::Predict` and other modules choose an execution strategy.
53
+ - Ordinary Ruby owns fixed sequencing, branching, persistence, permissions, and failure policy.
54
+ - Results cross a runtime validation boundary; validation establishes shape, not factual correctness.
163
55
 
164
- **Observability**: Zero-config Langfuse integration via OpenTelemetry. Non-blocking, production-ready.
165
-
166
- **Optimization**: MIPROv2 (Bayesian optimization) and GEPA (genetic evolution) for prompt tuning.
167
-
168
- **Provider Support**: OpenAI, Anthropic, Gemini, Ollama, and OpenRouter via official SDKs.
169
-
170
- ## Documentation
171
-
172
- **[Full Documentation](https://oss.vicente.services/dspy.rb/)** — Getting started, core concepts, advanced patterns.
173
-
174
- **[llms.txt](https://oss.vicente.services/dspy.rb/llms.txt)** — LLM-friendly reference for AI assistants.
175
-
176
- ### Claude Skill
177
-
178
- A [Claude Skill](https://github.com/vicentereig/dspy-rb-skill) is available to help you build DSPy.rb applications:
179
-
180
- ```bash
181
- # Claude Code — install from the vicentereig/engineering marketplace
182
- claude install-skill vicentereig/engineering --skill dspy-rb
183
- ```
184
-
185
- For Claude.ai Pro/Max, download the [skill ZIP](https://github.com/vicentereig/dspy-rb-skill/archive/refs/heads/main.zip) and upload via Settings > Skills.
186
-
187
- ## Examples
188
-
189
- The [examples/](examples/) directory has runnable code for common patterns:
190
-
191
- - Sentiment classification
192
- - ReAct agents with tools
193
- - Image analysis
194
- - Prompt optimization
195
-
196
- ```bash
197
- bundle exec ruby examples/basic_search_agent.rb
198
- ```
56
+ ### ReAct Agents
199
57
 
200
- ## Optional Gems
58
+ Use `ReAct` when the model needs to choose among typed Ruby tools inside an iteration bound. The application still owns tool authorization, side effects, budgets, and errors. See [Predictors](https://oss.vicente.services/dspy.rb/core-concepts/predictors/) and [Toolsets](https://oss.vicente.services/dspy.rb/core-concepts/toolsets/).
201
59
 
202
- DSPy.rb ships sibling gems for features with heavier dependencies. Add them as needed:
60
+ ## Explore
203
61
 
204
- | Gem | What it does |
205
- | --- | --- |
206
- | `dspy-datasets` | Dataset helpers, Parquet/Polars tooling |
207
- | `dspy-evals` | Evaluation harness with metrics and callbacks |
208
- | `dspy-miprov2` | Bayesian optimization for prompt tuning |
209
- | `dspy-gepa` | Genetic-Pareto prompt evolution |
210
- | `dspy-o11y-langfuse` | Auto-configure Langfuse tracing |
211
- | `dspy-code_act` | Think-Code-Observe agents |
212
- | `dspy-deep_search` | Production DeepSearch with Exa |
62
+ - [Documentation](https://oss.vicente.services/dspy.rb/) task-oriented guides and reference
63
+ - [Examples](examples/) repository demos indexed by capability and prerequisites
64
+ - [llms.txt](https://oss.vicente.services/dspy.rb/llms.txt) generated reference for AI assistants
213
65
 
214
- See [the full list](https://oss.vicente.services/dspy.rb/getting-started/installation/) in the docs.
66
+ Provider adapters, optimizers, observability exporters, datasets, and code-executing agents are separate packages when they add dependencies. Check the package matrix before selecting one; package availability does not guarantee uniform provider or model behavior.
215
67
 
216
68
  ## Contributing
217
69
 
218
- Feedback is invaluable. If you encounter issues, [open an issue](https://github.com/vicentereig/dspy.rb/issues). For suggestions, [start a discussion](https://github.com/vicentereig/dspy.rb/discussions).
70
+ For bugs, [open an issue](https://github.com/vicentereig/dspy.rb/issues). For suggestions, [start a discussion](https://github.com/vicentereig/dspy.rb/discussions).
219
71
 
220
72
  Want to contribute code? Reach out: hey at vicente.services
221
73
 
@@ -1,174 +1,53 @@
1
- # DSPy RubyLLM Adapter
1
+ # DSPy RubyLLM Adapter (Preview)
2
2
 
3
- Unified access to 12+ LLM providers through a single adapter using [RubyLLM](https://rubyllm.com).
3
+ `dspy-ruby_llm` delegates model lookup, provider selection, request transport, and capability discovery to [RubyLLM](https://rubyllm.com). The package is preview: provider coverage is not a uniform capability promise.
4
4
 
5
- ## Installation
5
+ See the [package and capability matrix](https://oss.vicente.services/dspy.rb/getting-started/packages/) for canonical status and the files shared with the core gem.
6
6
 
7
- Add to your Gemfile:
7
+ ## Prerequisites
8
8
 
9
- ```ruby
10
- gem 'dspy-ruby_llm'
11
- ```
12
-
13
- ## Usage
9
+ - Ruby, Bundler, and `gem "dspy-ruby_llm"`
10
+ - RubyLLM credentials and configuration for the selected provider
11
+ - a model ID recognized by RubyLLM, or an explicit `provider:` override
14
12
 
15
- ### Using Existing RubyLLM Configuration (Recommended)
16
-
17
- If you already have RubyLLM configured, DSPy will use your existing setup automatically:
13
+ ## Install and Run
18
14
 
19
15
  ```ruby
20
- # Your existing RubyLLM configuration
21
- RubyLLM.configure do |config|
22
- config.openai_api_key = ENV['OPENAI_API_KEY']
23
- config.anthropic_api_key = ENV['ANTHROPIC_API_KEY']
24
- end
25
-
26
- # DSPy uses your existing config - no api_key needed!
27
- lm = DSPy::LM.new("ruby_llm/gpt-4o")
28
- lm = DSPy::LM.new("ruby_llm/claude-sonnet-4")
16
+ gem "dspy-ruby_llm"
29
17
  ```
30
18
 
31
- ### Model ID Format
32
-
33
- Use `ruby_llm/{model_id}` format where `model_id` is the RubyLLM model identifier:
19
+ Save this as `ruby_llm_smoke.rb`:
34
20
 
35
21
  ```ruby
36
- # With explicit API key (creates scoped context)
37
- lm = DSPy::LM.new("ruby_llm/gpt-4o", api_key: ENV['OPENAI_API_KEY'])
38
-
39
- # Or use global RubyLLM config (no api_key needed)
40
- lm = DSPy::LM.new("ruby_llm/gpt-4o")
41
- lm = DSPy::LM.new("ruby_llm/claude-sonnet-4")
42
- lm = DSPy::LM.new("ruby_llm/gemini-1.5-pro")
22
+ require "dspy"
23
+ require "dspy/ruby_llm"
43
24
 
44
- # For models not in RubyLLM registry, specify provider explicitly
45
- lm = DSPy::LM.new("ruby_llm/llama3.2", provider: 'ollama')
46
- ```
47
-
48
- The adapter detects the provider from RubyLLM's model registry. For models not in the registry, use the `provider:` option.
49
-
50
- ### Provider Override
51
-
52
- For custom deployments or models not in the registry, explicitly specify the provider:
53
-
54
- ```ruby
55
- # OpenRouter
56
- lm = DSPy::LM.new("ruby_llm/anthropic/claude-3-opus",
57
- api_key: ENV['OPENROUTER_API_KEY'],
58
- provider: 'openrouter'
59
- )
60
-
61
- # Custom model with explicit provider
62
- lm = DSPy::LM.new("ruby_llm/my-custom-model",
63
- api_key: ENV['OPENAI_API_KEY'],
64
- provider: 'openai',
65
- base_url: 'https://custom-endpoint.com/v1'
66
- )
67
-
68
- # AWS Bedrock - configure RubyLLM globally first
69
- RubyLLM.configure do |c|
70
- c.bedrock_api_key = ENV['AWS_ACCESS_KEY_ID']
71
- c.bedrock_secret_key = ENV['AWS_SECRET_ACCESS_KEY']
72
- c.bedrock_region = 'us-east-1'
25
+ RubyLLM.configure do |config|
26
+ config.openai_api_key = ENV.fetch("OPENAI_API_KEY")
73
27
  end
74
- lm = DSPy::LM.new("ruby_llm/anthropic.claude-3-5-sonnet", provider: 'bedrock')
75
28
 
76
- # VertexAI - configure RubyLLM globally first
77
- RubyLLM.configure do |c|
78
- c.vertexai_project_id = 'your-project-id'
79
- c.vertexai_location = 'us-central1'
80
- end
81
- lm = DSPy::LM.new("ruby_llm/gemini-pro", provider: 'vertexai')
29
+ lm = DSPy::LM.new("ruby_llm/gpt-4o")
30
+ response = lm.raw_chat([{ role: "user", content: "Reply with: adapter ready" }])
31
+ puts response.content
82
32
  ```
83
33
 
84
- ### Supported Providers
85
-
86
- | Provider | Example Model ID | Notes |
87
- |----------|------------------|-------|
88
- | OpenAI | `ruby_llm/gpt-4o` | In RubyLLM registry |
89
- | Anthropic | `ruby_llm/claude-sonnet-4` | In RubyLLM registry |
90
- | Google Gemini | `ruby_llm/gemini-1.5-pro` | In RubyLLM registry |
91
- | DeepSeek | `ruby_llm/deepseek-chat` | In RubyLLM registry |
92
- | Mistral | `ruby_llm/mistral-large` | In RubyLLM registry |
93
- | Ollama | `ruby_llm/llama3.2` | Use `provider: 'ollama'`, no API key needed |
94
- | AWS Bedrock | `ruby_llm/anthropic.claude-3-5-sonnet` | Configure RubyLLM globally |
95
- | VertexAI | `ruby_llm/gemini-pro` | Configure RubyLLM globally |
96
- | OpenRouter | `ruby_llm/anthropic/claude-3-opus` | Use `provider: 'openrouter'` |
97
- | Perplexity | `ruby_llm/llama-3.1-sonar-large` | Use `provider: 'perplexity'` |
98
- | GPUStack | `ruby_llm/model-name` | Use `provider: 'gpustack'` |
99
-
100
- ### Configuration Options
101
-
102
- ```ruby
103
- lm = DSPy::LM.new("ruby_llm/gpt-4o",
104
- api_key: ENV['OPENAI_API_KEY'], # API key (or use global RubyLLM config)
105
- base_url: 'https://custom.com/v1', # Custom endpoint
106
- timeout: 120, # Request timeout in seconds
107
- max_retries: 3, # Retry count
108
- structured_outputs: true # Enable JSON schema (default: true)
109
- )
34
+ ```bash
35
+ bundle install
36
+ export OPENAI_API_KEY="your-key"
37
+ bundle exec ruby ruby_llm_smoke.rb
110
38
  ```
111
39
 
112
- For providers with non-standard auth (Bedrock, VertexAI), configure RubyLLM globally - see examples above.
40
+ The command prints the provider response. DSPy reuses RubyLLM's global configuration only when the `DSPy::LM` call does not supply `api_key`, `base_url`, `timeout`, or `max_retries` overrides. Supplying any of those options creates a scoped adapter configuration.
113
41
 
114
- ### With DSPy Signatures
42
+ For a model absent from RubyLLM's registry, specify the provider explicitly:
115
43
 
116
44
  ```ruby
117
- class Summarize < DSPy::Signature
118
- description "Summarize the given text"
119
-
120
- input do
121
- const :text, String
122
- end
123
-
124
- output do
125
- const :summary, String
126
- end
127
- end
128
-
129
- DSPy.configure do |config|
130
- config.lm = DSPy::LM.new("ruby_llm/claude-sonnet-4")
131
- end
132
-
133
- summarizer = DSPy::Predict.new(Summarize)
134
- result = summarizer.call(text: "Long article text here...")
135
- puts result.summary
45
+ lm = DSPy::LM.new("ruby_llm/my-model", provider: "ollama")
136
46
  ```
137
47
 
138
- ### Streaming
139
-
140
- ```ruby
141
- lm = DSPy::LM.new("ruby_llm/gpt-4o", api_key: ENV['OPENAI_API_KEY'])
142
-
143
- response = lm.chat(messages: [{ role: 'user', content: 'Tell me a story' }]) do |chunk|
144
- print chunk # Print each chunk as it arrives
145
- end
146
- ```
147
-
148
- ## Dependencies
149
-
150
- This gem depends on:
151
- - `dspy` (>= 0.32)
152
- - `ruby_llm` (~> 1.3)
153
-
154
- RubyLLM itself has minimal dependencies (Faraday, Zeitwerk, Marcel).
155
-
156
- ## Why Use This Adapter?
157
-
158
- 1. **Unified interface** - One API for all providers
159
- 2. **Lightweight** - RubyLLM has only 3 dependencies
160
- 3. **Provider coverage** - Access Bedrock, VertexAI, DeepSeek without separate adapters
161
- 4. **Built-in retries** - Automatic retry with exponential backoff
162
- 5. **Model registry** - 500+ models with capability detection and auto provider resolution
163
-
164
- ## Error Handling
165
-
166
- The adapter maps RubyLLM errors to DSPy error types:
48
+ ## Capability and Failure Boundaries
167
49
 
168
- | RubyLLM Error | DSPy Error |
169
- |---------------|------------|
170
- | `UnauthorizedError` | `MissingAPIKeyError` |
171
- | `RateLimitError` | `AdapterError` (with retry hint) |
172
- | `ModelNotFoundError` | `AdapterError` |
173
- | `BadRequestError` | `AdapterError` |
174
- | `ConfigurationError` | `ConfigurationError` |
50
+ - A missing RubyLLM credential or unknown model/provider fails before or during the request.
51
+ - Registry data and capability detection vary with the installed RubyLLM version.
52
+ - Authentication, attachments, schemas, streaming, retries, and request options vary by underlying provider and model; document input is currently restricted to Anthropic.
53
+ - Error mapping preserves a DSPy boundary, but provider-specific details still come from RubyLLM and its SDKs. Test the exact model and request your application will use.
@@ -2,6 +2,6 @@
2
2
 
3
3
  module DSPy
4
4
  module RubyLLM
5
- VERSION = '0.1.1'
5
+ VERSION = '0.1.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dspy-ruby_llm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vicente Reig Rincón de Arellano
@@ -86,5 +86,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
86
  requirements: []
87
87
  rubygems_version: 3.6.9
88
88
  specification_version: 4
89
- summary: RubyLLM adapter for DSPy.rb - unified access to 12+ LLM providers.
89
+ summary: RubyLLM provider adapter for DSPy.rb.
90
90
  test_files: []