dspy 0.29.1 → 0.30.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +45 -0
  3. data/README.md +159 -95
  4. data/lib/dspy/callbacks.rb +93 -19
  5. data/lib/dspy/context.rb +101 -5
  6. data/lib/dspy/errors.rb +19 -1
  7. data/lib/dspy/{datasets.rb → evals/version.rb} +2 -3
  8. data/lib/dspy/{evaluate.rb → evals.rb} +373 -110
  9. data/lib/dspy/mixins/instruction_updatable.rb +22 -0
  10. data/lib/dspy/module.rb +213 -17
  11. data/lib/dspy/observability.rb +40 -182
  12. data/lib/dspy/predict.rb +10 -2
  13. data/lib/dspy/propose/dataset_summary_generator.rb +28 -18
  14. data/lib/dspy/re_act.rb +21 -0
  15. data/lib/dspy/schema/sorbet_json_schema.rb +302 -0
  16. data/lib/dspy/schema/version.rb +7 -0
  17. data/lib/dspy/schema.rb +4 -0
  18. data/lib/dspy/structured_outputs_prompt.rb +48 -0
  19. data/lib/dspy/support/warning_filters.rb +27 -0
  20. data/lib/dspy/teleprompt/gepa.rb +9 -588
  21. data/lib/dspy/teleprompt/instruction_updates.rb +94 -0
  22. data/lib/dspy/teleprompt/teleprompter.rb +6 -6
  23. data/lib/dspy/teleprompt/utils.rb +5 -65
  24. data/lib/dspy/type_system/sorbet_json_schema.rb +2 -299
  25. data/lib/dspy/version.rb +1 -1
  26. data/lib/dspy.rb +39 -7
  27. metadata +18 -61
  28. data/lib/dspy/code_act.rb +0 -477
  29. data/lib/dspy/datasets/ade.rb +0 -90
  30. data/lib/dspy/observability/async_span_processor.rb +0 -250
  31. data/lib/dspy/observability/observation_type.rb +0 -65
  32. data/lib/dspy/optimizers/gaussian_process.rb +0 -141
  33. data/lib/dspy/teleprompt/mipro_v2.rb +0 -1672
  34. data/lib/gepa/api.rb +0 -61
  35. data/lib/gepa/core/engine.rb +0 -226
  36. data/lib/gepa/core/evaluation_batch.rb +0 -26
  37. data/lib/gepa/core/result.rb +0 -92
  38. data/lib/gepa/core/state.rb +0 -231
  39. data/lib/gepa/logging/experiment_tracker.rb +0 -54
  40. data/lib/gepa/logging/logger.rb +0 -57
  41. data/lib/gepa/logging.rb +0 -9
  42. data/lib/gepa/proposer/base.rb +0 -27
  43. data/lib/gepa/proposer/merge_proposer.rb +0 -424
  44. data/lib/gepa/proposer/reflective_mutation/base.rb +0 -48
  45. data/lib/gepa/proposer/reflective_mutation/reflective_mutation.rb +0 -188
  46. data/lib/gepa/strategies/batch_sampler.rb +0 -91
  47. data/lib/gepa/strategies/candidate_selector.rb +0 -97
  48. data/lib/gepa/strategies/component_selector.rb +0 -57
  49. data/lib/gepa/strategies/instruction_proposal.rb +0 -120
  50. data/lib/gepa/telemetry.rb +0 -122
  51. data/lib/gepa/utils/pareto.rb +0 -119
  52. data/lib/gepa.rb +0 -21
data/lib/dspy/context.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'securerandom'
4
+ require 'json'
4
5
 
5
6
  module DSPy
6
7
  class Context
@@ -32,7 +33,8 @@ module DSPy
32
33
  context = {
33
34
  trace_id: SecureRandom.uuid,
34
35
  span_stack: [],
35
- otel_span_stack: []
36
+ otel_span_stack: [],
37
+ module_stack: []
36
38
  }
37
39
 
38
40
  # Set in both Thread and Fiber storage
@@ -47,14 +49,15 @@ module DSPy
47
49
  span_id = SecureRandom.uuid
48
50
  parent_span_id = current[:span_stack].last
49
51
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
50
-
52
+ sanitized_attributes = sanitize_span_attributes(attributes)
53
+
51
54
  # Prepare attributes with context information
52
55
  span_attributes = {
53
56
  trace_id: current[:trace_id],
54
57
  span_id: span_id,
55
58
  parent_span_id: parent_span_id,
56
59
  operation: operation,
57
- **attributes
60
+ **sanitized_attributes
58
61
  }
59
62
 
60
63
  # Log span start with proper hierarchy (internal logging only)
@@ -67,7 +70,7 @@ module DSPy
67
70
  # Use OpenTelemetry's proper context management for nesting
68
71
  if DSPy::Observability.enabled? && DSPy::Observability.tracer
69
72
  # Prepare attributes and add trace name for root spans
