dspy 0.31.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 972e09f00d8d5417d5c1af255eb01503fc33ab370264345b4c7de880b4f99fda
4
- data.tar.gz: 21f6f7952a9caaf8398a24a69516147bf68c88e510b88aa2f45239786bbfd31b
3
+ metadata.gz: 584b43a98aa27dcc4bcdb83c64f48b4c3d0f4b67453abfe9a0942cbc30b14e7d
4
+ data.tar.gz: ab2b58be33ef10e36d1825c371e29783a7a3cea72bfb34b765b9aeab5fb4644f
5
5
  SHA512:
6
- metadata.gz: 780f786797df9d50950c1526296c8bc7a0db87dab29078e14de2d32a0ec608ae30585963a4dda3a5261b60530b3dd59e0b9e5915eab6c8d60360c4d1b6e1d8af
7
- data.tar.gz: 3fb5d69bb58d9b57905a6f9985ecdd5d792be0aa91fb5e980f437f5d8887144564697d1151ed9bb8ec742b4ae4c1efff5303b53d937603ac7719959ffb11cfd9
6
+ metadata.gz: 6671b0cca0709aa9dea2eaf46abbbc2366d0779a89139377b6840279afa767e659bf635e8314a0a3ca45bc3f476200b7d430f74e83b0af08a085ae221a97c7c5
7
+ data.tar.gz: 1f8aaba8e3e2f16e1bd0eb81f9694b677f20cbeb7d0dadbacc9571613076ab29bc44991c79f3c5908e821397b2e0616e04801704f4ced5e0e5a8e9eed4f84f4f
@@ -36,6 +36,8 @@ module DSPy
36
36
  coerce_array_value(value, prop_type)
37
37
  when ->(type) { hash_type?(type) }
38
38
  coerce_hash_value(value, prop_type)
39
+ when ->(type) { type == String || simple_type_match?(type, String) }
40
+ value.to_s
39
41
  when ->(type) { enum_type?(type) }
40
42
  coerce_enum_value(value, prop_type)
41
43
  when ->(type) { type == Float || simple_type_match?(type, Float) }
data/lib/dspy/prompt.rb CHANGED
@@ -155,17 +155,38 @@ module DSPy
155
155
  end
156
156
  end
157
157
 
158
- if data_format == :toon && @signature_class
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 << ""
159
162
  sections << "## Input values"
163
+ sections << "Copy the TOON block below and replace the placeholder values with the correct inputs."
160
164
  sections << "```toon"
161
165
  sections << "{input_values}"
162
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
+
163
176
  sections << ""
164
177
  sections << "## Output values"
165
- sections << "Respond exclusively with a ```toon``` block containing only the output fields defined above, in the same order."
178
+ sections << "Respond exclusively with a ```toon``` block that lists the output fields in the exact order shown in the schema."
166
179
  sections << "```toon"
167
180
  sections << "{output_values}"
168
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
169
190
  else
170
191
  sections << "## Input values"
171
192
  sections << "```json"
@@ -189,15 +210,16 @@ module DSPy
189
210
  def render_user_prompt(input_values)
190
211
  sections = []
191
212
 
192
- if data_format == :toon && @signature_class
213
+ if toon_data_format_enabled?
193
214
  toon_payload = DSPy::Schema::SorbetToonAdapter.render_input(@signature_class, input_values)
194
215
 
195
216
  sections << "## Input Values"
217
+ sections << "Use the TOON block below as-is; do not convert it to JSON."
196
218
  sections << "```toon"
197
219
  sections << toon_payload
198
220
  sections << "```"
199
221
  sections << ""
200
- sections << "Respond with the corresponding output schema fields encoded as TOON inside a ```toon``` block starting with the heading `## Output values`."
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."
201
223
  else
202
224
  sections << "## Input Values"
203
225
  sections << "```json"
@@ -393,5 +415,96 @@ module DSPy
393
415
 
394
416
  result
395
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
396
509
  end
397
510
  end
@@ -37,7 +37,8 @@ module DSPy
37
37
  Sorbet::Toon.decode(
38
38
  payload,
39
39
  signature: signature_class,
40
- role: :output
40
+ role: :output,
41
+ strict: false
41
42
  )
42
43
  rescue Sorbet::Toon::DecodeError => e
43
44
  log_decode_error(payload, e)
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.31.0"
4
+ VERSION = "0.31.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dspy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0
4
+ version: 0.31.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vicente Reig Rincón de Arellano