dspy 0.30.1 → 0.31.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.
data/lib/dspy/prompt.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'sorbet-runtime'
5
+ require 'sorbet/toon'
6
+
4
7
  require_relative 'few_shot_example'
8
+ require_relative 'schema/sorbet_toon_adapter'
5
9
 
6
10
  module DSPy
7
11
  class Prompt
@@ -33,6 +37,15 @@ module DSPy
33
37
  DSPy.config.lm&.schema_format || @schema_format || :json
34
38
  end
35
39
 
40
+ sig { returns(Symbol) }
41
+ def data_format
42
+ return @data_format if @data_format && @data_format != :json
43
+
44
+ lm = DSPy.config.lm
45
+ lm_format = lm&.respond_to?(:data_format) ? lm.data_format : nil
46
+ lm_format || @data_format || :json
47
+ end
48
+
36
49
  sig { returns(T.nilable(T.class_of(Signature))) }
37
50
  attr_reader :signature_class
38
51
 
@@ -44,10 +57,11 @@ module DSPy
44
57
  few_shot_examples: T::Array[FewShotExample],
45
58
  signature_class_name: T.nilable(String),
46
59
  schema_format: Symbol,
47
- signature_class: T.nilable(T.class_of(Signature))
60
+ signature_class: T.nilable(T.class_of(Signature)),
61
+ data_format: Symbol
48
62
  ).void
49
63
  end
50
- def initialize(instruction:, input_schema:, output_schema:, few_shot_examples: [], signature_class_name: nil, schema_format: :json, signature_class: nil)
64
+ def initialize(instruction:, input_schema:, output_schema:, few_shot_examples: [], signature_class_name: nil, schema_format: :json, signature_class: nil, data_format: :json)
51
65
  @instruction = instruction
52
66
  @few_shot_examples = few_shot_examples.freeze
53
67
  @input_schema = input_schema.freeze
@@ -55,6 +69,7 @@ module DSPy
55
69
  @signature_class_name = signature_class_name
56
70
  @schema_format = schema_format
57
71
  @signature_class = signature_class
72
+ @data_format = data_format
58
73
  end
59
74
 
60
75
  # Immutable update methods for optimization
@@ -67,7 +82,8 @@ module DSPy
67
82
  few_shot_examples: @few_shot_examples,
68
83
  signature_class_name: @signature_class_name,
69
84
  schema_format: @schema_format,
70
- signature_class: @signature_class
85
+ signature_class: @signature_class,
86
+ data_format: @data_format
71
87
  )
72
88
  end
73
89
 
@@ -80,7 +96,8 @@ module DSPy
80
96
  few_shot_examples: new_examples,
81
97
  signature_class_name: @signature_class_name,
82
98
  schema_format: @schema_format,
83
- signature_class: @signature_class
99
+ signature_class: @signature_class,
100
+ data_format: @data_format
84
101
  )
85
102
  end
86
103
 
@@ -106,7 +123,13 @@ module DSPy
106
123
  sections << "```baml"
107
124
  sections << render_baml_schema(@output_schema, :output)
108
125
  sections << "```"
109
- else # :json (default)
126
+ when :toon
127
+ sections << "Your input schema fields (TOON order) are:"
128
+ sections << Sorbet::Toon::SignatureFormatter.describe_signature(@signature_class, :input)
129
+ sections << ""
130
+ sections << "Your output schema fields (TOON order) are:"
131
+ sections << Sorbet::Toon::SignatureFormatter.describe_signature(@signature_class, :output)
132
+ else
110
133
  sections << "Your input schema fields are:"
111
134
  sections << "```json"
112
135
  sections << JSON.pretty_generate(@input_schema)
@@ -117,11 +140,10 @@ module DSPy
117
140
  sections << JSON.pretty_generate(@output_schema)
118
141
  sections << "```"
119
142
  end
120
-
143
+
121
144
  sections << ""
122
145
  sections << "All interactions will be structured in the following way, with the appropriate values filled in."
123
-
124
- # Add few-shot examples if present
146
+
125
147
  if @few_shot_examples.any?
126
148
  sections << ""
127
149
  sections << "Here are some examples:"
@@ -133,36 +155,81 @@ module DSPy
133
155
  end
134
156
  end
135
157
 