70
- span_attributes = attributes.transform_keys(&:to_s).reject { |k, v| v.nil? }
73
+ span_attributes = sanitized_attributes.transform_keys(&:to_s).reject { |k, v| v.nil? }
71
74
 
72
75
  # Set trace name if this is likely a root span (no parent in our stack)
73
76
  if current[:span_stack].length == 1 # This will be the first span
@@ -156,6 +159,48 @@ module DSPy
156
159
  end
157
160
  end
158
161
  end
162
+
163
+ def with_module(module_instance, label: nil)
164
+ stack = module_stack
165
+ entry = build_module_entry(module_instance, label)
166
+ stack.push(entry)
167
+ yield
168
+ ensure
169
+ if stack.last.equal?(entry)
170
+ stack.pop
171
+ else
172
+ stack.delete(entry)
173
+ end
174
+ end
175
+
176
+ def module_stack
177
+ current[:module_stack] ||= []
178
+ end
179
+
180
+ def module_context_attributes
181
+ stack = module_stack
182
+ return {} if stack.empty?
183
+
184
+ path = stack.map do |entry|
185
+ {
186
+ id: entry[:id],
187
+ class: entry[:class],
188
+ label: entry[:label]
189
+ }
190
+ end
191
+
192
+ ancestry_token = path.map { |node| node[:id] }.join('>')
193
+
194
+ {
195
+ module_path: path,
196
+ module_root: path.first,
197
+ module_leaf: path.last,
198
+ module_scope: {
199
+ ancestry_token: ancestry_token,
200
+ depth: path.length
201
+ }
202
+ }
203
+ end
159
204
 
160
205
  def clear!
161
206
  # Clear both the thread-specific key and the legacy key
@@ -173,6 +218,57 @@ module DSPy
173
218
  rescue
174
219
  false
175
220
  end
221
+
222
+ def sanitize_span_attributes(attributes)
223
+ attributes.each_with_object({}) do |(key, value), acc|
224
+ sanitized_value = sanitize_attribute_value(value)
225
+ acc[key] = sanitized_value
226
+ end
227
+ end
228
+
229
+ def sanitize_attribute_value(value)
230
+ case value
231
+ when nil, String, Integer, Float, TrueClass, FalseClass
232
+ value
233
+ when Time
234
+ value.iso8601(3)
235
+ when Array
236
+ begin
237
+ JSON.generate(value.map { |item| sanitize_attribute_value(item) })
238
+ rescue StandardError
239
+ value.map(&:to_s).to_s
240
+ end
241
+ when Hash
242
+ begin
243
+ sanitized_hash = value.each_with_object({}) do |(k, v), hash|
244
+ sanitized = sanitize_attribute_value(v)
245
+ hash[k.to_s] = sanitized unless sanitized.nil?
246
+ end
247
+ JSON.generate(sanitized_hash)
248
+ rescue StandardError
249
+ value.to_s
250
+ end
251
+ else
252
+ if defined?(T::Struct) && value.is_a?(T::Struct)
253
+ begin
254
+ struct_hash = value.to_h.transform_keys(&:to_s).transform_values { |v| sanitize_attribute_value(v) }
255
+ JSON.generate(struct_hash)
256
+ rescue StandardError
257
+ value.to_s
258
+ end
259
+ else
260
+ value.respond_to?(:to_json) ? value.to_json : value.to_s
261
+ end
262
+ end
263
+ end
264
+
265
+ def build_module_entry(module_instance, explicit_label)
266
+ {
267
+ id: (module_instance.respond_to?(:module_scope_id) ? module_instance.module_scope_id : SecureRandom.uuid),
268
+ class: module_instance.class.name,
269
+ label: explicit_label || (module_instance.respond_to?(:module_scope_label) ? module_instance.module_scope_label : nil)
270
+ }
271
+ end
176
272
  end
177
273
  end
178
- end
274
+ end
data/lib/dspy/errors.rb CHANGED
@@ -28,4 +28,22 @@ module DSPy
28
28
  MESSAGE
29
29
  end
30
30
  end
31
- end
31
+
32
+ class InstructionUpdateError < Error
33
+ def self.missing_instruction_capability(module_class)
34
+ new(<<~MESSAGE)
35
+ #{module_class} must implement `with_instruction(new_instruction)` to support DSPy teleprompters.
36
+
37
+ Update the module to return a new instance with the provided instruction, or opt out of teleprompter optimizers.
38
+ MESSAGE
39
+ end
40
+
41
+ def self.missing_examples_capability(module_class)
42
+ new(<<~MESSAGE)
43
+ #{module_class} must implement `with_examples(few_shot_examples)` to support DSPy teleprompters.
44
+
45
+ Update the module to return a new instance with the provided examples, or opt out of example optimization flows.
46
+ MESSAGE
47
+ end
48
+ end
49
+ end
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'datasets/ade'
4
-
5
3
  module DSPy
6
- module Datasets
4
+ class Evals
5
+ VERSION = '1.0.0'
7
6
  end
8
7
  end