dspy 0.15.2 → 0.15.3

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: 9d0e1b54d0ffe7ca9bcb7bde17b9169ee6d716eb8c7add676e6c3ac3dac6e917
4
- data.tar.gz: 9b541c527a240a54a8af47aa90cefcadc4618e8787901a1ed75036a227058621
3
+ metadata.gz: 9cc1edc860e1ab653456abf9dbeccd8b5229b17445e666ceecff554b17427094
4
+ data.tar.gz: e5e595e5ff48a0f61ee6ecd1f3e537656d4b6c44d2ded5c270a7b8f040c9d6cb
5
5
  SHA512:
6
- metadata.gz: e3bb79cd7637b6dfcb1af8d4697e38becd7125c70f5a3dd876efa89742225a56ddf3b82117fa1d8d712e1ccf9d732474c213683530cf8b6c76f96755a21b0adc
7
- data.tar.gz: 0bf7b51392457a7f960e611343d24dd0498c2b0cd3b6486c5993c04c9e5327b16f524447028b0d768bb560865bc750d4b5d517ae695b2bc72882483d84fccf94
6
+ metadata.gz: 9f4ec0f3c1b3e76e7bf0420c006f4b245f74d62118560de157742a5da9f60f3c2e669c0b3d9465982aa5d6770718e685595d54d451752e23869b83e2447e8c2b
7
+ data.tar.gz: 4ba4df139927c765a31ab35235c9522c5d12c7052577501f588f6444c7dd9b3c03185250fad8a95ecd1083928b58142d9f772edde2b2f3fe8018e41c1c2c0320
data/README.md CHANGED
@@ -184,13 +184,15 @@ DSPy.rb has rapidly evolved from experimental to production-ready:
184
184
 
185
185
  ## Roadmap - Battle-Testing Toward v1.0
186
186
 
187
- DSPy.rb is currently at **v0.13.0** and approaching stability. I'm focusing on real-world usage and refinement through the 0.14, 0.15+ series before committing to a stable v1.0 API.
187
+ DSPy.rb is currently at **v0.15.2** and approaching stability. I'm focusing on real-world usage and refinement through the 0.16+ series before committing to a stable v1.0 API.
188
188
 
189
189
  **Current Focus Areas:**
