dspy 0.26.1 → 0.27.1
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 +4 -4
- data/README.md +21 -1
- data/lib/dspy/context.rb +24 -18
- data/lib/dspy/lm/adapter_factory.rb +2 -2
- data/lib/dspy/lm/adapters/gemini/schema_converter.rb +170 -0
- data/lib/dspy/lm/adapters/gemini_adapter.rb +4 -2
- data/lib/dspy/lm/strategies/gemini_structured_output_strategy.rb +67 -0
- data/lib/dspy/lm/strategy_selector.rb +25 -8
- data/lib/dspy/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7962564b749798c462b3202e16153aa9e508e8d7fb10d6618c76e740ecb171d1
|
4
|
+
data.tar.gz: d81fad4637c82967745d644c291f7436e26c0c1bf06785ff56028c71d4d5f26d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38ed78b62481f7267a9d1fa7157af436bdfd82d6a8281b00b9d8915fa74e7b0b5d896f8dfa9d6f4a91aa829973f7db94445d76b3cdb5e7c66800cd23f28f1fe9
|
7
|
+
data.tar.gz: 61f0ef47790c3c9aa6c6ecd1f7ee11268ac81b27ca75212af2c69b4f4b077fa07c9acb237c7d7b3f4174c85d7de415684ac9fef5c88e01d538671b93910fa889
|
data/README.md
CHANGED
@@ -59,6 +59,25 @@ puts result.sentiment # => #<Sentiment::Positive>
|
|
59
59
|
puts result.confidence # => 0.85
|
60
60
|
```
|
61
61
|
|
62
|
+
### Alternative Providers
|
63
|
+
|
64
|
+
DSPy.rb supports multiple providers with native structured outputs:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
# Google Gemini with native structured outputs
|
68
|
+
DSPy.configure do |c|
|
69
|
+
c.lm = DSPy::LM.new('gemini/gemini-1.5-flash',
|
70
|
+
api_key: ENV['GEMINI_API_KEY'],
|
71
|
+
structured_outputs: true) # Supports gemini-1.5-pro, gemini-1.5-flash, gemini-2.0-flash-exp
|
72
|
+
end
|
73
|
+
|
74
|
+
# Anthropic Claude with tool-based extraction
|
75
|
+
DSPy.configure do |c|
|
76
|
+
c.lm = DSPy::LM.new('anthropic/claude-3-sonnet-20241022',
|
77
|
+
api_key: ENV['ANTHROPIC_API_KEY']) # Automatic strategy selection
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
62
81
|
## What You Get
|
63
82
|
|
64
83
|
**Core Building Blocks:**
|
@@ -77,7 +96,7 @@ puts result.confidence # => 0.85
|
|
77
96
|
- **GEPA Optimization** - Genetic-Pareto optimization for multi-objective prompt improvement
|
78
97
|
|
79
98
|
**Production Features:**
|
80
|
-
- **Reliable JSON Extraction** - Native OpenAI
|
99
|
+
- **Reliable JSON Extraction** - Native structured outputs for OpenAI and Gemini, Anthropic tool-based extraction, and automatic strategy selection with fallback
|
81
100
|
- **Type-Safe Configuration** - Strategy enums with automatic provider optimization (Strict/Compatible modes)
|
82
101
|
- **Smart Retry Logic** - Progressive fallback with exponential backoff for handling transient failures
|
83
102
|
- **Zero-Config Langfuse Integration** - Set env vars and get automatic OpenTelemetry traces in Langfuse
|
@@ -89,6 +108,7 @@ puts result.confidence # => 0.85
|
|
89
108
|
- LLM provider support using official Ruby clients:
|
90
109
|
- [OpenAI Ruby](https://github.com/openai/openai-ruby) with vision model support
|
91
110
|
- [Anthropic Ruby SDK](https://github.com/anthropics/anthropic-sdk-ruby) with multimodal capabilities
|
111
|
+
- [Google Gemini API](https://ai.google.dev/) with native structured outputs
|
92
112
|
- [Ollama](https://ollama.com/) via OpenAI compatibility layer for local models
|
93
113
|
- **Multimodal Support** - Complete image analysis with DSPy::Image, type-safe bounding boxes, vision-capable models
|
94
114
|
- Runtime type checking with [Sorbet](https://sorbet.org/) including T::Enum and union types
|
data/lib/dspy/context.rb
CHANGED
@@ -6,29 +6,31 @@ module DSPy
|
|
6
6
|
class Context
|
7
7
|
class << self
|
8
8
|
def current
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
Fiber[:dspy_context]
|
9
|
+
# Use a combination of Thread and Fiber storage for proper context management
|
10
|
+
# Thread storage ensures thread isolation
|
11
|
+
# Fiber storage ensures OpenTelemetry context propagation
|
12
|
+
|
13
|
+
# Create a unique key for this thread to ensure isolation
|
14
|
+
thread_key = :"dspy_context_#{Thread.current.object_id}"
|
15
|
+
|
16
|
+
# Check if this thread has its own context
|
17
|
+
if Thread.current[thread_key]
|
18
|
+
# Thread has context, ensure fiber has it too for OpenTelemetry
|
19
|
+
Fiber[:dspy_context] ||= Thread.current[thread_key]
|
20
20
|
else
|
21
|
-
#
|
22
|
-
|
21
|
+
# No context for this thread - create new one
|
22
|
+
context = {
|
23
23
|
trace_id: SecureRandom.uuid,
|
24
24
|
span_stack: []
|
25
25
|
}
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
Thread.current[:dspy_context]
|
26
|
+
# Set in both Thread and Fiber storage
|
27
|
+
Thread.current[thread_key] = context
|
28
|
+
Thread.current[:dspy_context] = context # Keep for backward compatibility
|
29
|
+
Fiber[:dspy_context] = context
|
31
30
|
end
|
31
|
+
|
32
|
+
# Return the context (from Fiber storage for OpenTelemetry compatibility)
|
33
|
+
Fiber[:dspy_context]
|
32
34
|
end
|
33
35
|
|
34
36
|
def with_span(operation:, **attributes)
|
@@ -65,6 +67,7 @@ module DSPy
|
|
65
67
|
# Record start time for explicit duration tracking
|
66
68
|
otel_start_time = Time.now
|
67
69
|
|
70
|
+
# Always use in_span which properly manages context internally
|
68
71
|
DSPy::Observability.tracer.in_span(
|
69
72
|
operation,
|
70
73
|
attributes: span_attributes,
|
@@ -102,6 +105,9 @@ module DSPy
|
|
102
105
|
end
|
103
106
|
|
104
107
|
def clear!
|
108
|
+
# Clear both the thread-specific key and the legacy key
|
109
|
+
thread_key = :"dspy_context_#{Thread.current.object_id}"
|
110
|
+
Thread.current[thread_key] = nil
|
105
111
|
Thread.current[:dspy_context] = nil
|
106
112
|
Fiber[:dspy_context] = nil
|
107
113
|
end
|
@@ -24,8 +24,8 @@ module DSPy
|
|
24
24
|
|
25
25
|
# Pass provider-specific options
|
26
26
|
adapter_options = { model: model, api_key: api_key }
|
27
|
-
#
|
28
|
-
adapter_options.merge!(options) if %w[openai ollama].include?(provider)
|
27
|
+
# OpenAI, Ollama, and Gemini accept additional options
|
28
|
+
adapter_options.merge!(options) if %w[openai ollama gemini].include?(provider)
|
29
29
|
|
30
30
|
adapter_class.new(**adapter_options)
|
31
31
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sorbet-runtime"
|
4
|
+
require_relative "../../cache_manager"
|
5
|
+
|
6
|
+
module DSPy
|
7
|
+
class LM
|
8
|
+
module Adapters
|
9
|
+
module Gemini
|
10
|
+
# Converts DSPy signatures to Gemini structured output format
|
11
|
+
class SchemaConverter
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
# Models that support structured outputs
|
15
|
+
STRUCTURED_OUTPUT_MODELS = T.let([
|
16
|
+
"gemini-1.5-pro",
|
17
|
+
"gemini-1.5-flash",
|
18
|
+
"gemini-2.0-flash-exp"
|
19
|
+
].freeze, T::Array[String])
|
20
|
+
|
21
|
+
sig { params(signature_class: T.class_of(DSPy::Signature)).returns(T::Hash[Symbol, T.untyped]) }
|
22
|
+
def self.to_gemini_format(signature_class)
|
23
|
+
# Check cache first
|
24
|
+
cache_manager = DSPy::LM.cache_manager
|
25
|
+
cached_schema = cache_manager.get_schema(signature_class, "gemini", {})
|
26
|
+
|
27
|
+
if cached_schema
|
28
|
+
DSPy.logger.debug("Using cached schema for #{signature_class.name}")
|
29
|
+
return cached_schema
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get the output JSON schema from the signature class
|
33
|
+
output_schema = signature_class.output_json_schema
|
34
|
+
|
35
|
+
# Convert to Gemini format (OpenAPI 3.0 Schema subset - not related to OpenAI)
|
36
|
+
gemini_schema = convert_dspy_schema_to_gemini(output_schema)
|
37
|
+
|
38
|
+
# Cache the result
|
39
|
+
cache_manager.cache_schema(signature_class, "gemini", gemini_schema, {})
|
40
|
+
|
41
|
+
gemini_schema
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { params(model: String).returns(T::Boolean) }
|
45
|
+
def self.supports_structured_outputs?(model)
|
46
|
+
# Check cache first
|
47
|
+
cache_manager = DSPy::LM.cache_manager
|
48
|
+
cached_result = cache_manager.get_capability(model, "structured_outputs")
|
49
|
+
|
50
|
+
if !cached_result.nil?
|
51
|
+
DSPy.logger.debug("Using cached capability check for #{model}")
|
52
|
+
return cached_result
|
53
|
+
end
|
54
|
+
|
55
|
+
# Extract base model name without provider prefix
|
56
|
+
base_model = model.sub(/^gemini\//, "")
|
57
|
+
|
58
|
+
# Check if it's a supported model or a newer version
|
59
|
+
result = STRUCTURED_OUTPUT_MODELS.any? { |supported| base_model.start_with?(supported) }
|
60
|
+
|
61
|
+
# Cache the result
|
62
|
+
cache_manager.cache_capability(model, "structured_outputs", result)
|
63
|
+
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
sig { params(schema: T::Hash[Symbol, T.untyped]).returns(T::Array[String]) }
|
68
|
+
def self.validate_compatibility(schema)
|
69
|
+
issues = []
|
70
|
+
|
71
|
+
# Check for deeply nested objects (Gemini has depth limits)
|
72
|
+
depth = calculate_depth(schema)
|
73
|
+
if depth > 5
|
74
|
+
issues << "Schema depth (#{depth}) exceeds recommended limit of 5 levels"
|
75
|
+
end
|
76
|
+
|
77
|
+
issues
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
sig { params(dspy_schema: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
83
|
+
def self.convert_dspy_schema_to_gemini(dspy_schema)
|
84
|
+
result = {
|
85
|
+
type: "object",
|
86
|
+
properties: {},
|
87
|
+
required: []
|
88
|
+
}
|
89
|
+
|
90
|
+
# Convert properties
|
91
|
+
properties = dspy_schema[:properties] || {}
|
92
|
+
properties.each do |prop_name, prop_schema|
|
93
|
+
result[:properties][prop_name] = convert_property_to_gemini(prop_schema)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Set required fields
|
97
|
+
result[:required] = (dspy_schema[:required] || []).map(&:to_s)
|
98
|
+
|
99
|
+
result
|
100
|
+
end
|
101
|
+
|
102
|
+
sig { params(property_schema: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
103
|
+
def self.convert_property_to_gemini(property_schema)
|
104
|
+
case property_schema[:type]
|
105
|
+
when "string"
|
106
|
+
result = { type: "string" }
|
107
|
+
result[:enum] = property_schema[:enum] if property_schema[:enum]
|
108
|
+
result
|
109
|
+
when "integer"
|
110
|
+
{ type: "integer" }
|
111
|
+
when "number"
|
112
|
+
{ type: "number" }
|
113
|
+
when "boolean"
|
114
|
+
{ type: "boolean" }
|
115
|
+
when "array"
|
116
|
+
{
|
117
|
+
type: "array",
|
118
|
+
items: convert_property_to_gemini(property_schema[:items] || { type: "string" })
|
119
|
+
}
|
120
|
+
when "object"
|
121
|
+
result = { type: "object" }
|
122
|
+
|
123
|
+
if property_schema[:properties]
|
124
|
+
result[:properties] = {}
|
125
|
+
property_schema[:properties].each do |nested_prop, nested_schema|
|
126
|
+
result[:properties][nested_prop] = convert_property_to_gemini(nested_schema)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Set required fields for nested objects
|
130
|
+
if property_schema[:required]
|
131
|
+
result[:required] = property_schema[:required].map(&:to_s)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
result
|
136
|
+
else
|
137
|
+
# Default to string for unknown types
|
138
|
+
{ type: "string" }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
sig { params(schema: T::Hash[Symbol, T.untyped], current_depth: Integer).returns(Integer) }
|
143
|
+
def self.calculate_depth(schema, current_depth = 0)
|
144
|
+
return current_depth unless schema.is_a?(Hash)
|
145
|
+
|
146
|
+
max_depth = current_depth
|
147
|
+
|
148
|
+
# Check properties
|
149
|
+
if schema[:properties].is_a?(Hash)
|
150
|
+
schema[:properties].each_value do |prop|
|
151
|
+
if prop.is_a?(Hash)
|
152
|
+
prop_depth = calculate_depth(prop, current_depth + 1)
|
153
|
+
max_depth = [max_depth, prop_depth].max
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Check array items
|
159
|
+
if schema[:items].is_a?(Hash)
|
160
|
+
items_depth = calculate_depth(schema[:items], current_depth + 1)
|
161
|
+
max_depth = [max_depth, items_depth].max
|
162
|
+
end
|
163
|
+
|
164
|
+
max_depth
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -7,10 +7,12 @@ require_relative '../vision_models'
|
|
7
7
|
module DSPy
|
8
8
|
class LM
|
9
9
|
class GeminiAdapter < Adapter
|
10
|
-
def initialize(model:, api_key:)
|
11
|
-
super
|
10
|
+
def initialize(model:, api_key:, structured_outputs: false)
|
11
|
+
super(model: model, api_key: api_key)
|
12
12
|
validate_api_key!(api_key, 'gemini')
|
13
13
|
|
14
|
+
@structured_outputs_enabled = structured_outputs
|
15
|
+
|
14
16
|
@client = Gemini.new(
|
15
17
|
credentials: {
|
16
18
|
service: 'generative-language-api',
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_strategy"
|
4
|
+
require_relative "../adapters/gemini/schema_converter"
|
5
|
+
|
6
|
+
module DSPy
|
7
|
+
class LM
|
8
|
+
module Strategies
|
9
|
+
# Strategy for using Gemini's native structured output feature
|
10
|
+
class GeminiStructuredOutputStrategy < BaseStrategy
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { override.returns(T::Boolean) }
|
14
|
+
def available?
|
15
|
+
# Check if adapter is Gemini and supports structured outputs
|
16
|
+
return false unless adapter.is_a?(DSPy::LM::GeminiAdapter)
|
17
|
+
return false unless adapter.instance_variable_get(:@structured_outputs_enabled)
|
18
|
+
|
19
|
+
DSPy::LM::Adapters::Gemini::SchemaConverter.supports_structured_outputs?(adapter.model)
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { override.returns(Integer) }
|
23
|
+
def priority
|
24
|
+
100 # Highest priority - native structured outputs are most reliable
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { override.returns(String) }
|
28
|
+
def name
|
29
|
+
"gemini_structured_output"
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { override.params(messages: T::Array[T::Hash[Symbol, String]], request_params: T::Hash[Symbol, T.untyped]).void }
|
33
|
+
def prepare_request(messages, request_params)
|
34
|
+
# Convert signature to Gemini schema format
|
35
|
+
schema = DSPy::LM::Adapters::Gemini::SchemaConverter.to_gemini_format(signature_class)
|
36
|
+
|
37
|
+
# Add generation_config for structured output
|
38
|
+
request_params[:generation_config] = {
|
39
|
+
response_mime_type: "application/json",
|
40
|
+
response_schema: schema
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
sig { override.params(response: DSPy::LM::Response).returns(T.nilable(String)) }
|
45
|
+
def extract_json(response)
|
46
|
+
# With Gemini structured outputs, the response should already be valid JSON
|
47
|
+
# Just return the content as-is
|
48
|
+
response.content
|
49
|
+
end
|
50
|
+
|
51
|
+
sig { override.params(error: StandardError).returns(T::Boolean) }
|
52
|
+
def handle_error(error)
|
53
|
+
# Handle Gemini-specific structured output errors
|
54
|
+
error_msg = error.message.to_s.downcase
|
55
|
+
if error_msg.include?("schema") || error_msg.include?("generation_config") || error_msg.include?("response_schema")
|
56
|
+
# Log the error and return true to indicate we handled it
|
57
|
+
# This allows fallback to another strategy
|
58
|
+
DSPy.logger.warn("Gemini structured output failed: #{error.message}")
|
59
|
+
true
|
60
|
+
else
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -5,6 +5,7 @@ require_relative "strategies/base_strategy"
|
|
5
5
|
require_relative "strategies/openai_structured_output_strategy"
|
6
6
|
require_relative "strategies/anthropic_tool_use_strategy"
|
7
7
|
require_relative "strategies/anthropic_extraction_strategy"
|
8
|
+
require_relative "strategies/gemini_structured_output_strategy"
|
8
9
|
require_relative "strategies/enhanced_prompting_strategy"
|
9
10
|
|
10
11
|
module DSPy
|
@@ -13,11 +14,23 @@ module DSPy
|
|
13
14
|
class StrategySelector
|
14
15
|
extend T::Sig
|
15
16
|
|
17
|
+
# Strategy names enum for type safety
|
18
|
+
class StrategyName < T::Enum
|
19
|
+
enums do
|
20
|
+
OpenAIStructuredOutput = new('openai_structured_output')
|
21
|
+
AnthropicToolUse = new('anthropic_tool_use')
|
22
|
+
AnthropicExtraction = new('anthropic_extraction')
|
23
|
+
GeminiStructuredOutput = new('gemini_structured_output')
|
24
|
+
EnhancedPrompting = new('enhanced_prompting')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
16
28
|
# Available strategies in order of registration
|
17
29
|
STRATEGIES = [
|
18
30
|
Strategies::OpenAIStructuredOutputStrategy,
|
19
31
|
Strategies::AnthropicToolUseStrategy,
|
20
32
|
Strategies::AnthropicExtractionStrategy,
|
33
|
+
Strategies::GeminiStructuredOutputStrategy,
|
21
34
|
Strategies::EnhancedPromptingStrategy
|
22
35
|
].freeze
|
23
36
|
|
@@ -38,7 +51,7 @@ module DSPy
|
|
38
51
|
|
39
52
|
# If strict strategy not available, fall back to compatible for Strict preference
|
40
53
|
if is_strict_preference?(DSPy.config.structured_outputs.strategy)
|
41
|
-
compatible_strategy = find_strategy_by_name(
|
54
|
+
compatible_strategy = find_strategy_by_name(StrategyName::EnhancedPrompting)
|
42
55
|
return compatible_strategy if compatible_strategy&.available?
|
43
56
|
end
|
44
57
|
|
@@ -65,7 +78,7 @@ module DSPy
|
|
65
78
|
end
|
66
79
|
|
67
80
|
# Check if a specific strategy is available
|
68
|
-
sig { params(strategy_name:
|
81
|
+
sig { params(strategy_name: StrategyName).returns(T::Boolean) }
|
69
82
|
def strategy_available?(strategy_name)
|
70
83
|
strategy = find_strategy_by_name(strategy_name)
|
71
84
|
strategy&.available? || false
|
@@ -82,7 +95,7 @@ module DSPy
|
|
82
95
|
select_provider_optimized_strategy
|
83
96
|
when DSPy::Strategy::Compatible
|
84
97
|
# Use enhanced prompting
|
85
|
-
find_strategy_by_name(
|
98
|
+
find_strategy_by_name(StrategyName::EnhancedPrompting)
|
86
99
|
else
|
87
100
|
nil
|
88
101
|
end
|
@@ -98,15 +111,19 @@ module DSPy
|
|
98
111
|
sig { returns(T.nilable(Strategies::BaseStrategy)) }
|
99
112
|
def select_provider_optimized_strategy
|
100
113
|
# Try OpenAI structured output first
|
101
|
-
openai_strategy = find_strategy_by_name(
|
114
|
+
openai_strategy = find_strategy_by_name(StrategyName::OpenAIStructuredOutput)
|
102
115
|
return openai_strategy if openai_strategy&.available?
|
103
116
|
|
117
|
+
# Try Gemini structured output
|
118
|
+
gemini_strategy = find_strategy_by_name(StrategyName::GeminiStructuredOutput)
|
119
|
+
return gemini_strategy if gemini_strategy&.available?
|
120
|
+
|
104
121
|
# Try Anthropic tool use first
|
105
|
-
anthropic_tool_strategy = find_strategy_by_name(
|
122
|
+
anthropic_tool_strategy = find_strategy_by_name(StrategyName::AnthropicToolUse)
|
106
123
|
return anthropic_tool_strategy if anthropic_tool_strategy&.available?
|
107
124
|
|
108
125
|
# Fall back to Anthropic extraction
|
109
|
-
anthropic_strategy = find_strategy_by_name(
|
126
|
+
anthropic_strategy = find_strategy_by_name(StrategyName::AnthropicExtraction)
|
110
127
|
return anthropic_strategy if anthropic_strategy&.available?
|
111
128
|
|
112
129
|
# No provider-specific strategy available
|
@@ -118,9 +135,9 @@ module DSPy
|
|
118
135
|
STRATEGIES.map { |klass| klass.new(@adapter, @signature_class) }
|
119
136
|
end
|
120
137
|
|
121
|
-
sig { params(name:
|
138
|
+
sig { params(name: StrategyName).returns(T.nilable(Strategies::BaseStrategy)) }
|
122
139
|
def find_strategy_by_name(name)
|
123
|
-
@strategies.find { |s| s.name == name }
|
140
|
+
@strategies.find { |s| s.name == name.serialize }
|
124
141
|
end
|
125
142
|
end
|
126
143
|
end
|
data/lib/dspy/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dspy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.27.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vicente Reig Rincón de Arellano
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-09-
|
10
|
+
date: 2025-09-14 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: dry-configurable
|
@@ -207,6 +207,7 @@ files:
|
|
207
207
|
- lib/dspy/lm/adapter.rb
|
208
208
|
- lib/dspy/lm/adapter_factory.rb
|
209
209
|
- lib/dspy/lm/adapters/anthropic_adapter.rb
|
210
|
+
- lib/dspy/lm/adapters/gemini/schema_converter.rb
|
210
211
|
- lib/dspy/lm/adapters/gemini_adapter.rb
|
211
212
|
- lib/dspy/lm/adapters/ollama_adapter.rb
|
212
213
|
- lib/dspy/lm/adapters/openai/schema_converter.rb
|
@@ -221,6 +222,7 @@ files:
|
|
221
222
|
- lib/dspy/lm/strategies/anthropic_tool_use_strategy.rb
|
222
223
|
- lib/dspy/lm/strategies/base_strategy.rb
|
223
224
|
- lib/dspy/lm/strategies/enhanced_prompting_strategy.rb
|
225
|
+
- lib/dspy/lm/strategies/gemini_structured_output_strategy.rb
|
224
226
|
- lib/dspy/lm/strategies/openai_structured_output_strategy.rb
|
225
227
|
- lib/dspy/lm/strategy_selector.rb
|
226
228
|
- lib/dspy/lm/structured_output_strategy.rb
|