136
- sections << "## Input values"
137
- sections << "```json"
138
- sections << "{input_values}"
139
- sections << "```"
140
-
141
- sections << "## Output values"
142
- sections << "Respond exclusively with the output schema fields in the json block below."
143
- sections << "```json"
144
- sections << "{output_values}"
145
- sections << "```"
146
-
158
+ if toon_data_format_enabled?
159
+ sections << "## TOON data format instructions"
160
+ sections << "All input and output payloads must use Token-Oriented Object Notation (TOON). Do not return JSON, YAML, or prose."
161
+ sections << ""
162
+ sections << "## Input values"
163
+ sections << "Copy the TOON block below and replace the placeholder values with the correct inputs."
164
+ sections << "```toon"
165
+ sections << "{input_values}"
166
+ sections << "```"
167
+
168
+ if (example_input = example_toon_payload(:input))
169
+ sections << ""
170
+ sections << "### Example TOON input"
171
+ sections << "```toon"
172
+ sections << example_input
173
+ sections << "```"
174
+ end
175
+
176
+ sections << ""
177
+ sections << "## Output values"
178
+ sections << "Respond exclusively with a ```toon``` block that lists the output fields in the exact order shown in the schema."
179
+ sections << "```toon"
180
+ sections << "{output_values}"
181
+ sections << "```"
182
+
183
+ if (example_output = example_toon_payload(:output))
184
+ sections << ""
185
+ sections << "### Example TOON output"
186
+ sections << "```toon"
187
+ sections << example_output
188
+ sections << "```"
189
+ end
190
+ else
191
+ sections << "## Input values"
192
+ sections << "```json"
193
+ sections << "{input_values}"
194
+ sections << "```"
195
+
196
+ sections << "## Output values"
197
+ sections << "Respond exclusively with the output schema fields in the json block below."
198
+ sections << "```json"
199
+ sections << "{output_values}"
200
+ sections << "```"
201
+ end
202
+
147
203
  sections << ""
148
204
  sections << "In adhering to this structure, your objective is: #{@instruction}"
149
-
205
+
150
206
  sections.join("\n")
151
207
  end
152
208
 
153
209
  sig { params(input_values: T::Hash[Symbol, T.untyped]).returns(String) }
154
210
  def render_user_prompt(input_values)
155
211
  sections = []
156
-
157
- sections << "## Input Values"
158
- sections << "```json"
159
- sections << JSON.pretty_generate(serialize_for_json(input_values))
160
- sections << "```"
161
-
162
- sections << ""
163
- sections << "Respond with the corresponding output schema fields wrapped in a ```json ``` block,"
164
- sections << "starting with the heading `## Output values`."
165
-
212
+
213
+ if toon_data_format_enabled?
214
+ toon_payload = DSPy::Schema::SorbetToonAdapter.render_input(@signature_class, input_values)
215
+
216
+ sections << "## Input Values"
217
+ sections << "Use the TOON block below as-is; do not convert it to JSON."
218
+ sections << "```toon"
219
+ sections << toon_payload
220
+ sections << "```"
221
+ sections << ""
222
+ sections << "Respond with the corresponding output schema fields encoded as TOON inside a ```toon``` block starting with the heading `## Output values`. Do not include any JSON."
223
+ else
224
+ sections << "## Input Values"
225
+ sections << "```json"
226
+ sections << JSON.pretty_generate(serialize_for_json(input_values))
227
+ sections << "```"
228
+ sections << ""
229
+ sections << "Respond with the corresponding output schema fields wrapped in a ```json ``` block,"
230
+ sections << "starting with the heading `## Output values`."
231
+ end
232
+
166
233
  sections.join("\n")
167
234
  end
168
235
 
@@ -184,7 +251,8 @@ module DSPy
184
251
  input_schema: @input_schema,
185
252
  output_schema: @output_schema,
186
253
  signature_class_name: @signature_class_name,
187
- schema_format: @schema_format
254
+ schema_format: @schema_format,
255
+ data_format: @data_format
188
256
  }
189
257
  end
190
258
 
@@ -198,13 +266,24 @@ module DSPy
198
266
  output_schema: hash[:output_schema] || {},
199
267
  few_shot_examples: examples,
200
268
  signature_class_name: hash[:signature_class_name],
201
- schema_format: hash[:schema_format] || :json
269
+ schema_format: hash[:schema_format] || :json,
270
+ data_format: hash[:data_format] || :json
202
271
  )
203
272
  end
204
273
 
205
274
  # Create prompt from signature class
