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 +4 -4
- data/README.md +5 -3
- data/lib/dspy/instrumentation/event_payload_factory.rb +282 -0
- data/lib/dspy/instrumentation/event_payloads.rb +476 -0
- data/lib/dspy/instrumentation.rb +36 -4
- data/lib/dspy/lm/adapters/anthropic_adapter.rb +11 -6
- data/lib/dspy/lm/adapters/openai_adapter.rb +13 -8
- data/lib/dspy/lm/message.rb +99 -0
- data/lib/dspy/lm/message_builder.rb +26 -3
- data/lib/dspy/lm/response.rb +143 -14
- data/lib/dspy/lm/strategies/anthropic_tool_use_strategy.rb +2 -2
- data/lib/dspy/lm.rb +95 -19
- data/lib/dspy/mixins/type_coercion.rb +48 -0
- data/lib/dspy/subscribers/otel_subscriber.rb +3 -4
- data/lib/dspy/version.rb +1 -1
- metadata +6 -6
@@ -0,0 +1,476 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
|
5
|
+
module DSPy
|
6
|
+
module Instrumentation
|
7
|
+
# Type-safe event payload structures for DSPy instrumentation
|
8
|
+
# Each event is a complete T::Struct (no inheritance due to T::Struct limitations)
|
9
|
+
|
10
|
+
# LM Request event payload
|
11
|
+
class LMRequestEvent < T::Struct
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
# Common fields
|
15
|
+
const :timestamp, String
|
16
|
+
const :duration_ms, Float
|
17
|
+
const :cpu_time_ms, Float
|
18
|
+
const :status, String
|
19
|
+
|
20
|
+
# LM-specific fields
|
21
|
+
const :gen_ai_operation_name, String
|
22
|
+
const :gen_ai_system, String
|
23
|
+
const :gen_ai_request_model, String
|
24
|
+
const :signature_class, T.nilable(String), default: nil
|
25
|
+
const :provider, String
|
26
|
+
const :adapter_class, String
|
27
|
+
const :input_size, Integer
|
28
|
+
|
29
|
+
# Error fields (optional)
|
30
|
+
const :error_type, T.nilable(String), default: nil
|
31
|
+
const :error_message, T.nilable(String), default: nil
|
32
|
+
|
33
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
34
|
+
def to_h
|
35
|
+
hash = {
|
36
|
+
timestamp: timestamp,
|
37
|
+
duration_ms: duration_ms,
|
38
|
+
cpu_time_ms: cpu_time_ms,
|
39
|
+
status: status,
|
40
|
+
gen_ai_operation_name: gen_ai_operation_name,
|
41
|
+
gen_ai_system: gen_ai_system,
|
42
|
+
gen_ai_request_model: gen_ai_request_model,
|
43
|
+
provider: provider,
|
44
|
+
adapter_class: adapter_class,
|
45
|
+
input_size: input_size
|
46
|
+
}
|
47
|
+
hash[:signature_class] = signature_class if signature_class
|
48
|
+
hash[:error_type] = error_type if error_type
|
49
|
+
hash[:error_message] = error_message if error_message
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Token usage event payload
|
55
|
+
class LMTokensEvent < T::Struct
|
56
|
+
extend T::Sig
|
57
|
+
|
58
|
+
# Common fields
|
59
|
+
const :timestamp, String
|
60
|
+
const :status, String
|
61
|
+
|
62
|
+
# Token-specific fields
|
63
|
+
const :input_tokens, Integer
|
64
|
+
const :output_tokens, Integer
|
65
|
+
const :total_tokens, Integer
|
66
|
+
const :gen_ai_system, String
|
67
|
+
const :gen_ai_request_model, String
|
68
|
+
const :signature_class, T.nilable(String), default: nil
|
69
|
+
|
70
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
71
|
+
def to_h
|
72
|
+
hash = {
|
73
|
+
timestamp: timestamp,
|
74
|
+
status: status,
|
75
|
+
input_tokens: input_tokens,
|
76
|
+
output_tokens: output_tokens,
|
77
|
+
total_tokens: total_tokens,
|
78
|
+
gen_ai_system: gen_ai_system,
|
79
|
+
gen_ai_request_model: gen_ai_request_model
|
80
|
+
}
|
81
|
+
hash[:signature_class] = signature_class if signature_class
|
82
|
+
hash
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# LM Response parsed event payload
|
87
|
+
class LMResponseParsedEvent < T::Struct
|
88
|
+
extend T::Sig
|
89
|
+
|
90
|
+
# Common fields
|
91
|
+
const :timestamp, String
|
92
|
+
const :duration_ms, Float
|
93
|
+
const :cpu_time_ms, Float
|
94
|
+
const :status, String
|
95
|
+
|
96
|
+
# Response parsing fields
|
97
|
+
const :signature_class, String
|
98
|
+
const :provider, String
|
99
|
+
const :success, T::Boolean
|
100
|
+
const :response_length, Integer
|
101
|
+
const :parse_type, T.nilable(String), default: nil
|
102
|
+
|
103
|
+
# Error fields (optional)
|
104
|
+
const :error_type, T.nilable(String), default: nil
|
105
|
+
const :error_message, T.nilable(String), default: nil
|
106
|
+
|
107
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
108
|
+
def to_h
|
109
|
+
hash = {
|
110
|
+
timestamp: timestamp,
|
111
|
+
duration_ms: duration_ms,
|
112
|
+
cpu_time_ms: cpu_time_ms,
|
113
|
+
status: status,
|
114
|
+
signature_class: signature_class,
|
115
|
+
provider: provider,
|
116
|
+
success: success,
|
117
|
+
response_length: response_length
|
118
|
+
}
|
119
|
+
hash[:parse_type] = parse_type if parse_type
|
120
|
+
hash[:error_type] = error_type if error_type
|
121
|
+
hash[:error_message] = error_message if error_message
|
122
|
+
hash
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Predict event payload
|
127
|
+
class PredictEvent < T::Struct
|
128
|
+
extend T::Sig
|
129
|
+
|
130
|
+
# Common fields
|
131
|
+
const :timestamp, String
|
132
|
+
const :duration_ms, Float
|
133
|
+
const :cpu_time_ms, Float
|
134
|
+
const :status, String
|
135
|
+
|
136
|
+
# Predict-specific fields
|
137
|
+
const :signature_class, String
|
138
|
+
const :module_name, String
|
139
|
+
const :model, String
|
140
|
+
const :provider, String
|
141
|
+
const :input_fields, T::Array[String]
|
142
|
+
const :input_size, T.nilable(Integer), default: nil
|
143
|
+
const :output_size, T.nilable(Integer), default: nil
|
144
|
+
|
145
|
+
# Error fields (optional)
|
146
|
+
const :error_type, T.nilable(String), default: nil
|
147
|
+
const :error_message, T.nilable(String), default: nil
|
148
|
+
|
149
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
150
|
+
def to_h
|
151
|
+
hash = {
|
152
|
+
timestamp: timestamp,
|
153
|
+
duration_ms: duration_ms,
|
154
|
+
cpu_time_ms: cpu_time_ms,
|
155
|
+
status: status,
|
156
|
+
signature_class: signature_class,
|
157
|
+
module_name: module_name,
|
158
|
+
model: model,
|
159
|
+
provider: provider,
|
160
|
+
input_fields: input_fields
|
161
|
+
}
|
162
|
+
hash[:input_size] = input_size if input_size
|
163
|
+
hash[:output_size] = output_size if output_size
|
164
|
+
hash[:error_type] = error_type if error_type
|
165
|
+
hash[:error_message] = error_message if error_message
|
166
|
+
hash
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
# Chain of Thought event payload
|
171
|
+
class ChainOfThoughtEvent < T::Struct
|
172
|
+
extend T::Sig
|
173
|
+
|
174
|
+
# Common fields
|
175
|
+
const :timestamp, String
|
176
|
+
const :duration_ms, Float
|
177
|
+
const :cpu_time_ms, Float
|
178
|
+
const :status, String
|
179
|
+
|
180
|
+
# CoT-specific fields
|
181
|
+
const :signature_class, String
|
182
|
+
const :module_name, String
|
183
|
+
const :model, String
|
184
|
+
const :provider, String
|
185
|
+
const :reasoning_length, T.nilable(Integer), default: nil
|
186
|
+
const :answer_length, T.nilable(Integer), default: nil
|
187
|
+
|
188
|
+
# Error fields (optional)
|
189
|
+
const :error_type, T.nilable(String), default: nil
|
190
|
+
const :error_message, T.nilable(String), default: nil
|
191
|
+
|
192
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
193
|
+
def to_h
|
194
|
+
hash = {
|
195
|
+
timestamp: timestamp,
|
196
|
+
duration_ms: duration_ms,
|
197
|
+
cpu_time_ms: cpu_time_ms,
|
198
|
+
status: status,
|
199
|
+
signature_class: signature_class,
|
200
|
+
module_name: module_name,
|
201
|
+
model: model,
|
202
|
+
provider: provider
|
203
|
+
}
|
204
|
+
hash[:reasoning_length] = reasoning_length if reasoning_length
|
205
|
+
hash[:answer_length] = answer_length if answer_length
|
206
|
+
hash[:error_type] = error_type if error_type
|
207
|
+
hash[:error_message] = error_message if error_message
|
208
|
+
hash
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# ReAct iteration event payload
|
213
|
+
class ReactIterationEvent < T::Struct
|
214
|
+
extend T::Sig
|
215
|
+
|
216
|
+
# Common fields
|
217
|
+
const :timestamp, String
|
218
|
+
const :duration_ms, Float
|
219
|
+
const :cpu_time_ms, Float
|
220
|
+
const :status, String
|
221
|
+
|
222
|
+
# ReAct-specific fields
|
223
|
+
const :iteration, Integer
|
224
|
+
const :max_iterations, Integer
|
225
|
+
const :history_length, Integer
|
226
|
+
const :tools_used_so_far, T::Array[String]
|
227
|
+
|
228
|
+
# Error fields (optional)
|
229
|
+
const :error_type, T.nilable(String), default: nil
|
230
|
+
const :error_message, T.nilable(String), default: nil
|
231
|
+
|
232
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
233
|
+
def to_h
|
234
|
+
hash = {
|
235
|
+
timestamp: timestamp,
|
236
|
+
duration_ms: duration_ms,
|
237
|
+
cpu_time_ms: cpu_time_ms,
|
238
|
+
status: status,
|
239
|
+
iteration: iteration,
|
240
|
+
max_iterations: max_iterations,
|
241
|
+
history_length: history_length,
|
242
|
+
tools_used_so_far: tools_used_so_far
|
243
|
+
}
|
244
|
+
hash[:error_type] = error_type if error_type
|
245
|
+
hash[:error_message] = error_message if error_message
|
246
|
+
hash
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# ReAct tool call event payload
|
251
|
+
class ReactToolCallEvent < T::Struct
|
252
|
+
extend T::Sig
|
253
|
+
|
254
|
+
# Common fields
|
255
|
+
const :timestamp, String
|
256
|
+
const :duration_ms, Float
|
257
|
+
const :cpu_time_ms, Float
|
258
|
+
const :status, String
|
259
|
+
|
260
|
+
# Tool call fields
|
261
|
+
const :iteration, Integer
|
262
|
+
const :tool_name, String
|
263
|
+
const :tool_input, T.untyped
|
264
|
+
|
265
|
+
# Error fields (optional)
|
266
|
+
const :error_type, T.nilable(String), default: nil
|
267
|
+
const :error_message, T.nilable(String), default: nil
|
268
|
+
|
269
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
270
|
+
def to_h
|
271
|
+
hash = {
|
272
|
+
timestamp: timestamp,
|
273
|
+
duration_ms: duration_ms,
|
274
|
+
cpu_time_ms: cpu_time_ms,
|
275
|
+
status: status,
|
276
|
+
iteration: iteration,
|
277
|
+
tool_name: tool_name,
|
278
|
+
tool_input: tool_input
|
279
|
+
}
|
280
|
+
hash[:error_type] = error_type if error_type
|
281
|
+
hash[:error_message] = error_message if error_message
|
282
|
+
hash
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# ReAct iteration complete event (emit, not instrument)
|
287
|
+
class ReactIterationCompleteEvent < T::Struct
|
288
|
+
extend T::Sig
|
289
|
+
|
290
|
+
# Common fields
|
291
|
+
const :timestamp, String
|
292
|
+
const :status, String
|
293
|
+
|
294
|
+
# Iteration complete fields
|
295
|
+
const :iteration, Integer
|
296
|
+
const :thought, String
|
297
|
+
const :action, String
|
298
|
+
const :action_input, T.untyped
|
299
|
+
const :observation, String
|
300
|
+
const :tools_used, T::Array[String]
|
301
|
+
|
302
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
303
|
+
def to_h
|
304
|
+
{
|
305
|
+
timestamp: timestamp,
|
306
|
+
status: status,
|
307
|
+
iteration: iteration,
|
308
|
+
thought: thought,
|
309
|
+
action: action,
|
310
|
+
action_input: action_input,
|
311
|
+
observation: observation,
|
312
|
+
tools_used: tools_used
|
313
|
+
}
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# ReAct max iterations event (emit, not instrument)
|
318
|
+
class ReactMaxIterationsEvent < T::Struct
|
319
|
+
extend T::Sig
|
320
|
+
|
321
|
+
# Common fields
|
322
|
+
const :timestamp, String
|
323
|
+
const :status, String
|
324
|
+
|
325
|
+
# Max iterations fields
|
326
|
+
const :iteration_count, Integer
|
327
|
+
const :max_iterations, Integer
|
328
|
+
const :tools_used, T::Array[String]
|
329
|
+
const :final_history_length, Integer
|
330
|
+
|
331
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
332
|
+
def to_h
|
333
|
+
{
|
334
|
+
timestamp: timestamp,
|
335
|
+
status: status,
|
336
|
+
iteration_count: iteration_count,
|
337
|
+
max_iterations: max_iterations,
|
338
|
+
tools_used: tools_used,
|
339
|
+
final_history_length: final_history_length
|
340
|
+
}
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# CodeAct iteration event payload
|
345
|
+
class CodeActIterationEvent < T::Struct
|
346
|
+
extend T::Sig
|
347
|
+
|
348
|
+
# Common fields
|
349
|
+
const :timestamp, String
|
350
|
+
const :duration_ms, Float
|
351
|
+
const :cpu_time_ms, Float
|
352
|
+
const :status, String
|
353
|
+
|
354
|
+
# CodeAct-specific fields
|
355
|
+
const :iteration, Integer
|
356
|
+
const :max_iterations, Integer
|
357
|
+
const :history_length, Integer
|
358
|
+
const :code_blocks_executed, Integer
|
359
|
+
|
360
|
+
# Error fields (optional)
|
361
|
+
const :error_type, T.nilable(String), default: nil
|
362
|
+
const :error_message, T.nilable(String), default: nil
|
363
|
+
|
364
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
365
|
+
def to_h
|
366
|
+
hash = {
|
367
|
+
timestamp: timestamp,
|
368
|
+
duration_ms: duration_ms,
|
369
|
+
cpu_time_ms: cpu_time_ms,
|
370
|
+
status: status,
|
371
|
+
iteration: iteration,
|
372
|
+
max_iterations: max_iterations,
|
373
|
+
history_length: history_length,
|
374
|
+
code_blocks_executed: code_blocks_executed
|
375
|
+
}
|
376
|
+
hash[:error_type] = error_type if error_type
|
377
|
+
hash[:error_message] = error_message if error_message
|
378
|
+
hash
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# CodeAct code execution event payload
|
383
|
+
class CodeActCodeExecutionEvent < T::Struct
|
384
|
+
extend T::Sig
|
385
|
+
|
386
|
+
# Common fields
|
387
|
+
const :timestamp, String
|
388
|
+
const :duration_ms, Float
|
389
|
+
const :cpu_time_ms, Float
|
390
|
+
const :status, String
|
391
|
+
|
392
|
+
# Code execution fields
|
393
|
+
const :iteration, Integer
|
394
|
+
const :code_type, String
|
395
|
+
const :code_length, Integer
|
396
|
+
const :execution_success, T::Boolean
|
397
|
+
|
398
|
+
# Error fields (optional)
|
399
|
+
const :error_type, T.nilable(String), default: nil
|
400
|
+
const :error_message, T.nilable(String), default: nil
|
401
|
+
|
402
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
403
|
+
def to_h
|
404
|
+
hash = {
|
405
|
+
timestamp: timestamp,
|
406
|
+
duration_ms: duration_ms,
|
407
|
+
cpu_time_ms: cpu_time_ms,
|
408
|
+
status: status,
|
409
|
+
iteration: iteration,
|
410
|
+
code_type: code_type,
|
411
|
+
code_length: code_length,
|
412
|
+
execution_success: execution_success
|
413
|
+
}
|
414
|
+
hash[:error_type] = error_type if error_type
|
415
|
+
hash[:error_message] = error_message if error_message
|
416
|
+
hash
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
# Chain of thought reasoning complete event (emit, not instrument)
|
421
|
+
class ChainOfThoughtReasoningCompleteEvent < T::Struct
|
422
|
+
extend T::Sig
|
423
|
+
|
424
|
+
# Common fields
|
425
|
+
const :timestamp, String
|
426
|
+
const :status, String
|
427
|
+
|
428
|
+
# Reasoning complete fields
|
429
|
+
const :signature_class, String
|
430
|
+
const :module_name, String
|
431
|
+
const :reasoning_length, Integer
|
432
|
+
const :answer_present, T::Boolean
|
433
|
+
|
434
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
435
|
+
def to_h
|
436
|
+
{
|
437
|
+
timestamp: timestamp,
|
438
|
+
status: status,
|
439
|
+
signature_class: signature_class,
|
440
|
+
module_name: module_name,
|
441
|
+
reasoning_length: reasoning_length,
|
442
|
+
answer_present: answer_present
|
443
|
+
}
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
# Validation error event (emit, not instrument)
|
448
|
+
class PredictValidationErrorEvent < T::Struct
|
449
|
+
extend T::Sig
|
450
|
+
|
451
|
+
# Common fields
|
452
|
+
const :timestamp, String
|
453
|
+
const :status, String
|
454
|
+
|
455
|
+
# Validation error fields
|
456
|
+
const :signature_class, String
|
457
|
+
const :module_name, String
|
458
|
+
const :field_name, String
|
459
|
+
const :error_message, String
|
460
|
+
const :retry_count, Integer
|
461
|
+
|
462
|
+
sig { returns(T::Hash[Symbol, T.untyped]) }
|
463
|
+
def to_h
|
464
|
+
{
|
465
|
+
timestamp: timestamp,
|
466
|
+
status: status,
|
467
|
+
signature_class: signature_class,
|
468
|
+
module_name: module_name,
|
469
|
+
field_name: field_name,
|
470
|
+
error_message: error_message,
|
471
|
+
retry_count: retry_count
|
472
|
+
}
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
end
|
data/lib/dspy/instrumentation.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'dry-monitor'
|
4
4
|
require 'dry-configurable'
|
5
5
|
require 'time'
|
6
|
+
require_relative 'instrumentation/event_payload_factory'
|
6
7
|
|
7
8
|
module DSPy
|
8
9
|
# Core instrumentation module using dry-monitor for event emission
|
@@ -133,7 +134,9 @@ module DSPy
|
|
133
134
|
status: 'success'
|
134
135
|
).merge(generate_timestamp)
|
135
136
|
|
136
|
-
|
137
|
+
# Create typed event struct
|
138
|
+
event_struct = EventPayloadFactory.create_event(event_name, enhanced_payload)
|
139
|
+
self.emit_event(event_name, event_struct)
|
137
140
|
result
|
138
141
|
rescue => error
|
139
142
|
end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -147,7 +150,9 @@ module DSPy
|
|
147
150
|
error_message: error.message
|
148
151
|
).merge(generate_timestamp)
|
149
152
|
|
150
|
-
|
153
|
+
# Create typed event struct
|
154
|
+
event_struct = EventPayloadFactory.create_event(event_name, error_payload)
|
155
|
+
self.emit_event(event_name, event_struct)
|
151
156
|
raise
|
152
157
|
end
|
153
158
|
end
|
@@ -161,7 +166,9 @@ module DSPy
|
|
161
166
|
status: payload[:status] || 'success'
|
162
167
|
).merge(generate_timestamp)
|
163
168
|
|
164
|
-
|
169
|
+
# Create typed event struct
|
170
|
+
event_struct = EventPayloadFactory.create_event(event_name, enhanced_payload)
|
171
|
+
self.emit_event(event_name, event_struct)
|
165
172
|
end
|
166
173
|
|
167
174
|
# Register additional events dynamically (useful for testing)
|
@@ -183,7 +190,32 @@ module DSPy
|
|
183
190
|
|
184
191
|
def self.emit_event(event_name, payload)
|
185
192
|
# Only emit events - subscribers self-register when explicitly created
|
186
|
-
|
193
|
+
# Convert struct to hash if needed (dry-monitor expects hash)
|
194
|
+
if payload.respond_to?(:to_h)
|
195
|
+
payload_hash = payload.to_h
|
196
|
+
# Restore original timestamp format if needed
|
197
|
+
restore_timestamp_format(payload_hash)
|
198
|
+
else
|
199
|
+
payload_hash = payload
|
200
|
+
end
|
201
|
+
notifications.instrument(event_name, payload_hash)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Restore timestamp to original format based on configuration
|
205
|
+
def self.restore_timestamp_format(payload_hash)
|
206
|
+
return unless payload_hash[:timestamp]
|
207
|
+
|
208
|
+
case DSPy.config.instrumentation.timestamp_format
|
209
|
+
when DSPy::TimestampFormat::UNIX_NANO
|
210
|
+
# Convert ISO8601 back to nanoseconds
|
211
|
+
timestamp = Time.parse(payload_hash[:timestamp])
|
212
|
+
payload_hash.delete(:timestamp)
|
213
|
+
payload_hash[:timestamp_ns] = (timestamp.to_f * 1_000_000_000).to_i
|
214
|
+
when DSPy::TimestampFormat::RFC3339_NANO
|
215
|
+
# Convert to RFC3339 with nanoseconds
|
216
|
+
timestamp = Time.parse(payload_hash[:timestamp])
|
217
|
+
payload_hash[:timestamp] = timestamp.strftime('%Y-%m-%dT%H:%M:%S.%9N%z')
|
218
|
+
end
|
187
219
|
end
|
188
220
|
|
189
221
|
def self.setup_subscribers
|
@@ -49,14 +49,16 @@ module DSPy
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
# Create typed metadata for streaming response
|
53
|
+
metadata = ResponseMetadataFactory.create('anthropic', {
|
54
|
+
model: model,
|
55
|
+
streaming: true
|
56
|
+
})
|
57
|
+
|
52
58
|
Response.new(
|
53
59
|
content: content,
|
54
60
|
usage: nil, # Usage not available in streaming
|
55
|
-
metadata:
|
56
|
-
provider: 'anthropic',
|
57
|
-
model: model,
|
58
|
-
streaming: true
|
59
|
-
}
|
61
|
+
metadata: metadata
|
60
62
|
)
|
61
63
|
else
|
62
64
|
response = @client.messages.create(**request_params)
|
@@ -99,10 +101,13 @@ module DSPy
|
|
99
101
|
# Add tool calls to metadata if present
|
100
102
|
metadata[:tool_calls] = tool_calls unless tool_calls.empty?
|
101
103
|
|
104
|
+
# Create typed metadata
|
105
|
+
typed_metadata = ResponseMetadataFactory.create('anthropic', metadata)
|
106
|
+
|
102
107
|
Response.new(
|
103
108
|
content: content,
|
104
109
|
usage: usage_struct,
|
105
|
-
metadata:
|
110
|
+
metadata: typed_metadata
|
106
111
|
)
|
107
112
|
end
|
108
113
|
rescue => e
|
@@ -43,7 +43,8 @@ module DSPy
|
|
43
43
|
raise AdapterError, "OpenAI API error: #{response.error}"
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
choice = response.choices.first
|
47
|
+
message = choice.message
|
47
48
|
content = message.content
|
48
49
|
usage = response.usage
|
49
50
|
|
@@ -55,16 +56,20 @@ module DSPy
|
|
55
56
|
# Convert usage data to typed struct
|
56
57
|
usage_struct = UsageFactory.create('openai', usage)
|
57
58
|
|
59
|
+
# Create typed metadata
|
60
|
+
metadata = ResponseMetadataFactory.create('openai', {
|
61
|
+
model: model,
|
62
|
+
response_id: response.id,
|
63
|
+
created: response.created,
|
64
|
+
structured_output: @structured_outputs_enabled && signature && supports_structured_outputs?,
|
65
|
+
system_fingerprint: response.system_fingerprint,
|
66
|
+
finish_reason: choice.finish_reason
|
67
|
+
})
|
68
|
+
|
58
69
|
Response.new(
|
59
70
|
content: content,
|
60
71
|
usage: usage_struct,
|
61
|
-
metadata:
|
62
|
-
provider: 'openai',
|
63
|
-
model: model,
|
64
|
-
response_id: response.id,
|
65
|
-
created: response.created,
|
66
|
-
structured_output: @structured_outputs_enabled && signature && supports_structured_outputs?
|
67
|
-
}
|
72
|
+
metadata: metadata
|
68
73
|
)
|
69
74
|
rescue => e
|
70
75
|
raise AdapterError, "OpenAI adapter error: #{e.message}"
|