Class: DSPy::Tools::Base
- Inherits:
-
Object
- Object
- DSPy::Tools::Base
- Extended by:
- T::Helpers, T::Sig
- Includes:
- Mixins::TypeCoercion
- Defined in:
- lib/dspy/tools/base.rb
Overview
Base class for all Sorbet-based tools with DSL support
Direct Known Subclasses
Class Attribute Summary collapse
-
.tool_description_value ⇒ Object
readonly
Returns the value of attribute tool_description_value.
-
.tool_name_value ⇒ Object
readonly
Returns the value of attribute tool_name_value.
Class Method Summary collapse
- .call_schema ⇒ Object
- .call_schema_object ⇒ Object
- .tool_description(description) ⇒ Object
- .tool_name(name) ⇒ Object
Instance Method Summary collapse
- #call_schema ⇒ Object
- #description ⇒ Object
- #dynamic_call(args_json) ⇒ Object
- #name ⇒ Object
- #schema ⇒ Object
Class Attribute Details
.tool_description_value ⇒ Object (readonly)
Returns the value of attribute tool_description_value.
20 21 22 |
# File 'lib/dspy/tools/base.rb', line 20 def tool_description_value @tool_description_value end |
.tool_name_value ⇒ Object (readonly)
Returns the value of attribute tool_name_value.
20 21 22 |
# File 'lib/dspy/tools/base.rb', line 20 def tool_name_value @tool_name_value end |
Class Method Details
.call_schema ⇒ Object
89 90 91 92 93 94 95 96 97 98 |
# File 'lib/dspy/tools/base.rb', line 89 def call_schema { type: 'function', function: { name: 'call', description: "Call the #{self.name} tool", parameters: call_schema_object } } end |
.call_schema_object ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/dspy/tools/base.rb', line 36 def call_schema_object method_obj = instance_method(:call) sig_info = T::Utils.signature_for_method(method_obj) if sig_info.nil? # Fallback for methods without signatures return { type: "object", properties: {}, required: [] } end properties = {} required = [] # Handle positional arguments sig_info.arg_types.each do |param_name, param_type| next if param_name == :block # Skip block parameters schema = DSPy::TypeSystem::SorbetJsonSchema.type_to_json_schema(param_type) properties[param_name] = schema.merge({ description: "Parameter #{param_name}" }) # Check if parameter is required (not nilable) unless param_type.class.name.include?('Union') && param_type.name.include?('NilClass') required << param_name.to_s end end # Handle keyword arguments (more common in Ruby) sig_info.kwarg_types.each do |param_name, param_type| next if param_name == :block # Skip block parameters schema = DSPy::TypeSystem::SorbetJsonSchema.type_to_json_schema(param_type) properties[param_name] = schema.merge({ description: "Parameter #{param_name}" }) # Check if parameter is required by looking at required kwarg names if sig_info.req_kwarg_names.include?(param_name) required << param_name.to_s else properties[param_name][:description] += " (optional)" end end { type: "object", properties: properties, required: required } end |
.tool_description(description) ⇒ Object
30 31 32 |
# File 'lib/dspy/tools/base.rb', line 30 def tool_description(description) @tool_description_value = description end |
.tool_name(name) ⇒ Object
24 25 26 |
# File 'lib/dspy/tools/base.rb', line 24 def tool_name(name) @tool_name_value = name end |
Instance Method Details
#call_schema ⇒ Object
127 128 129 |
# File 'lib/dspy/tools/base.rb', line 127 def call_schema self.class.call_schema end |
#description ⇒ Object
109 110 111 |
# File 'lib/dspy/tools/base.rb', line 109 def description self.class.tool_description_value || "Tool: #{name}" end |
#dynamic_call(args_json) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/dspy/tools/base.rb', line 133 def dynamic_call(args_json) # Parse arguments based on the call schema schema = self.class.call_schema_object if schema[:properties].empty? # No parameters - call without arguments call else # Parse arguments and call with keyword arguments args = case args_json when Hash args_json when String begin JSON.parse(args_json) rescue JSON::ParserError return "Error: Invalid JSON input" end else return "Error: Expected Hash or JSON string" end # Convert string keys to symbols and validate types kwargs = {} # Get method signature for type information method_obj = self.class.instance_method(:call) sig_info = T::Utils.signature_for_method(method_obj) if sig_info # Handle kwargs using type signature information sig_info.kwarg_types.each do |param_name, param_type| next if param_name == :block key = param_name.to_s if args.key?(key) kwargs[param_name] = coerce_value_to_type(args[key], param_type) elsif schema[:required].include?(key) return "Error: Missing required parameter: #{key}" end end # Handle positional args if any sig_info.arg_types.each do |param_name, param_type| next if param_name == :block key = param_name.to_s if args.key?(key) kwargs[param_name] = coerce_value_to_type(args[key], param_type) elsif schema[:required].include?(key) return "Error: Missing required parameter: #{key}" end end end call(**kwargs) end rescue => e "Error: #{e.}" end |
#name ⇒ Object
104 105 106 |
# File 'lib/dspy/tools/base.rb', line 104 def name self.class.tool_name_value || self.class.name&.split('::')&.last&.downcase || 'unknown_tool' end |
#schema ⇒ Object
115 116 117 118 119 120 121 122 123 |
# File 'lib/dspy/tools/base.rb', line 115 def schema schema_obj = self.class.call_schema_object tool_info = { name: name, description: description, parameters: schema_obj } JSON.generate(tool_info) end |