206
- sig { params(signature_class: T.class_of(Signature), schema_format: Symbol).returns(Prompt) }
207
- def self.from_signature(signature_class, schema_format: :json)
275
+ sig do
276
+ params(
277
+ signature_class: T.class_of(Signature),
278
+ schema_format: T.nilable(Symbol),
279
+ data_format: T.nilable(Symbol)
280
+ ).returns(Prompt)
281
+ end
282
+ def self.from_signature(signature_class, schema_format: nil, data_format: nil)
283
+ lm = DSPy.config.lm
284
+ schema_format ||= lm&.schema_format || :json
285
+ data_format ||= (lm&.respond_to?(:data_format) ? lm.data_format : nil) || :json
286
+
208
287
  new(
209
288
  instruction: signature_class.description || "Complete this task.",
210
289
  input_schema: signature_class.input_json_schema,
@@ -212,7 +291,8 @@ module DSPy
212
291
  few_shot_examples: [],
213
292
  signature_class_name: signature_class.name,
214
293
  schema_format: schema_format,
215
- signature_class: signature_class
294
+ signature_class: signature_class,
295
+ data_format: data_format
216
296
  )
217
297
  end
218
298
 
@@ -335,5 +415,96 @@ module DSPy
335
415
 
336
416
  result
337
417
  end
418
+
419
+ def toon_data_format_enabled?
420
+ data_format == :toon && @signature_class
421
+ end
422
+
423
+ SAMPLE_DEPTH_LIMIT = 3
424
+ private_constant :SAMPLE_DEPTH_LIMIT
425
+
426
+ def example_toon_payload(role)
427
+ return nil unless toon_data_format_enabled?
428
+
429
+ sample_values = case role
430
+ when :input
431
+ sample_struct_values(@signature_class.input_struct_class)
432
+ when :output
433
+ sample_struct_values(@signature_class.output_struct_class)
434
+ else
435
+ {}
436
+ end
437
+
438
+ return nil if sample_values.empty?
439
+
440
+ case role
441
+ when :input
442
+ DSPy::Schema::SorbetToonAdapter.render_input(@signature_class, sample_values)
443
+ when :output
444
+ DSPy::Schema::SorbetToonAdapter.render_expected_output(@signature_class, sample_values)
445
+ end
446
+ rescue StandardError
447
+ nil
448
+ end
449
+
450
+ def sample_struct_values(struct_class, depth = 0)
451
+ return {} unless struct_class&.respond_to?(:props)
452
+ struct_class.props.each_with_object({}) do |(name, prop_info), memo|
453
+ memo[name] = sample_value_for_type(prop_info[:type], name, depth)
454
+ end
455
+ end
456
+
457
+ def sample_value_for_type(prop_type, field_name, depth)
458
+ return sample_string(field_name) if prop_type.nil? || depth > SAMPLE_DEPTH_LIMIT
459
+
460
+ case prop_type
461
+ when T::Types::Simple
462
+ sample_value_for_type(prop_type.raw_type, field_name, depth + 1)
463
+ when T::Types::Union
464
+ preferred = prop_type.types.find { |type| !nil_type?(type) } || prop_type.types.first
465
+ sample_value_for_type(preferred, field_name, depth + 1)
466
+ when T::Types::TypedArray
467
+ [sample_value_for_type(prop_type.type, field_name, depth + 1)]
468
+ when T::Types::TypedHash
469
+ key_sample = sample_value_for_type(prop_type.keys, "#{field_name}_key", depth + 1)
470
+ value_sample = sample_value_for_type(prop_type.values, "#{field_name}_value", depth + 1)
471
+ { key_sample.to_s => value_sample }
472
+ when Class
473
+ sample_for_class_type(prop_type, field_name, depth)
474
+ else
475
+ sample_string(field_name)
476
+ end
477
+ end
478
+
479
+ def sample_for_class_type(prop_type, field_name, depth)
480
+ if prop_type <= String
481
+ sample_string(field_name)
482
+ elsif prop_type <= Integer
483
+ 1
484
+ elsif prop_type <= Float
485
+ 1.0
486
+ elsif prop_type <= Numeric
487
+ 1
488
+ elsif prop_type <= TrueClass || prop_type <= FalseClass
489
+ true
490
+ elsif prop_type <= T::Enum
491
+ enum_value = prop_type.values.first
492
+ enum_value ? enum_value.serialize : sample_string(field_name)
493
+ elsif prop_type <= T::Struct
494
+ sample_struct_values(prop_type, depth + 1)
495
+ else
496
+ sample_string(field_name)
497
+ end
498
+ end
499
+
500
+ def nil_type?(type)
501
+ (type.respond_to?(:raw_type) && type.raw_type == NilClass) || type == NilClass
502
+ end
503
+
504
+ def sample_string(field_name)
505
+ base = field_name.to_s.gsub(/[^a-z0-9]+/i, '_').gsub(/_{2,}/, '_').sub(/^_+|_+$/, '')
506
+ base = 'value' if base.empty?
507
+ "example_#{base}"
508
+ end
338
509
  end
