dspy 0.27.6 → 0.28.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.
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base_strategy"
4
-
5
- module DSPy
6
- class LM
7
- module Strategies
8
- # Strategy for using OpenAI's native structured output feature
9
- class OpenAIStructuredOutputStrategy < BaseStrategy
10
- extend T::Sig
11
-
12
- sig { override.returns(T::Boolean) }
13
- def available?
14
- # Check if adapter is OpenAI or Ollama and supports structured outputs
15
- return false unless adapter.is_a?(DSPy::LM::OpenAIAdapter) || adapter.is_a?(DSPy::LM::OllamaAdapter)
16
- return false unless adapter.instance_variable_get(:@structured_outputs_enabled)
17
-
18
- # For Ollama, we assume it supports basic structured outputs
19
- if adapter.is_a?(DSPy::LM::OllamaAdapter)
20
- return true
21
- end
22
-
23
- DSPy::LM::Adapters::OpenAI::SchemaConverter.supports_structured_outputs?(adapter.model)
24
- end
25
-
26
- sig { override.returns(Integer) }
27
- def priority
28
- 100 # Highest priority - native structured outputs are most reliable
29
- end
30
-
31
- sig { override.returns(String) }
32
- def name
33
- "openai_structured_output"
34
- end
35
-
36
- sig { override.params(messages: T::Array[T::Hash[Symbol, String]], request_params: T::Hash[Symbol, T.untyped]).void }
37
- def prepare_request(messages, request_params)
38
- # Add structured output format to request
39
- response_format = DSPy::LM::Adapters::OpenAI::SchemaConverter.to_openai_format(signature_class)
40
- request_params[:response_format] = response_format
41
- end
42
-
43
- sig { override.params(response: DSPy::LM::Response).returns(T.nilable(String)) }
44
- def extract_json(response)
45
- # With structured outputs, the response should already be valid JSON
46
- # Just return the content as-is
47
- response.content
48
- end
49
-
50
- sig { override.params(error: StandardError).returns(T::Boolean) }
51
- def handle_error(error)
52
- # Handle OpenAI-specific structured output errors
53
- if error.message.include?("response_format") || error.message.include?("Invalid schema")
54
- # Log the error and return true to indicate we handled it
55
- # This allows fallback to another strategy
56
- DSPy.logger.warn("OpenAI structured output failed: #{error.message}")
57
- true
58
- else
59
- false
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
@@ -1,144 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sorbet-runtime"
4
- require_relative "strategies/base_strategy"
5
- require_relative "strategies/openai_structured_output_strategy"
6
- require_relative "strategies/anthropic_tool_use_strategy"
7
- require_relative "strategies/anthropic_extraction_strategy"
8
- require_relative "strategies/gemini_structured_output_strategy"
9
- require_relative "strategies/enhanced_prompting_strategy"
10
-
11
- module DSPy
12
- class LM
13
- # Selects the best JSON extraction strategy based on the adapter and capabilities
14
- class StrategySelector
15
- extend T::Sig
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
-
28
- # Available strategies in order of registration
29
- STRATEGIES = [
30
- Strategies::OpenAIStructuredOutputStrategy,
31
- Strategies::AnthropicToolUseStrategy,
32
- Strategies::AnthropicExtractionStrategy,
33
- Strategies::GeminiStructuredOutputStrategy,
34
- Strategies::EnhancedPromptingStrategy
35
- ].freeze
36
-
37
- sig { params(adapter: DSPy::LM::Adapter, signature_class: T.class_of(DSPy::Signature)).void }
38
- def initialize(adapter, signature_class)
39
- @adapter = adapter
40
- @signature_class = signature_class
41
- @strategies = build_strategies
42
- end
43
-
44
- # Select the best available strategy
45
- sig { returns(Strategies::BaseStrategy) }
46
- def select
47
- # Allow manual override via configuration
48
- if DSPy.config.structured_outputs.strategy
49
- strategy = select_strategy_from_preference(DSPy.config.structured_outputs.strategy)
50
- return strategy if strategy&.available?
51
-
52
- # If strict strategy not available, fall back to compatible for Strict preference
53
- if is_strict_preference?(DSPy.config.structured_outputs.strategy)
54
- compatible_strategy = find_strategy_by_name(StrategyName::EnhancedPrompting)
55
- return compatible_strategy if compatible_strategy&.available?
56
- end
57
-
58
- DSPy.logger.warn("No available strategy found for preference '#{DSPy.config.structured_outputs.strategy}'")
59
- end
60
-
61
- # Select the highest priority available strategy
62
- available_strategies = @strategies.select(&:available?)
63
-
64
- if available_strategies.empty?
65
- raise "No JSON extraction strategies available for #{@adapter.class}"
66
- end
67
-
68
- selected = available_strategies.max_by(&:priority)
69
-
70
- DSPy.logger.debug("Selected JSON extraction strategy: #{selected.name}")
71
- selected
72
- end
73
-
74
- # Get all available strategies
75
- sig { returns(T::Array[Strategies::BaseStrategy]) }
76
- def available_strategies
77
- @strategies.select(&:available?)
78
- end
79
-
80
- # Check if a specific strategy is available
81
- sig { params(strategy_name: StrategyName).returns(T::Boolean) }
82
- def strategy_available?(strategy_name)
83
- strategy = find_strategy_by_name(strategy_name)
84
- strategy&.available? || false
85
- end
86
-
87
- private
88
-
89
- # Select internal strategy based on user preference
90
- sig { params(preference: DSPy::Strategy).returns(T.nilable(Strategies::BaseStrategy)) }
91
- def select_strategy_from_preference(preference)
92
- case preference
93
- when DSPy::Strategy::Strict
94
- # Try provider-optimized strategies first
95
- select_provider_optimized_strategy
96
- when DSPy::Strategy::Compatible
97
- # Use enhanced prompting
98
- find_strategy_by_name(StrategyName::EnhancedPrompting)
99
- else
100
- nil
101
- end
102
- end
103
-
104
- # Check if preference is for strict (provider-optimized) strategies
105
- sig { params(preference: DSPy::Strategy).returns(T::Boolean) }
106
- def is_strict_preference?(preference)
107
- preference == DSPy::Strategy::Strict
108
- end
109
-
110
- # Select the best provider-optimized strategy for the current adapter
111
- sig { returns(T.nilable(Strategies::BaseStrategy)) }
112
- def select_provider_optimized_strategy
113
- # Try OpenAI structured output first
114
- openai_strategy = find_strategy_by_name(StrategyName::OpenAIStructuredOutput)
115
- return openai_strategy if openai_strategy&.available?
116
-
117
- # Try Gemini structured output
118
- gemini_strategy = find_strategy_by_name(StrategyName::GeminiStructuredOutput)
119
- return gemini_strategy if gemini_strategy&.available?
120
-
121
- # Try Anthropic tool use first
122
- anthropic_tool_strategy = find_strategy_by_name(StrategyName::AnthropicToolUse)
123
- return anthropic_tool_strategy if anthropic_tool_strategy&.available?
124
-
125
- # Fall back to Anthropic extraction
126
- anthropic_strategy = find_strategy_by_name(StrategyName::AnthropicExtraction)
127
- return anthropic_strategy if anthropic_strategy&.available?
128
-
129
- # No provider-specific strategy available
130
- nil
131
- end
132
-
133
- sig { returns(T::Array[Strategies::BaseStrategy]) }
134
- def build_strategies
135
- STRATEGIES.map { |klass| klass.new(@adapter, @signature_class) }
136
- end
137
-
138
- sig { params(name: StrategyName).returns(T.nilable(Strategies::BaseStrategy)) }
139
- def find_strategy_by_name(name)
140
- @strategies.find { |s| s.name == name.serialize }
141
- end
142
- end
143
- end
144
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sorbet-runtime"
4
-
5
- module DSPy
6
- class LM
7
- # Enum for structured output strategies
8
- class StructuredOutputStrategy < T::Enum
9
- enums do
10
- OpenAIStructuredOutput = new("openai_structured_output")
11
- AnthropicToolUse = new("anthropic_tool_use")
12
- AnthropicExtraction = new("anthropic_extraction")
13
- EnhancedPrompting = new("enhanced_prompting")
14
- end
15
- end
16
- end
17
- end
data/lib/dspy/strategy.rb DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "sorbet-runtime"
4
-
5
- module DSPy
6
- # User-facing enum for structured output strategy preferences
7
- class Strategy < T::Enum
8
- enums do
9
- # Use provider-optimized strategies when available (OpenAI structured outputs, Anthropic extraction)
10
- # Falls back to Compatible if provider-specific strategy isn't available
11
- Strict = new("strict")
12
-
13
- # Use enhanced prompting that works with any provider
14
- # More compatible but potentially less reliable than provider-specific strategies
15
- Compatible = new("compatible")
16
- end
17
- end
18
- end