190
- - 🚧 **Ollama Support** - Local model integration
190
+ - **Ollama Support** - Local model integration (completed in v0.15.0)
191
+ - ✅ **Agentic Memory** - Persistent agent state management with Memory module
192
+ - 🚧 **Google Gemini Support** - Integration with Gemini models (#52)
191
193
  - 🚧 **Context Engineering** - Advanced prompt optimization techniques
192
194
  - 🚧 **MCP Support** - Model Context Protocol integration
193
- - 🚧 **Agentic Memory** - Persistent agent state management
195
+ - 🚧 **Additional Optimizer Support** - Expanding teleprompt capabilities
194
196
  - 🚧 **Performance Optimization** - Based on production usage patterns
195
197
 
196
198
  **v1.0 Philosophy:**
@@ -0,0 +1,282 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sorbet-runtime'
4
+ require_relative 'event_payloads'
5
+
6
+ module DSPy
7
+ module Instrumentation
8
+ # Factory for creating typed event payloads from hash data
9
+ module EventPayloadFactory
10
+ extend T::Sig
11
+ extend self
12
+
13
+ # Create appropriate event struct based on event name
14
+ sig { params(event_name: String, payload: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
15
+ def create_event(event_name, payload)
16
+ case event_name
17
+ when 'dspy.lm.request'
18
+ create_lm_request_event(payload)
19
+ when 'dspy.lm.tokens'
20
+ create_lm_tokens_event(payload)
21
+ when 'dspy.lm.response.parsed'
22
+ create_lm_response_parsed_event(payload)
23
+ when 'dspy.predict'
24
+ create_predict_event(payload)
25
+ when 'dspy.predict.validation_error'
26
+ create_predict_validation_error_event(payload)
27
+ when 'dspy.chain_of_thought'
28
+ create_chain_of_thought_event(payload)
29
+ when 'dspy.chain_of_thought.reasoning_complete'
30
+ create_chain_of_thought_reasoning_complete_event(payload)
31
+ when 'dspy.react.iteration'
32
+ create_react_iteration_event(payload)
33
+ when 'dspy.react.tool_call'
34
+ create_react_tool_call_event(payload)
35
+ when 'dspy.react.iteration_complete'
36
+ create_react_iteration_complete_event(payload)
37
+ when 'dspy.react.max_iterations'
38
+ create_react_max_iterations_event(payload)
39
+ when 'dspy.codeact.iteration'
40
+ create_codeact_iteration_event(payload)
41
+ when 'dspy.codeact.code_execution'
42
+ create_codeact_code_execution_event(payload)
43
+ else
44
+ # Return original payload for unhandled events
45
+ payload
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # LM Request Event
52
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(LMRequestEvent) }
53
+ def create_lm_request_event(payload)
54
+ # Extract timestamp, handling both timestamp and timestamp_ns keys
55
+ timestamp = extract_timestamp(payload)
56
+
57
+ LMRequestEvent.new(
58
+ timestamp: timestamp,
59
+ duration_ms: payload[:duration_ms] || 0.0,
60
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
61
+ status: payload[:status] || 'success',
62
+ gen_ai_operation_name: payload[:gen_ai_operation_name] || 'unknown',
63
+ gen_ai_system: payload[:gen_ai_system] || 'unknown',
64
+ gen_ai_request_model: payload[:gen_ai_request_model] || 'unknown',
65
+ signature_class: payload[:signature_class],
66
+ provider: payload[:provider] || 'unknown',
67
+ adapter_class: payload[:adapter_class] || 'unknown',
68
+ input_size: payload[:input_size] || 0,
69
+ error_type: payload[:error_type],
70
+ error_message: payload[:error_message]
71
+ )
72
+ end
73
+
74
+ # Helper to extract timestamp from various formats
75
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(String) }
76
+ def extract_timestamp(payload)
77
+ if payload[:timestamp]
78
+ payload[:timestamp]
79
+ elsif payload[:timestamp_ns]
80
+ # Convert nanoseconds to ISO8601 for storage in struct
81
+ Time.at(payload[:timestamp_ns] / 1_000_000_000.0).iso8601
82
+ else
83
+ Time.now.iso8601
84
+ end
85
+ end
86
+
87
+ # LM Tokens Event
88
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(LMTokensEvent) }
89
+ def create_lm_tokens_event(payload)
90
+ LMTokensEvent.new(
91
+ timestamp: extract_timestamp(payload),
92
+ status: payload[:status] || 'success',
93
+ input_tokens: payload[:input_tokens] || 0,
94
+ output_tokens: payload[:output_tokens] || 0,
95
+ total_tokens: payload[:total_tokens] || 0,
96
+ gen_ai_system: payload[:gen_ai_system] || 'unknown',
97
+ gen_ai_request_model: payload[:gen_ai_request_model] || 'unknown',
98
+ signature_class: payload[:signature_class]
99
+ )
100
+ end
101
+
102
+ # LM Response Parsed Event
103
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(LMResponseParsedEvent) }
104
+ def create_lm_response_parsed_event(payload)
105
+ LMResponseParsedEvent.new(
106
+ timestamp: extract_timestamp(payload),
107
+ duration_ms: payload[:duration_ms] || 0.0,
108
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
109
+ status: payload[:status] || 'success',
110
+ signature_class: payload[:signature_class] || 'unknown',
111
+ provider: payload[:provider] || 'unknown',
112
+ success: payload[:success] || false,
113
+ response_length: payload[:response_length] || 0,
114
+ parse_type: payload[:parse_type],
115
+ error_type: payload[:error_type],
116
+ error_message: payload[:error_message]
117
+ )
118
+ end
119
+
120
+ # Predict Event
121
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(PredictEvent) }
122
+ def create_predict_event(payload)
123
+ PredictEvent.new(
124
+ timestamp: extract_timestamp(payload),
125
+ duration_ms: payload[:duration_ms] || 0.0,
126
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
127
+ status: payload[:status] || 'success',
128
+ signature_class: payload[:signature_class] || 'unknown',
129
+ module_name: payload[:module_name] || 'unknown',
130
+ model: payload[:model] || 'unknown',
131
+ provider: payload[:provider] || 'unknown',
132
+ input_fields: payload[:input_fields] || [],
133
+ input_size: payload[:input_size],
134
+ output_size: payload[:output_size],
135
+ error_type: payload[:error_type],
136
+ error_message: payload[:error_message]
137
+ )
138
+ end
139
+
140
+ # Predict Validation Error Event
141
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(PredictValidationErrorEvent) }
142
+ def create_predict_validation_error_event(payload)
143
+ PredictValidationErrorEvent.new(
144
+ timestamp: payload[:timestamp] || Time.now.iso8601,
145
+ status: payload[:status] || 'error',
146
+ signature_class: payload[:signature_class] || 'unknown',
147
+ module_name: payload[:module_name] || 'unknown',
148
+ field_name: payload[:field_name] || 'unknown',
149
+ error_message: payload[:error_message] || 'unknown error',
150
+ retry_count: payload[:retry_count] || 0
151
+ )
152
+ end
153
+
154
+ # Chain of Thought Event
155
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(ChainOfThoughtEvent) }
156
+ def create_chain_of_thought_event(payload)
157
+ ChainOfThoughtEvent.new(
158
+ timestamp: extract_timestamp(payload),
159
+ duration_ms: payload[:duration_ms] || 0.0,
160
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
161
+ status: payload[:status] || 'success',
162
+ signature_class: payload[:signature_class] || 'unknown',
163
+ module_name: payload[:module_name] || 'unknown',
164
+ model: payload[:model] || 'unknown',
165
+ provider: payload[:provider] || 'unknown',
166
+ reasoning_length: payload[:reasoning_length],
167
+ answer_length: payload[:answer_length],
168
+ error_type: payload[:error_type],
169
+ error_message: payload[:error_message]
170
+ )
171
+ end
172
+
173
+ # Chain of Thought Reasoning Complete Event
174
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(ChainOfThoughtReasoningCompleteEvent) }
175
+ def create_chain_of_thought_reasoning_complete_event(payload)
176
+ ChainOfThoughtReasoningCompleteEvent.new(
177
+ timestamp: payload[:timestamp] || Time.now.iso8601,
178
+ status: payload[:status] || 'success',
179
+ signature_class: payload[:signature_class] || 'unknown',
180
+ module_name: payload[:module_name] || 'unknown',
181
+ reasoning_length: payload[:reasoning_length] || 0,
182
+ answer_present: payload[:answer_present] || false
183
+ )
184
+ end
185
+
186
+ # ReAct Iteration Event
187
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(ReactIterationEvent) }
188
+ def create_react_iteration_event(payload)
189
+ ReactIterationEvent.new(
190
+ timestamp: payload[:timestamp] || Time.now.iso8601,
191
+ duration_ms: payload[:duration_ms] || 0.0,
192
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
193
+ status: payload[:status] || 'success',
194
+ iteration: payload[:iteration] || 0,
195
+ max_iterations: payload[:max_iterations] || 5,
196
+ history_length: payload[:history_length] || 0,
197
+ tools_used_so_far: payload[:tools_used_so_far] || [],
198
+ error_type: payload[:error_type],
199
+ error_message: payload[:error_message]
200
+ )
201
+ end
202
+
203
+ # ReAct Tool Call Event
204
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(ReactToolCallEvent) }
205
+ def create_react_tool_call_event(payload)
206
+ ReactToolCallEvent.new(
207
+ timestamp: payload[:timestamp] || Time.now.iso8601,
208
+ duration_ms: payload[:duration_ms] || 0.0,
209
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
210
+ status: payload[:status] || 'success',
211
+ iteration: payload[:iteration] || 0,
212
+ tool_name: payload[:tool_name] || 'unknown',
213
+ tool_input: payload[:tool_input],
214
+ error_type: payload[:error_type],
215
+ error_message: payload[:error_message]
216
+ )
217
+ end
218
+
219
+ # ReAct Iteration Complete Event
220
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(ReactIterationCompleteEvent) }
221
+ def create_react_iteration_complete_event(payload)
222
+ ReactIterationCompleteEvent.new(
223
+ timestamp: payload[:timestamp] || Time.now.iso8601,
224
+ status: payload[:status] || 'success',
225
+ iteration: payload[:iteration] || 0,
226
+ thought: payload[:thought] || '',
227
+ action: payload[:action] || '',
228
+ action_input: payload[:action_input],
229
+ observation: payload[:observation] || '',
230
+ tools_used: payload[:tools_used] || []
231
+ )
232
+ end
233
+
234
+ # ReAct Max Iterations Event
235
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(ReactMaxIterationsEvent) }
236
+ def create_react_max_iterations_event(payload)
237
+ ReactMaxIterationsEvent.new(
238
+ timestamp: payload[:timestamp] || Time.now.iso8601,
239
+ status: payload[:status] || 'warning',
240
+ iteration_count: payload[:iteration_count] || 0,
241
+ max_iterations: payload[:max_iterations] || 5,
242
+ tools_used: payload[:tools_used] || [],
243
+ final_history_length: payload[:final_history_length] || 0
244
+ )
245
+ end
246
+
247
+ # CodeAct Iteration Event
248
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(CodeActIterationEvent) }
249
+ def create_codeact_iteration_event(payload)
250
+ CodeActIterationEvent.new(
251
+ timestamp: payload[:timestamp] || Time.now.iso8601,
252
+ duration_ms: payload[:duration_ms] || 0.0,
253
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
254
+ status: payload[:status] || 'success',
255
+ iteration: payload[:iteration] || 0,
256
+ max_iterations: payload[:max_iterations] || 5,
257
+ history_length: payload[:history_length] || 0,
258
+ code_blocks_executed: payload[:code_blocks_executed] || 0,
259
+ error_type: payload[:error_type],
260
+ error_message: payload[:error_message]
261
+ )
262
+ end
263
+
264
+ # CodeAct Code Execution Event
265
+ sig { params(payload: T::Hash[Symbol, T.untyped]).returns(CodeActCodeExecutionEvent) }
266
+ def create_codeact_code_execution_event(payload)
267
+ CodeActCodeExecutionEvent.new(
268
+ timestamp: payload[:timestamp] || Time.now.iso8601,
269
+ duration_ms: payload[:duration_ms] || 0.0,
270
+ cpu_time_ms: payload[:cpu_time_ms] || 0.0,
271
+ status: payload[:status] || 'success',
272
+ iteration: payload[:iteration] || 0,
273
+ code_type: payload[:code_type] || 'unknown',
274
+ code_length: payload[:code_length] || 0,
275
+ execution_success: payload[:execution_success] || false,
276
+ error_type: payload[:error_type],
277
+ error_message: payload[:error_message]
278
+ )
279
+ end
280
+ end
281
+ end
282
+ end