339
- end
510
+ end
data/lib/dspy/re_act.rb CHANGED
@@ -98,13 +98,14 @@ module DSPy
98
98
  @tools = T.let({}, T::Hash[String, T.untyped])
99
99
  tools.each { |tool| @tools[tool.name.downcase] = tool }
100
100
  @max_iterations = max_iterations
101
+ @data_format = T.let(DSPy.config.lm&.data_format || :json, Symbol)
101
102
 
102
103
  # Create dynamic ActionEnum class with tool names + finish
103
104
  @action_enum_class = create_action_enum_class
104
105
 
105
106
  # Create dynamic signature classes that include the original input fields
106
- thought_signature = create_thought_signature(signature_class)
107
- observation_signature = create_observation_signature(signature_class)
107
+ thought_signature = create_thought_signature(signature_class, @data_format)
108
+ observation_signature = create_observation_signature(signature_class, @data_format)
108
109
 
109
110
  # Create thought generator using Predict to preserve field descriptions
110
111
  @thought_generator = T.let(DSPy::Predict.new(thought_signature), DSPy::Predict)
@@ -216,6 +217,28 @@ module DSPy
216
217
  end
217
218
  end
218
219
 
220
+ sig { params(input_struct: T.untyped).returns(T.untyped) }
221
+ def format_input_context(input_struct)
222
+ return input_struct if toon_data_format?
223
+
224
+ DSPy::TypeSerializer.serialize(input_struct).to_json
225
+ end
226
+
227
+ sig { params(history: T::Array[HistoryEntry]).returns(T.untyped) }
228
+ def format_history(history)
229
+ toon_data_format? ? history : serialize_history_for_llm(history)
230
+ end
231
+
232
+ sig { params(observation: T.untyped).returns(T.untyped) }
233
+ def format_observation(observation)
234
+ toon_data_format? ? observation : serialize_for_llm(observation)
235
+ end
236
+
237
+ sig { returns(T::Boolean) }
238
+ def toon_data_format?
239
+ @data_format == :toon
240
+ end
241
+
219
242
  # Creates a dynamic ActionEnum class with tool names and "finish"
220
243
  sig { returns(T.class_of(T::Enum)) }
221
244
  def create_action_enum_class
@@ -241,9 +264,14 @@ module DSPy
241
264
  end
242
265
 
243
266
  # Creates a dynamic Thought signature that includes the original input fields
244
- sig { params(signature_class: T.class_of(DSPy::Signature)).returns(T.class_of(DSPy::Signature)) }
245
- def create_thought_signature(signature_class)
267
+ sig { params(signature_class: T.class_of(DSPy::Signature), data_format: Symbol).returns(T.class_of(DSPy::Signature)) }
268
+ def create_thought_signature(signature_class, data_format)
246
269
  action_enum_class = @action_enum_class
270
+ input_context_type = if data_format == :toon
271
+ signature_class.input_struct_class || String
272
+ else
273
+ String
274
+ end
247
275
  # Create new class that inherits from DSPy::Signature
248
276
  Class.new(DSPy::Signature) do
249
277
  # Set description
@@ -251,8 +279,8 @@ module DSPy
251
279
 
252
280
  # Define input fields
253
281
  input do
254
- const :input_context, String,
255
- description: "Serialized representation of all input fields"
282
+ const :input_context, input_context_type,
283
+ description: data_format == :toon ? "All original input fields with their typed values" : "Serialized representation of all input fields"
256
284
  const :history, T::Array[HistoryEntry],
257
285
  description: "Previous thoughts and actions, including observations from tools."
258
286
  const :available_tools, T::Array[AvailableTool],
@@ -272,8 +300,13 @@ module DSPy
272
300
  end
273
301
 
274
302
  # Creates a dynamic observation signature that includes the original input fields
