dspy 0.5.1 → 0.6.0
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 +1 -0
- data/lib/dspy/code_act.rb +463 -0
- data/lib/dspy/instrumentation.rb +15 -0
- data/lib/dspy/lm/adapters/anthropic_adapter.rb +106 -0
- data/lib/dspy/lm.rb +21 -7
- data/lib/dspy/memory/embedding_engine.rb +68 -0
- data/lib/dspy/memory/in_memory_store.rb +216 -0
- data/lib/dspy/memory/local_embedding_engine.rb +241 -0
- data/lib/dspy/memory/memory_compactor.rb +299 -0
- data/lib/dspy/memory/memory_manager.rb +248 -0
- data/lib/dspy/memory/memory_record.rb +163 -0
- data/lib/dspy/memory/memory_store.rb +90 -0
- data/lib/dspy/memory.rb +30 -0
- data/lib/dspy/mixins/instrumentation_helpers.rb +3 -5
- data/lib/dspy/mixins/type_coercion.rb +3 -0
- data/lib/dspy/prompt.rb +48 -1
- data/lib/dspy/subscribers/logger_subscriber.rb +91 -1
- data/lib/dspy/tools/base.rb +1 -1
- data/lib/dspy/tools/memory_toolset.rb +117 -0
- data/lib/dspy/tools/text_processing_toolset.rb +186 -0
- data/lib/dspy/tools/toolset.rb +223 -0
- data/lib/dspy/tools.rb +1 -0
- data/lib/dspy/version.rb +1 -1
- data/lib/dspy.rb +2 -0
- metadata +28 -2
@@ -0,0 +1,223 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module DSPy
|
7
|
+
module Tools
|
8
|
+
# Base class for multi-method tool classes where each method can be exposed as an individual tool
|
9
|
+
# Similar to Rails controllers where each action is exposed as an endpoint
|
10
|
+
class Toolset
|
11
|
+
extend T::Sig
|
12
|
+
extend T::Helpers
|
13
|
+
|
14
|
+
class << self
|
15
|
+
extend T::Sig
|
16
|
+
|
17
|
+
sig { returns(T::Hash[Symbol, T::Hash[Symbol, String]]) }
|
18
|
+
attr_reader :exposed_tools
|
19
|
+
|
20
|
+
# DSL method to expose a method as a tool
|
21
|
+
sig { params(method_name: Symbol, tool_name: T.nilable(String), description: T.nilable(String)).void }
|
22
|
+
def tool(method_name, tool_name: nil, description: nil)
|
23
|
+
@exposed_tools ||= {}
|
24
|
+
@exposed_tools[method_name] = {
|
25
|
+
tool_name: tool_name || "#{toolset_name}_#{method_name}",
|
26
|
+
description: description || "#{method_name.to_s.tr('_', ' ').capitalize} operation"
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# DSL method to set the toolset name prefix
|
31
|
+
sig { params(name: T.nilable(String)).returns(String) }
|
32
|
+
def toolset_name(name = nil)
|
33
|
+
if name
|
34
|
+
@toolset_name = name
|
35
|
+
else
|
36
|
+
@toolset_name || self.name.split('::').last.gsub(/Toolset$/, '').downcase
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get all exposed tools as individual tool instances
|
41
|
+
sig { returns(T::Array[ToolProxy]) }
|
42
|
+
def to_tools
|
43
|
+
instance = new
|
44
|
+
(@exposed_tools || {}).map do |method_name, config|
|
45
|
+
ToolProxy.new(instance, method_name, config[:tool_name], config[:description])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Generate schema for a specific method using Sorbet signatures
|
50
|
+
sig { params(method_name: Symbol).returns(T::Hash[Symbol, T.untyped]) }
|
51
|
+
def schema_for_method(method_name)
|
52
|
+
method_obj = instance_method(method_name)
|
53
|
+
sig_info = T::Utils.signature_for_method(method_obj)
|
54
|
+
|
55
|
+
if sig_info.nil?
|
56
|
+
# Fallback for methods without signatures
|
57
|
+
return {
|
58
|
+
type: :object,
|
59
|
+
properties: {},
|
60
|
+
required: []
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reuse the schema generation logic from Base
|
65
|
+
properties = {}
|
66
|
+
required = []
|
67
|
+
|
68
|
+
# Handle keyword arguments (most common in Ruby)
|
69
|
+
sig_info.kwarg_types.each do |param_name, param_type|
|
70
|
+
next if param_name == :block
|
71
|
+
|
72
|
+
properties[param_name] = {
|
73
|
+
type: sorbet_type_to_json_schema(param_type)[:type],
|
74
|
+
description: "Parameter #{param_name}"
|
75
|
+
}
|
76
|
+
|
77
|
+
# Check if parameter is required
|
78
|
+
if sig_info.req_kwarg_names.include?(param_name)
|
79
|
+
required << param_name.to_s
|
80
|
+
else
|
81
|
+
properties[param_name][:description] += " (optional)"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
{
|
86
|
+
type: :object,
|
87
|
+
properties: properties,
|
88
|
+
required: required
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# Convert Sorbet types to JSON Schema types (extracted from Base)
|
95
|
+
sig { params(sorbet_type: T.untyped).returns(T::Hash[Symbol, T.untyped]) }
|
96
|
+
def sorbet_type_to_json_schema(sorbet_type)
|
97
|
+
# Check for boolean types first (SimplePairUnion of TrueClass | FalseClass)
|
98
|
+
if sorbet_type.respond_to?(:types) && sorbet_type.types.length == 2
|
99
|
+
raw_types = sorbet_type.types.map do |t|
|
100
|
+
t.is_a?(T::Types::Simple) ? t.raw_type : t
|
101
|
+
end
|
102
|
+
|
103
|
+
if raw_types.include?(TrueClass) && raw_types.include?(FalseClass)
|
104
|
+
return { type: :boolean }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if sorbet_type.is_a?(T::Types::Simple)
|
109
|
+
raw_type = sorbet_type.raw_type
|
110
|
+
|
111
|
+
case raw_type
|
112
|
+
when String
|
113
|
+
{ type: :string }
|
114
|
+
when Integer
|
115
|
+
{ type: :integer }
|
116
|
+
when Float, Numeric
|
117
|
+
{ type: :number }
|
118
|
+
when TrueClass, FalseClass, T::Boolean
|
119
|
+
{ type: :boolean }
|
120
|
+
else
|
121
|
+
{ type: :string, description: "#{raw_type} (converted to string)" }
|
122
|
+
end
|
123
|
+
elsif sorbet_type.is_a?(T::Types::Union)
|
124
|
+
# Handle nilable types
|
125
|
+
non_nil_types = sorbet_type.types.reject { |t| t == T::Utils.coerce(NilClass) }
|
126
|
+
if non_nil_types.length == 1
|
127
|
+
result = sorbet_type_to_json_schema(non_nil_types.first)
|
128
|
+
result[:description] = "#{result[:description] || ''} (optional)".strip
|
129
|
+
result
|
130
|
+
else
|
131
|
+
{ type: :string, description: "Union type (converted to string)" }
|
132
|
+
end
|
133
|
+
elsif sorbet_type.is_a?(T::Types::TypedArray)
|
134
|
+
{
|
135
|
+
type: :array,
|
136
|
+
items: sorbet_type_to_json_schema(sorbet_type.type)
|
137
|
+
}
|
138
|
+
else
|
139
|
+
{ type: :string, description: "#{sorbet_type} (converted to string)" }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Inner class that wraps a method as a tool, compatible with DSPy::Tools::Base interface
|
145
|
+
class ToolProxy < Base
|
146
|
+
extend T::Sig
|
147
|
+
|
148
|
+
sig { params(instance: Toolset, method_name: Symbol, tool_name: String, description: String).void }
|
149
|
+
def initialize(instance, method_name, tool_name, description)
|
150
|
+
@instance = instance
|
151
|
+
@method_name = method_name
|
152
|
+
@tool_name_override = tool_name
|
153
|
+
@description_override = description
|
154
|
+
end
|
155
|
+
|
156
|
+
sig { override.returns(String) }
|
157
|
+
def name
|
158
|
+
@tool_name_override
|
159
|
+
end
|
160
|
+
|
161
|
+
sig { override.returns(String) }
|
162
|
+
def description
|
163
|
+
@description_override
|
164
|
+
end
|
165
|
+
|
166
|
+
sig { override.returns(String) }
|
167
|
+
def schema
|
168
|
+
schema_obj = @instance.class.schema_for_method(@method_name)
|
169
|
+
tool_info = {
|
170
|
+
name: name,
|
171
|
+
description: description,
|
172
|
+
parameters: schema_obj
|
173
|
+
}
|
174
|
+
JSON.generate(tool_info)
|
175
|
+
end
|
176
|
+
|
177
|
+
# The main call method that tools must implement
|
178
|
+
sig { params(kwargs: T.untyped).returns(T.untyped) }
|
179
|
+
def call(**kwargs)
|
180
|
+
@instance.send(@method_name, **kwargs)
|
181
|
+
end
|
182
|
+
|
183
|
+
sig { override.params(args_json: T.untyped).returns(T.untyped) }
|
184
|
+
def dynamic_call(args_json)
|
185
|
+
schema = @instance.class.schema_for_method(@method_name)
|
186
|
+
|
187
|
+
if schema[:properties].empty?
|
188
|
+
@instance.send(@method_name)
|
189
|
+
else
|
190
|
+
# Parse arguments
|
191
|
+
args = case args_json
|
192
|
+
when Hash
|
193
|
+
args_json
|
194
|
+
when String
|
195
|
+
begin
|
196
|
+
JSON.parse(args_json)
|
197
|
+
rescue JSON::ParserError
|
198
|
+
return "Error: Invalid JSON input"
|
199
|
+
end
|
200
|
+
else
|
201
|
+
return "Error: Expected Hash or JSON string"
|
202
|
+
end
|
203
|
+
|
204
|
+
# Convert string keys to symbols and validate types
|
205
|
+
kwargs = {}
|
206
|
+
schema[:properties].each do |param_name, param_schema|
|
207
|
+
key = param_name.to_s
|
208
|
+
if args.key?(key)
|
209
|
+
kwargs[param_name] = convert_argument_type(args[key], param_schema)
|
210
|
+
elsif schema[:required].include?(key)
|
211
|
+
return "Error: Missing required parameter: #{key}"
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
@instance.send(@method_name, **kwargs)
|
216
|
+
end
|
217
|
+
rescue => e
|
218
|
+
"Error: #{e.message}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
data/lib/dspy/tools.rb
CHANGED
data/lib/dspy/version.rb
CHANGED
data/lib/dspy.rb
CHANGED
@@ -112,6 +112,7 @@ require_relative 'dspy/lm'
|
|
112
112
|
require_relative 'dspy/predict'
|
113
113
|
require_relative 'dspy/chain_of_thought'
|
114
114
|
require_relative 'dspy/re_act'
|
115
|
+
require_relative 'dspy/code_act'
|
115
116
|
require_relative 'dspy/evaluate'
|
116
117
|
require_relative 'dspy/teleprompt/teleprompter'
|
117
118
|
require_relative 'dspy/teleprompt/utils'
|
@@ -121,6 +122,7 @@ require_relative 'dspy/teleprompt/simple_optimizer'
|
|
121
122
|
require_relative 'dspy/teleprompt/mipro_v2'
|
122
123
|
require_relative 'dspy/subscribers/logger_subscriber'
|
123
124
|
require_relative 'dspy/tools'
|
125
|
+
require_relative 'dspy/memory'
|
124
126
|
require_relative 'dspy/instrumentation'
|
125
127
|
require_relative 'dspy/storage/program_storage'
|
126
128
|
require_relative 'dspy/storage/storage_manager'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dspy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
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-07-
|
10
|
+
date: 2025-07-05 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: dry-configurable
|
@@ -135,6 +135,20 @@ dependencies:
|
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
137
|
version: 0.20.0
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: informers
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - "~>"
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '1.2'
|
145
|
+
type: :runtime
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '1.2'
|
138
152
|
description: A Ruby implementation of DSPy, a framework for programming with large
|
139
153
|
language models
|
140
154
|
email:
|
@@ -146,6 +160,7 @@ files:
|
|
146
160
|
- README.md
|
147
161
|
- lib/dspy.rb
|
148
162
|
- lib/dspy/chain_of_thought.rb
|
163
|
+
- lib/dspy/code_act.rb
|
149
164
|
- lib/dspy/evaluate.rb
|
150
165
|
- lib/dspy/example.rb
|
151
166
|
- lib/dspy/few_shot_example.rb
|
@@ -159,6 +174,14 @@ files:
|
|
159
174
|
- lib/dspy/lm/adapters/openai_adapter.rb
|
160
175
|
- lib/dspy/lm/errors.rb
|
161
176
|
- lib/dspy/lm/response.rb
|
177
|
+
- lib/dspy/memory.rb
|
178
|
+
- lib/dspy/memory/embedding_engine.rb
|
179
|
+
- lib/dspy/memory/in_memory_store.rb
|
180
|
+
- lib/dspy/memory/local_embedding_engine.rb
|
181
|
+
- lib/dspy/memory/memory_compactor.rb
|
182
|
+
- lib/dspy/memory/memory_manager.rb
|
183
|
+
- lib/dspy/memory/memory_record.rb
|
184
|
+
- lib/dspy/memory/memory_store.rb
|
162
185
|
- lib/dspy/mixins/instrumentation_helpers.rb
|
163
186
|
- lib/dspy/mixins/struct_builder.rb
|
164
187
|
- lib/dspy/mixins/type_coercion.rb
|
@@ -184,6 +207,9 @@ files:
|
|
184
207
|
- lib/dspy/teleprompt/utils.rb
|
185
208
|
- lib/dspy/tools.rb
|
186
209
|
- lib/dspy/tools/base.rb
|
210
|
+
- lib/dspy/tools/memory_toolset.rb
|
211
|
+
- lib/dspy/tools/text_processing_toolset.rb
|
212
|
+
- lib/dspy/tools/toolset.rb
|
187
213
|
- lib/dspy/version.rb
|
188
214
|
homepage: https://github.com/vicentereig/dspy.rb
|
189
215
|
licenses:
|