Class: DSPy::ChainOfThought

Inherits:
Predict show all
Extended by:
T::Sig
Includes:
Mixins::StructBuilder
Defined in:
lib/dspy/chain_of_thought.rb

Overview

Enhances prediction by encouraging step-by-step reasoning before providing a final answer using Sorbet signatures.

Constant Summary collapse

FieldDescriptor =
DSPy::Signature::FieldDescriptor

Instance Attribute Summary collapse

Attributes inherited from Predict

#prompt, #signature_class

Instance Method Summary collapse

Methods inherited from Predict

#add_examples, from_h, #system_signature, #user_signature

Methods inherited from Module

#call, #call_untyped, #forward, #lm

Constructor Details

#initialize(signature_class) ⇒ ChainOfThought

Returns a new instance of ChainOfThought.



20
21
22
23
24
25
26
27
# File 'lib/dspy/chain_of_thought.rb', line 20

def initialize(signature_class)
  @original_signature = signature_class
  enhanced_signature = build_enhanced_signature(signature_class)
  
  # Call parent constructor with enhanced signature
  super(enhanced_signature)
  @signature_class = enhanced_signature
end

Instance Attribute Details

#original_signatureObject (readonly)

Returns the value of attribute original_signature.



84
85
86
# File 'lib/dspy/chain_of_thought.rb', line 84

def original_signature
  @original_signature
end

Instance Method Details

#forward_untyped(**input_values) ⇒ Object



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/dspy/chain_of_thought.rb', line 89

def forward_untyped(**input_values)
  # Create a Predict instance and call its forward method (which will create Predict span via Module#forward)
  # We can't call super.forward because that would go to Module#forward_untyped, not Module#forward
  
  # Create a temporary Predict instance with our enhanced signature to get the prediction
  predict_instance = DSPy::Predict.new(@signature_class)
  predict_instance.config.lm = self.lm  # Use the same LM configuration
  
  # Call predict's forward method, which will create the Predict span
  prediction_result = predict_instance.forward(**input_values)
  
  # Add ChainOfThought-specific analysis and events
  if DSPy::Observability.enabled? && prediction_result
    # Add reasoning metrics via events
    if prediction_result.respond_to?(:reasoning) && prediction_result.reasoning
      DSPy.event('chain_of_thought.reasoning_metrics', {
        'cot.reasoning_length' => prediction_result.reasoning.length,
        'cot.has_reasoning' => true,
        'cot.reasoning_steps' => count_reasoning_steps(prediction_result.reasoning),
        'dspy.module_type' => 'chain_of_thought',
        'dspy.signature' => @original_signature.name
      })
    end
  end
  
  # Analyze reasoning (emits events for backwards compatibility)
  analyze_reasoning(prediction_result)
  
  prediction_result
end

#with_examples(examples) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/dspy/chain_of_thought.rb', line 63

def with_examples(examples)
  # Convert examples to include reasoning if they don't have it
  enhanced_examples = examples.map do |example|
    if example.reasoning.nil? || example.reasoning.empty?
      # Try to extract reasoning from the output if it contains a reasoning field
      reasoning = example.output[:reasoning] || "Step by step reasoning for this example."
      DSPy::FewShotExample.new(
        input: example.input,
        output: example.output,
        reasoning: reasoning
      )
    else
      example
    end
  end
  
  super(enhanced_examples)
end

#with_instruction(instruction) ⇒ Object



57
58
59
60
# File 'lib/dspy/chain_of_thought.rb', line 57

def with_instruction(instruction)
  enhanced_instruction = ensure_chain_of_thought_instruction(instruction)
  super(enhanced_instruction)
end

#with_prompt(new_prompt) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/dspy/chain_of_thought.rb', line 32

def with_prompt(new_prompt)
  # Create a new ChainOfThought with the same original signature
  instance = self.class.new(@original_signature)
  
  # Ensure the instruction includes "Think step by step" if not already present
  enhanced_instruction = if new_prompt.instruction.include?("Think step by step")
                           new_prompt.instruction
                         else
                           "#{new_prompt.instruction} Think step by step."
                         end
  
  # Create enhanced prompt with ChainOfThought-specific schemas
  enhanced_prompt = Prompt.new(
    instruction: enhanced_instruction,
    input_schema: @signature_class.input_json_schema,
    output_schema: @signature_class.output_json_schema,
    few_shot_examples: new_prompt.few_shot_examples,
    signature_class_name: @signature_class.name
  )
  
  instance.instance_variable_set(:@prompt, enhanced_prompt)
  instance
end