275
- sig { params(signature_class: T.class_of(DSPy::Signature)).returns(T.class_of(DSPy::Signature)) }
276
- def create_observation_signature(signature_class)
303
+ sig { params(signature_class: T.class_of(DSPy::Signature), data_format: Symbol).returns(T.class_of(DSPy::Signature)) }
304
+ def create_observation_signature(signature_class, data_format)
305
+ input_context_type = if data_format == :toon
306
+ signature_class.input_struct_class || String
307
+ else
308
+ String
309
+ end
277
310
  # Create new class that inherits from DSPy::Signature
278
311
  Class.new(DSPy::Signature) do
279
312
  # Set description
@@ -281,8 +314,8 @@ module DSPy
281
314
 
282
315
  # Define input fields
283
316
  input do
284
- const :input_context, String,
285
- description: "Serialized representation of all input fields"
317
+ const :input_context, input_context_type,
318
+ description: data_format == :toon ? "All original input fields with their typed values" : "Serialized representation of all input fields"
286
319
  const :history, T::Array[HistoryEntry],
287
320
  description: "Previous thoughts, actions, and observations."
288
321
  const :observation, T.untyped,
@@ -358,8 +391,8 @@ module DSPy
358
391
  ) do
359
392
  # Generate thought and action
360
393
  thought_obj = @thought_generator.forward(
361
- input_context: DSPy::TypeSerializer.serialize(input_struct).to_json,
362
- history: serialize_history_for_llm(history),
394
+ input_context: format_input_context(input_struct),
395
+ history: format_history(history),
363
396
  available_tools: available_tools_desc
364
397
  )
365
398
 
@@ -617,9 +650,9 @@ module DSPy
617
650
  sig { params(input_struct: T.untyped, history: T::Array[HistoryEntry], observation: T.untyped, available_tools_desc: T::Array[AvailableTool], iteration: Integer).returns(T::Hash[Symbol, T.untyped]) }
618
651
  def process_observation_and_decide_next_step(input_struct, history, observation, available_tools_desc, iteration)
619
652
  observation_result = @observation_processor.forward(
620
- input_context: DSPy::TypeSerializer.serialize(input_struct).to_json,
621
- history: serialize_history_for_llm(history),
622
- observation: serialize_for_llm(observation)
653
+ input_context: format_input_context(input_struct),
654
+ history: format_history(history),
655
+ observation: format_observation(observation)
623
656
  )
624
657
 
625
658
  return { should_finish: false } unless observation_result.next_step == NextStep::Finish
@@ -634,8 +667,8 @@ module DSPy
634
667
  sig { params(input_struct: T.untyped, history: T::Array[HistoryEntry], available_tools_desc: T::Array[AvailableTool], observation_result: T.untyped, iteration: Integer).returns(T.untyped) }
635
668
  def generate_forced_final_answer(input_struct, history, available_tools_desc, observation_result, iteration)
636
669
  final_thought = @thought_generator.forward(
637
- input_context: DSPy::TypeSerializer.serialize(input_struct).to_json,
638
- history: serialize_history_for_llm(history),
670
+ input_context: format_input_context(input_struct),
671
+ history: format_history(history),
639
672
  available_tools: available_tools_desc
640
673
  )
641
674
 
@@ -254,10 +254,13 @@ module DSPy
254
254
  "DSPy uses _type for automatic type detection in union types."
255
255
  end
256
256
 
257
+ struct_name = struct_class.name || "Struct#{format('%x', struct_class.object_id)}"
258
+ simple_name = struct_name.split('::').last || struct_name
259
+
257
260
  # Add automatic _type field for type detection
258
261
  properties[:_type] = {
259
262
  type: "string",
260
- const: struct_class.name.split('::').last # Use the simple class name
263
+ const: simple_name # Use the simple class name
261
264
  }
262
265
  required << "_type"
263
266
 
@@ -280,7 +283,7 @@ module DSPy
280
283
  type: "object",
281
284
  properties: properties,
282
285
  required: required,
283
- description: "#{struct_class.name} struct"
286
+ description: "#{struct_name} struct"
284
287
  }
285
288
  end
