dspy 0.22.0 → 0.23.0

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: e7897711c81ee7a4b72dd86a8fbeee9faf4cdf3e398b9878435e0da5750b0fc1
4
- data.tar.gz: '079cfdea700a852a94d08415bbdbbe2ff4c4e7d61e4dfae5e739fb88dec29c79'
3
+ metadata.gz: a74fa7e459b97d25d515492a9c8ef786981ca5212dcfe970b9f8311e5a2b7be4
4
+ data.tar.gz: 7cf45b5f12849e55b1a180f3b39e9054357a23477bbc8dacad4b85be35bc234a
5
5
  SHA512:
6
- metadata.gz: b81f7fefb5727bbdbc5f94b6618ba7de9cbea26de78324d2ed5f29f6aa1722f5a4e0191d99e3a1393962f5fe224198e25fe3b73fe34b59e99bd486f08bab1189
7
- data.tar.gz: 89515f64f64e681ae9cee83ffdad853b7e29409381351528324047b9a60627ed4721b79dd1943cee1ab3300996521aeb595e7a3ce57c90ad09cda47489acca77
6
+ metadata.gz: d7610ab7261fb039264acf5c2de69b70c7cc33932335faf70b97ba65e459a0d2048a428e8b1f49b6ccd73de3455ad4adc9351bb2e5dc58acfac47323086b7c3d
7
+ data.tar.gz: de8c212916e2135c27e881be529f12121439b12bbc8effca7dd98d6aa912f6057ae304e9d1aa5eee6c2dd2fafee904b3665cde4569f61d91360cacb6acbc2e43
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sorbet-runtime'
4
+
5
+ module DSPy
6
+ module Events
7
+ # Mixin for adding class-level event subscriptions
8
+ # Provides a clean way to subscribe to events at the class level
9
+ # instead of requiring instance-based subscriptions
10
+ #
11
+ # Usage:
12
+ # class MyTracker
13
+ # include DSPy::Events::SubscriberMixin
14
+ #
15
+ # add_subscription('llm.*') do |name, attrs|
16
+ # # Handle LLM events globally for this class
17
+ # end
18
+ # end
19
+ module SubscriberMixin
20
+ extend T::Sig
21
+
22
+ def self.included(base)
23
+ base.extend(ClassMethods)
24
+ base.class_eval do
25
+ @event_subscriptions = []
26
+ @subscription_mutex = Mutex.new
27
+
28
+ # Initialize subscriptions when the class is first loaded
29
+ @subscriptions_initialized = false
30
+ end
31
+ end
32
+
33
+ module ClassMethods
34
+ extend T::Sig
35
+
36
+ # Add a class-level event subscription
37
+ sig { params(pattern: String, block: T.proc.params(arg0: String, arg1: T::Hash[T.any(String, Symbol), T.untyped]).void).returns(String) }
38
+ def add_subscription(pattern, &block)
39
+ subscription_mutex.synchronize do
40
+ subscription_id = DSPy.events.subscribe(pattern, &block)
41
+ event_subscriptions << subscription_id
42
+ subscription_id
43
+ end
44
+ end
45
+
46
+ # Remove all subscriptions for this class
47
+ sig { void }
48
+ def unsubscribe_all
49
+ subscription_mutex.synchronize do
50
+ event_subscriptions.each { |id| DSPy.events.unsubscribe(id) }
51
+ event_subscriptions.clear
52
+ end
53
+ end
54
+
55
+ # Get list of active subscription IDs
56
+ sig { returns(T::Array[String]) }
57
+ def subscriptions
58
+ subscription_mutex.synchronize do
59
+ event_subscriptions.dup
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ # Thread-safe access to subscriptions array
66
+ sig { returns(T::Array[String]) }
67
+ def event_subscriptions
68
+ @event_subscriptions ||= []
69
+ end
70
+
71
+ # Thread-safe access to mutex
72
+ sig { returns(Mutex) }
73
+ def subscription_mutex
74
+ @subscription_mutex ||= Mutex.new
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -53,22 +53,9 @@ module DSPy
53
53
  def embed_batch(texts)
54
54
  ensure_ready!
55
55
 
56
- # Preprocess all texts
57
- cleaned_texts = texts.map { |text| preprocess_text(text) }
58
-
59
- # Generate embeddings in batch
60
- results = @model.call(cleaned_texts)
61
-
62
- # Extract and normalize embeddings
63
- results.map do |result|
64
- # Handle both single embeddings and batch results
65
- embedding = case result
66
- when Array
67
- result.flatten # Flatten in case of nested arrays
68
- else
69
- result.to_a.flatten
70
- end
71
- normalize_vector(embedding)
56
+ # Generate embeddings one by one (informers doesn't support true batch processing)
57
+ texts.map do |text|
58
+ embed(text)
72
59
  end
73
60
  end
74
61
 
@@ -140,8 +140,28 @@ module DSPy
140
140
  # Convert string keys to symbols
141
141
  symbolized_hash = value.transform_keys(&:to_sym)
142
142
 
143
+ # Get struct properties to understand what fields are expected
144
+ struct_props = struct_class.props
145
+
146
+ # Remove the _type field that DSPy adds for discriminating structs,
147
+ # but only if it's NOT a legitimate field in the struct definition
148
+ if !struct_props.key?(:_type) && symbolized_hash.key?(:_type)
149
+ symbolized_hash = symbolized_hash.except(:_type)
150
+ end
151
+
152
+ # Recursively coerce nested struct fields
153
+ coerced_hash = symbolized_hash.map do |key, val|
154
+ prop_info = struct_props[key]
155
+ if prop_info && prop_info[:type]
156
+ coerced_value = coerce_value_to_type(val, prop_info[:type])
157
+ [key, coerced_value]
158
+ else
159
+ [key, val]
160
+ end
161
+ end.to_h
162
+
143
163
  # Create the struct instance
144
- struct_class.new(**symbolized_hash)
164
+ struct_class.new(**coerced_hash)
145
165
  rescue ArgumentError => e
146
166
  # If struct creation fails, return the original value
147
167
  DSPy.logger.debug("Failed to coerce to struct #{struct_class}: #{e.message}")
@@ -54,7 +54,7 @@ module DSPy
54
54
 
55
55
  sig { returns(T::Hash[Symbol, T.untyped]) }
56
56
  def to_h
57
- @_struct.to_h
57
+ @_struct.serialize
58
58
  end
59
59
 
60
60
  private