dspy 0.2.0 → 0.3.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 +4 -4
- data/README.md +328 -249
- data/lib/dspy/chain_of_thought.rb +151 -11
- data/lib/dspy/instrumentation/token_tracker.rb +54 -0
- data/lib/dspy/instrumentation.rb +113 -0
- data/lib/dspy/lm/adapter.rb +41 -0
- data/lib/dspy/lm/adapter_factory.rb +59 -0
- data/lib/dspy/lm/adapters/anthropic_adapter.rb +96 -0
- data/lib/dspy/lm/adapters/openai_adapter.rb +53 -0
- data/lib/dspy/lm/adapters/ruby_llm_adapter.rb +81 -0
- data/lib/dspy/lm/errors.rb +10 -0
- data/lib/dspy/lm/response.rb +28 -0
- data/lib/dspy/lm.rb +92 -40
- data/lib/dspy/module.rb +51 -6
- data/lib/dspy/predict.rb +135 -15
- data/lib/dspy/re_act.rb +366 -191
- data/lib/dspy/schema_adapters.rb +55 -0
- data/lib/dspy/signature.rb +282 -10
- data/lib/dspy/subscribers/logger_subscriber.rb +215 -0
- data/lib/dspy/tools/{sorbet_tool.rb → base.rb} +33 -33
- data/lib/dspy/tools.rb +1 -1
- data/lib/dspy.rb +4 -9
- metadata +60 -28
- data/lib/dspy/ext/dry_schema.rb +0 -94
- data/lib/dspy/sorbet_chain_of_thought.rb +0 -91
- data/lib/dspy/sorbet_module.rb +0 -47
- data/lib/dspy/sorbet_predict.rb +0 -180
- data/lib/dspy/sorbet_re_act.rb +0 -332
- data/lib/dspy/sorbet_signature.rb +0 -218
- data/lib/dspy/types.rb +0 -3
@@ -6,70 +6,70 @@ require 'json'
|
|
6
6
|
module DSPy
|
7
7
|
module Tools
|
8
8
|
# Base class for all Sorbet-based tools with DSL support
|
9
|
-
class
|
9
|
+
class Base
|
10
10
|
extend T::Sig
|
11
11
|
extend T::Helpers
|
12
|
-
|
12
|
+
|
13
13
|
class << self
|
14
14
|
extend T::Sig
|
15
|
-
|
15
|
+
|
16
16
|
sig { returns(T.nilable(String)) }
|
17
17
|
attr_reader :tool_name_value, :tool_description_value
|
18
|
-
|
18
|
+
|
19
19
|
# DSL method to set tool name
|
20
20
|
sig { params(name: String).void }
|
21
21
|
def tool_name(name)
|
22
22
|
@tool_name_value = name
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
# DSL method to set tool description
|
26
26
|
sig { params(description: String).void }
|
27
27
|
def tool_description(description)
|
28
28
|
@tool_description_value = description
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# Get the JSON schema for the call method based on its Sorbet signature
|
32
32
|
sig { returns(T::Hash[Symbol, T.untyped]) }
|
33
33
|
def call_schema
|
34
34
|
method_obj = instance_method(:call)
|
35
35
|
sig_info = T::Utils.signature_for_method(method_obj)
|
36
|
-
|
36
|
+
|
37
37
|
if sig_info.nil?
|
38
38
|
# Fallback for methods without signatures
|
39
|
-
return {
|
40
|
-
type: :object,
|
41
|
-
properties: {},
|
42
|
-
required: []
|
39
|
+
return {
|
40
|
+
type: :object,
|
41
|
+
properties: {},
|
42
|
+
required: []
|
43
43
|
}
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
properties = {}
|
47
47
|
required = []
|
48
|
-
|
48
|
+
|
49
49
|
# Handle positional arguments
|
50
50
|
sig_info.arg_types.each do |param_name, param_type|
|
51
51
|
next if param_name == :block # Skip block parameters
|
52
|
-
|
52
|
+
|
53
53
|
properties[param_name] = {
|
54
54
|
type: sorbet_type_to_json_schema(param_type)[:type],
|
55
55
|
description: "Parameter #{param_name}"
|
56
56
|
}
|
57
|
-
|
57
|
+
|
58
58
|
# Check if parameter is required (not nilable)
|
59
59
|
unless param_type.class.name.include?('Union') && param_type.name.include?('NilClass')
|
60
60
|
required << param_name.to_s
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
# Handle keyword arguments (more common in Ruby)
|
65
65
|
sig_info.kwarg_types.each do |param_name, param_type|
|
66
66
|
next if param_name == :block # Skip block parameters
|
67
|
-
|
67
|
+
|
68
68
|
properties[param_name] = {
|
69
69
|
type: sorbet_type_to_json_schema(param_type)[:type],
|
70
70
|
description: "Parameter #{param_name}"
|
71
71
|
}
|
72
|
-
|
72
|
+
|
73
73
|
# Check if parameter is required by looking at required kwarg names
|
74
74
|
if sig_info.req_kwarg_names.include?(param_name)
|
75
75
|
required << param_name.to_s
|
@@ -77,22 +77,22 @@ module DSPy
|
|
77
77
|
properties[param_name][:description] += " (optional)"
|
78
78
|
end
|
79
79
|
end
|
80
|
-
|
80
|
+
|
81
81
|
{
|
82
82
|
type: :object,
|
83
83
|
properties: properties,
|
84
84
|
required: required
|
85
85
|
}
|
86
86
|
end
|
87
|
-
|
87
|
+
|
88
88
|
private
|
89
|
-
|
89
|
+
|
90
90
|
# Convert Sorbet types to JSON Schema types
|
91
91
|
sig { params(sorbet_type: T.untyped).returns(T::Hash[Symbol, T.untyped]) }
|
92
92
|
def sorbet_type_to_json_schema(sorbet_type)
|
93
93
|
if sorbet_type.is_a?(T::Types::Simple)
|
94
94
|
raw_type = sorbet_type.raw_type
|
95
|
-
|
95
|
+
|
96
96
|
if raw_type == String
|
97
97
|
{ type: :string }
|
98
98
|
elsif raw_type == Integer
|
@@ -128,18 +128,18 @@ module DSPy
|
|
128
128
|
end
|
129
129
|
end
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
# Instance methods that tools can use
|
133
133
|
sig { returns(String) }
|
134
134
|
def name
|
135
135
|
self.class.tool_name_value || self.class.name&.split('::')&.last&.downcase || 'unknown_tool'
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
sig { returns(String) }
|
139
139
|
def description
|
140
140
|
self.class.tool_description_value || "Tool: #{name}"
|
141
141
|
end
|
142
|
-
|
142
|
+
|
143
143
|
# Get the JSON schema string for the tool, formatted for LLM consumption
|
144
144
|
sig { returns(String) }
|
145
145
|
def schema
|
@@ -149,15 +149,15 @@ module DSPy
|
|
149
149
|
description: description,
|
150
150
|
parameters: schema_obj
|
151
151
|
}
|
152
|
-
JSON.
|
152
|
+
JSON.generate(tool_info)
|
153
153
|
end
|
154
|
-
|
154
|
+
|
155
155
|
# Dynamic call method for ReAct agent - parses JSON arguments and calls the typed method
|
156
156
|
sig { params(args_json: T.untyped).returns(T.untyped) }
|
157
157
|
def dynamic_call(args_json)
|
158
158
|
# Parse arguments based on the call schema
|
159
159
|
schema = self.class.call_schema
|
160
|
-
|
160
|
+
|
161
161
|
if schema[:properties].empty?
|
162
162
|
# No parameters - call without arguments
|
163
163
|
call
|
@@ -175,7 +175,7 @@ module DSPy
|
|
175
175
|
else
|
176
176
|
return "Error: Expected Hash or JSON string"
|
177
177
|
end
|
178
|
-
|
178
|
+
|
179
179
|
# Convert string keys to symbols and validate types
|
180
180
|
kwargs = {}
|
181
181
|
schema[:properties].each do |param_name, param_schema|
|
@@ -186,17 +186,17 @@ module DSPy
|
|
186
186
|
return "Error: Missing required parameter: #{key}"
|
187
187
|
end
|
188
188
|
end
|
189
|
-
|
189
|
+
|
190
190
|
call(**kwargs)
|
191
191
|
end
|
192
192
|
rescue => e
|
193
193
|
"Error: #{e.message}"
|
194
194
|
end
|
195
|
-
|
195
|
+
|
196
196
|
# Subclasses must implement their own call method with their own signature
|
197
|
-
|
197
|
+
|
198
198
|
private
|
199
|
-
|
199
|
+
|
200
200
|
# Convert argument to the expected type based on JSON schema
|
201
201
|
sig { params(value: T.untyped, schema: T::Hash[Symbol, T.untyped]).returns(T.untyped) }
|
202
202
|
def convert_argument_type(value, schema)
|
data/lib/dspy/tools.rb
CHANGED
data/lib/dspy.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require '
|
3
|
-
require 'dry-schema'
|
2
|
+
require 'sorbet-runtime'
|
4
3
|
require 'dry-configurable'
|
5
4
|
require 'dry/logger'
|
6
|
-
require_relative 'dspy/ext/dry_schema'
|
7
5
|
|
8
6
|
module DSPy
|
9
7
|
extend Dry::Configurable
|
@@ -15,7 +13,6 @@ module DSPy
|
|
15
13
|
end
|
16
14
|
end
|
17
15
|
|
18
|
-
require_relative 'dspy/types'
|
19
16
|
require_relative 'dspy/module'
|
20
17
|
require_relative 'dspy/field'
|
21
18
|
require_relative 'dspy/signature'
|
@@ -23,10 +20,8 @@ require_relative 'dspy/lm'
|
|
23
20
|
require_relative 'dspy/predict'
|
24
21
|
require_relative 'dspy/chain_of_thought'
|
25
22
|
require_relative 'dspy/re_act'
|
23
|
+
require_relative 'dspy/subscribers/logger_subscriber'
|
26
24
|
require_relative 'dspy/tools'
|
25
|
+
require_relative 'dspy/instrumentation'
|
27
26
|
|
28
|
-
#
|
29
|
-
require_relative 'dspy/sorbet_signature'
|
30
|
-
require_relative 'dspy/sorbet_module'
|
31
|
-
require_relative 'dspy/sorbet_predict'
|
32
|
-
require_relative 'dspy/sorbet_chain_of_thought'
|
27
|
+
# LoggerSubscriber will be lazy-initialized when first accessed
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dspy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vicente Reig Rincón de Arellano
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-06-
|
10
|
+
date: 2025-06-27 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
|
-
name:
|
13
|
+
name: dry-configurable
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
15
15
|
requirements:
|
16
16
|
- - "~>"
|
@@ -24,21 +24,21 @@ dependencies:
|
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '1.0'
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
|
-
name: dry-
|
27
|
+
name: dry-logger
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
30
|
- - "~>"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '1.
|
32
|
+
version: '1.0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '1.
|
39
|
+
version: '1.0'
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
|
-
name: dry-
|
41
|
+
name: dry-monitor
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
@@ -52,61 +52,89 @@ dependencies:
|
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '1.0'
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
|
-
name:
|
55
|
+
name: async
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - "~>"
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: '
|
60
|
+
version: '2.23'
|
61
61
|
type: :runtime
|
62
62
|
prerelease: false
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
67
|
+
version: '2.23'
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
|
-
name:
|
69
|
+
name: openai
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
74
|
+
version: 0.9.0
|
75
75
|
type: :runtime
|
76
76
|
prerelease: false
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version:
|
81
|
+
version: 0.9.0
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
|
-
name:
|
83
|
+
name: anthropic
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
88
|
+
version: 1.1.0
|
89
89
|
type: :runtime
|
90
90
|
prerelease: false
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
93
|
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version:
|
95
|
+
version: 1.1.0
|
96
96
|
- !ruby/object:Gem::Dependency
|
97
|
-
name:
|
97
|
+
name: ruby_llm
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.0'
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '1.0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: sorbet-runtime
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.5'
|
117
|
+
type: :runtime
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0.5'
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: sorbet-schema
|
98
126
|
requirement: !ruby/object:Gem::Requirement
|
99
127
|
requirements:
|
100
128
|
- - "~>"
|
101
129
|
- !ruby/object:Gem::Version
|
102
|
-
version: 0.
|
130
|
+
version: '0.3'
|
103
131
|
type: :runtime
|
104
132
|
prerelease: false
|
105
133
|
version_requirements: !ruby/object:Gem::Requirement
|
106
134
|
requirements:
|
107
135
|
- - "~>"
|
108
136
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0.
|
137
|
+
version: '0.3'
|
110
138
|
description: A Ruby implementation of DSPy, a framework for programming with large
|
111
139
|
language models
|
112
140
|
email:
|
@@ -118,21 +146,25 @@ files:
|
|
118
146
|
- README.md
|
119
147
|
- lib/dspy.rb
|
120
148
|
- lib/dspy/chain_of_thought.rb
|
121
|
-
- lib/dspy/ext/dry_schema.rb
|
122
149
|
- lib/dspy/field.rb
|
150
|
+
- lib/dspy/instrumentation.rb
|
151
|
+
- lib/dspy/instrumentation/token_tracker.rb
|
123
152
|
- lib/dspy/lm.rb
|
153
|
+
- lib/dspy/lm/adapter.rb
|
154
|
+
- lib/dspy/lm/adapter_factory.rb
|
155
|
+
- lib/dspy/lm/adapters/anthropic_adapter.rb
|
156
|
+
- lib/dspy/lm/adapters/openai_adapter.rb
|
157
|
+
- lib/dspy/lm/adapters/ruby_llm_adapter.rb
|
158
|
+
- lib/dspy/lm/errors.rb
|
159
|
+
- lib/dspy/lm/response.rb
|
124
160
|
- lib/dspy/module.rb
|
125
161
|
- lib/dspy/predict.rb
|
126
162
|
- lib/dspy/re_act.rb
|
163
|
+
- lib/dspy/schema_adapters.rb
|
127
164
|
- lib/dspy/signature.rb
|
128
|
-
- lib/dspy/
|
129
|
-
- lib/dspy/sorbet_module.rb
|
130
|
-
- lib/dspy/sorbet_predict.rb
|
131
|
-
- lib/dspy/sorbet_re_act.rb
|
132
|
-
- lib/dspy/sorbet_signature.rb
|
165
|
+
- lib/dspy/subscribers/logger_subscriber.rb
|
133
166
|
- lib/dspy/tools.rb
|
134
|
-
- lib/dspy/tools/
|
135
|
-
- lib/dspy/types.rb
|
167
|
+
- lib/dspy/tools/base.rb
|
136
168
|
homepage: https://github.com/vicentereig/dspy.rb
|
137
169
|
licenses:
|
138
170
|
- MIT
|
@@ -144,7 +176,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
176
|
requirements:
|
145
177
|
- - ">="
|
146
178
|
- !ruby/object:Gem::Version
|
147
|
-
version:
|
179
|
+
version: 3.3.0
|
148
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
181
|
requirements:
|
150
182
|
- - ">="
|
data/lib/dspy/ext/dry_schema.rb
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'dry/schema/version'
|
2
|
-
|
3
|
-
if Dry::Schema::VERSION > Gem::Version.new('1.15')
|
4
|
-
raise 'Double check this monkey path before upgrading drys-schema.'
|
5
|
-
end
|
6
|
-
|
7
|
-
Dry::Schema.load_extensions(:json_schema)
|
8
|
-
# Monkey patch Macros::Core to add meta method
|
9
|
-
module Dry
|
10
|
-
module Schema
|
11
|
-
module Macros
|
12
|
-
class Core
|
13
|
-
def meta(metadata)
|
14
|
-
schema_dsl.meta(name, metadata)
|
15
|
-
self
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Monkey patch DSL to store metadata
|
23
|
-
module Dry
|
24
|
-
module Schema
|
25
|
-
class DSL
|
26
|
-
def meta(name, metadata)
|
27
|
-
@metas ||= {}
|
28
|
-
@metas[name] = metadata
|
29
|
-
self
|
30
|
-
end
|
31
|
-
|
32
|
-
def metas
|
33
|
-
@metas ||= {}
|
34
|
-
end
|
35
|
-
|
36
|
-
# Ensure metas are included in new instances
|
37
|
-
alias_method :original_new, :new
|
38
|
-
def new(**options, &block)
|
39
|
-
options[:metas] = metas
|
40
|
-
original_new(**options, &block)
|
41
|
-
end
|
42
|
-
|
43
|
-
# Ensure processor has access to metas
|
44
|
-
alias_method :original_call, :call
|
45
|
-
def call
|
46
|
-
processor = original_call
|
47
|
-
processor.instance_variable_set(:@schema_metas, metas)
|
48
|
-
processor
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Monkey patch Processor to expose schema_metas
|
55
|
-
module Dry
|
56
|
-
module Schema
|
57
|
-
class Processor
|
58
|
-
attr_reader :schema_metas
|
59
|
-
|
60
|
-
# Add schema_metas accessor
|
61
|
-
def schema_metas
|
62
|
-
@schema_metas ||= {}
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# Directly monkey patch the JSON Schema generation
|
69
|
-
module Dry
|
70
|
-
module Schema
|
71
|
-
module JSONSchema
|
72
|
-
module SchemaMethods
|
73
|
-
# Override the original json_schema method
|
74
|
-
def json_schema(loose: false)
|
75
|
-
compiler = SchemaCompiler.new(root: true, loose: loose)
|
76
|
-
compiler.call(to_ast)
|
77
|
-
result = compiler.to_hash
|
78
|
-
|
79
|
-
# Add descriptions to properties from schema_metas
|
80
|
-
if respond_to?(:schema_metas) && !schema_metas.empty?
|
81
|
-
schema_metas.each do |key, meta|
|
82
|
-
if meta[:description] && result[:properties][key]
|
83
|
-
result[:properties][key][:description] = meta[:description]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
result
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# typed: strict
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'sorbet-runtime'
|
5
|
-
require_relative 'sorbet_predict'
|
6
|
-
require_relative 'sorbet_signature'
|
7
|
-
|
8
|
-
module DSPy
|
9
|
-
# Enhances prediction by encouraging step-by-step reasoning
|
10
|
-
# before providing a final answer using Sorbet signatures.
|
11
|
-
class SorbetChainOfThought < SorbetPredict
|
12
|
-
extend T::Sig
|
13
|
-
|
14
|
-
FieldDescriptor = DSPy::SorbetSignature::FieldDescriptor
|
15
|
-
|
16
|
-
sig { params(signature_class: T.class_of(DSPy::SorbetSignature)).void }
|
17
|
-
def initialize(signature_class)
|
18
|
-
@original_signature = signature_class
|
19
|
-
|
20
|
-
# Create enhanced output struct with reasoning
|
21
|
-
enhanced_output_struct = create_enhanced_output_struct(signature_class)
|
22
|
-
|
23
|
-
# Create enhanced signature class
|
24
|
-
enhanced_signature = Class.new(DSPy::SorbetSignature) do
|
25
|
-
# Set the description
|
26
|
-
description "#{signature_class.description} Think step by step."
|
27
|
-
|
28
|
-
# Use the same input struct and copy field descriptors
|
29
|
-
@input_struct_class = signature_class.input_struct_class
|
30
|
-
@input_field_descriptors = signature_class.instance_variable_get(:@input_field_descriptors) || {}
|
31
|
-
|
32
|
-
# Use the enhanced output struct and create field descriptors for it
|
33
|
-
@output_struct_class = enhanced_output_struct
|
34
|
-
|
35
|
-
# Create field descriptors for the enhanced output struct
|
36
|
-
@output_field_descriptors = {}
|
37
|
-
|
38
|
-
# Copy original output field descriptors
|
39
|
-
original_output_descriptors = signature_class.instance_variable_get(:@output_field_descriptors) || {}
|
40
|
-
@output_field_descriptors.merge!(original_output_descriptors)
|
41
|
-
|
42
|
-
# Add reasoning field descriptor
|
43
|
-
@output_field_descriptors[:reasoning] = FieldDescriptor.new(String, nil)
|
44
|
-
|
45
|
-
class << self
|
46
|
-
attr_reader :input_struct_class, :output_struct_class
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
# Call parent constructor with enhanced signature
|
51
|
-
super(enhanced_signature)
|
52
|
-
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
sig { params(signature_class: T.class_of(DSPy::SorbetSignature)).returns(T.class_of(T::Struct)) }
|
57
|
-
def create_enhanced_output_struct(signature_class)
|
58
|
-
# Get original output props
|
59
|
-
original_props = signature_class.output_struct_class.props
|
60
|
-
|
61
|
-
# Create new struct class with reasoning added
|
62
|
-
Class.new(T::Struct) do
|
63
|
-
# Add all original fields
|
64
|
-
original_props.each do |name, prop|
|
65
|
-
# Extract the type and other options
|
66
|
-
type = prop[:type]
|
67
|
-
options = prop.except(:type, :type_object, :accessor_key, :sensitivity, :redaction)
|
68
|
-
|
69
|
-
# Handle default values
|
70
|
-
if options[:default]
|
71
|
-
const name, type, default: options[:default]
|
72
|
-
elsif options[:factory]
|
73
|
-
const name, type, factory: options[:factory]
|
74
|
-
else
|
75
|
-
const name, type
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Add reasoning field
|
80
|
-
const :reasoning, String
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
sig { override.returns(T::Hash[Symbol, T.untyped]) }
|
85
|
-
def generate_example_output
|
86
|
-
example = super
|
87
|
-
example[:reasoning] = "Let me think through this step by step..."
|
88
|
-
example
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
data/lib/dspy/sorbet_module.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'sorbet-runtime'
|
4
|
-
|
5
|
-
module DSPy
|
6
|
-
class SorbetModule
|
7
|
-
extend T::Sig
|
8
|
-
extend T::Generic
|
9
|
-
|
10
|
-
# The main forward method that users will call is generic and type parameterized
|
11
|
-
sig do
|
12
|
-
type_parameters(:I, :O)
|
13
|
-
.params(
|
14
|
-
input_values: T.type_parameter(:I)
|
15
|
-
)
|
16
|
-
.returns(T.type_parameter(:O))
|
17
|
-
end
|
18
|
-
def forward(**input_values)
|
19
|
-
# Cast the result of forward_untyped to the expected output type
|
20
|
-
T.cast(forward_untyped(**input_values), T.type_parameter(:O))
|
21
|
-
end
|
22
|
-
|
23
|
-
# The implementation method that subclasses must override
|
24
|
-
sig { params(input_values: T.untyped).returns(T.untyped) }
|
25
|
-
def forward_untyped(**input_values)
|
26
|
-
raise NotImplementedError, "Subclasses must implement forward_untyped method"
|
27
|
-
end
|
28
|
-
|
29
|
-
# The main call method that users will call is generic and type parameterized
|
30
|
-
sig do
|
31
|
-
type_parameters(:I, :O)
|
32
|
-
.params(
|
33
|
-
input_values: T.type_parameter(:I)
|
34
|
-
)
|
35
|
-
.returns(T.type_parameter(:O))
|
36
|
-
end
|
37
|
-
def call(**input_values)
|
38
|
-
forward(**input_values)
|
39
|
-
end
|
40
|
-
|
41
|
-
# The implementation method for call
|
42
|
-
sig { params(input_values: T.untyped).returns(T.untyped) }
|
43
|
-
def call_untyped(**input_values)
|
44
|
-
forward_untyped(**input_values)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|