286
289
 
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sorbet-runtime'
4
+ require 'sorbet/toon'
5
+
6
+ require_relative '../lm/errors'
7
+
8
+ module DSPy
9
+ module Schema
10
+ module SorbetToonAdapter
11
+ extend T::Sig
12
+
13
+ module_function
14
+
15
+ sig { params(signature_class: T.nilable(T.class_of(DSPy::Signature)), values: T::Hash[Symbol, T.untyped]).returns(String) }
16
+ def render_input(signature_class, values)
17
+ Sorbet::Toon.encode(
18
+ values,
19
+ signature: signature_class,
20
+ role: :input
21
+ )
22
+ end
23
+
24
+ sig { params(signature_class: T.nilable(T.class_of(DSPy::Signature)), values: T::Hash[Symbol, T.untyped]).returns(String) }
25
+ def render_expected_output(signature_class, values)
26
+ Sorbet::Toon.encode(
27
+ values,
28
+ signature: signature_class,
29
+ role: :output
30
+ )
31
+ end
32
+
33
+ sig { params(signature_class: T.nilable(T.class_of(DSPy::Signature)), toon_string: String).returns(T.untyped) }
34
+ def parse_output(signature_class, toon_string)
35
+ payload = strip_code_fences(toon_string)
36
+
37
+ Sorbet::Toon.decode(
38
+ payload,
39
+ signature: signature_class,
40
+ role: :output,
41
+ strict: false
42
+ )
43
+ rescue Sorbet::Toon::DecodeError => e
44
+ log_decode_error(payload, e)
45
+ raise DSPy::LM::AdapterError,
46
+ "Failed to parse TOON response: #{e.message}. Ensure the model replies with a ```toon``` block using the schema described in the system prompt."
47
+ end
48
+
49
+ sig { params(text: T.nilable(String)).returns(String) }
50
+ def strip_code_fences(text)
51
+ return '' if text.nil?
52
+
53
+ match = text.match(/```(?:toon)?\s*(.*?)```/m)
54
+ return match[1].strip if match
55
+
56
+ text.strip
57
+ end
58
+
59
+ sig { params(payload: String, error: StandardError).void }
60
+ def log_decode_error(payload, error)
61
+ logger = DSPy.logger if DSPy.respond_to?(:logger)
62
+ return unless logger.respond_to?(:warn)
63
+
64
+ preview = payload.to_s.lines.first(5).join
65
+ logger.warn(
66
+ event: 'toon.decode_error',
67
+ error: error.message,
68
+ preview: preview,
69
+ length: payload.to_s.length
70
+ )
71
+ end
72
+
73
+ sig { params(signature_class: T.nilable(T.class_of(DSPy::Signature)), role: Symbol).returns(String) }
74
+ def field_guidance(signature_class, role)
75
+ return '' unless signature_class
76
+
77
+ Sorbet::Toon::SignatureFormatter.describe_signature(signature_class, role)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -17,10 +17,11 @@ module DSPy
17
17
  few_shot_examples: T::Array[T.untyped],
18
18
  signature_class_name: T.nilable(String),
19
19
  schema_format: Symbol,
20
- signature_class: T.nilable(T.class_of(Signature))
20
+ signature_class: T.nilable(T.class_of(Signature)),
21
+ data_format: Symbol
21
22
  ).void
22
23
  end
23
- def initialize(instruction:, input_schema:, output_schema:, few_shot_examples: [], signature_class_name: nil, schema_format: :json, signature_class: nil)
24
+ def initialize(instruction:, input_schema:, output_schema:, few_shot_examples: [], signature_class_name: nil, schema_format: :json, signature_class: nil, data_format: :json)
24
25
  normalized_examples = few_shot_examples.map do |example|
25
26
  case example
26
27
  when FewShotExample
@@ -39,7 +40,8 @@ module DSPy
39
40
  few_shot_examples: normalized_examples,
40
41
  signature_class_name: signature_class_name,
41
42
  schema_format: schema_format,
42
- signature_class: signature_class
43
+ signature_class: signature_class,
44
+ data_format: data_format
43
45
  )
44
46
  end
45
47
 
@@ -17,6 +17,7 @@ module DSPy
17
17
  when Hash
18
18
  value.transform_values { |v| serialize(v) }
19
19
  else
20
+ return serialize(value.serialize) if value.respond_to?(:serialize)
20
21
  value
21
22
  end
22
23
  end
@@ -52,4 +53,4 @@ module DSPy
52
53
  result
53
54
  end
54
55
  end
55
- end
56
+ end
data/lib/dspy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DSPy
4
- VERSION = "0.30.1"
4
+ VERSION = "0.31.1"
5
5
  end