Class: SignalWire::DataMap
- Inherits:
-
Object
- Object
- SignalWire::DataMap
- Defined in:
- lib/signalwire/datamap/data_map.rb
Overview
Fluent builder for server-side DataMap tools.
DataMap tools execute on SignalWire servers without requiring webhook endpoints. This class provides a chainable API for building data_map configurations that become SWAIG function definitions.
All mutator methods return self so calls can be chained:
dm = DataMap.new('get_weather')
.purpose('Get current weather')
.parameter('location', 'string', 'City name', required: true)
.webhook('GET', 'https://api.weather.com/v1/current?q=${location}')
.output(Swaig::FunctionResult.new('Weather: ${response.current.temp_f}F'))
Instance Attribute Summary collapse
-
#function_name ⇒ Object
readonly
Returns the value of attribute function_name.
Class Method Summary collapse
-
.create_expression_tool(name:, patterns:, parameters: nil) ⇒ DataMap
Build an expression-only tool (no HTTP calls).
-
.create_simple_api_tool(name:, url:, response_template:, parameters: nil, method: 'GET', headers: nil, body: nil, error_keys: nil) ⇒ DataMap
Build a simple API-calling tool in one shot.
Instance Method Summary collapse
-
#body(data) ⇒ Object
Set the request body for the most-recently-added webhook (POST / PUT).
-
#description(desc) ⇒ Object
Alias for
purpose. -
#error_keys(keys) ⇒ Object
Set error keys on the most-recently-added webhook, or at the top level if no webhook has been added yet.
-
#expression(test_value, pattern, output, nomatch_output: nil) ⇒ Object
Add an expression (pattern-matching rule).
-
#fallback_output(result) ⇒ Object
Set a fallback output used when all webhooks fail.
-
#foreach(config) ⇒ Object
Configure array processing on the most-recently-added webhook response.
-
#global_error_keys(keys) ⇒ Object
Set top-level error keys (applies to all webhooks).
-
#initialize(function_name) ⇒ DataMap
constructor
A new instance of DataMap.
-
#output(result) ⇒ Object
Set the output result for the most-recently-added webhook.
-
#parameter(name, type, desc, required: false, enum: nil) ⇒ Object
Add a typed parameter to the function signature — the
descis LLM-FACING. -
#params(data) ⇒ Object
Set request params for the most-recently-added webhook.
-
#purpose(desc) ⇒ Object
Set the LLM-facing tool description (a.k.a. “purpose”).
-
#to_swaig_function ⇒ Hash
Serialize this DataMap into a complete SWAIG function definition Hash.
-
#webhook(method, url, headers: nil, form_param: nil, input_args_as_params: false, require_args: nil) ⇒ Object
Add a webhook (HTTP call) to the data_map pipeline.
-
#webhook_expressions(expressions) ⇒ Object
Add expressions to run after the most-recently-added webhook completes.
Constructor Details
#initialize(function_name) ⇒ DataMap
Returns a new instance of DataMap.
28 29 30 31 32 33 34 35 36 37 |
# File 'lib/signalwire/datamap/data_map.rb', line 28 def initialize(function_name) @function_name = function_name @purpose_text = '' @parameters = {} # name => { "type" => ..., "description" => ... } @required_params = [] @expressions = [] @webhooks = [] @fallback_output = nil @global_error_keys = [] end |
Instance Attribute Details
#function_name ⇒ Object (readonly)
Returns the value of attribute function_name.
26 27 28 |
# File 'lib/signalwire/datamap/data_map.rb', line 26 def function_name @function_name end |
Class Method Details
.create_expression_tool(name:, patterns:, parameters: nil) ⇒ DataMap
Build an expression-only tool (no HTTP calls).
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/signalwire/datamap/data_map.rb', line 294 def self.create_expression_tool(name:, patterns:, parameters: nil) dm = new(name) if parameters parameters.each do |pname, pdef| dm.parameter( pname, pdef.fetch("type", "string"), pdef.fetch("description", "#{pname} parameter"), required: pdef.fetch("required", false) ) end end patterns.each do |test_value, (pattern, result)| dm.expression(test_value, pattern, result) end dm end |
.create_simple_api_tool(name:, url:, response_template:, parameters: nil, method: 'GET', headers: nil, body: nil, error_keys: nil) ⇒ DataMap
Build a simple API-calling tool in one shot.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/signalwire/datamap/data_map.rb', line 266 def self.create_simple_api_tool(name:, url:, response_template:, parameters: nil, method: 'GET', headers: nil, body: nil, error_keys: nil) dm = new(name) if parameters parameters.each do |pname, pdef| dm.parameter( pname, pdef.fetch("type", "string"), pdef.fetch("description", "#{pname} parameter"), required: pdef.fetch("required", false) ) end end dm.webhook(method, url, headers: headers) dm.body(body) if body dm.error_keys(error_keys) if error_keys dm.output(Swaig::FunctionResult.new(response_template)) dm end |
Instance Method Details
#body(data) ⇒ Object
Set the request body for the most-recently-added webhook (POST / PUT).
156 157 158 159 160 161 |
# File 'lib/signalwire/datamap/data_map.rb', line 156 def body(data) raise ArgumentError, "Must add webhook before setting body" if @webhooks.empty? @webhooks.last["body"] = data self end |
#description(desc) ⇒ Object
Alias for purpose. Sets the LLM-facing tool description. This string is read by the model to decide WHEN to call this tool. See purpose for bad-vs-good examples.
64 65 66 |
# File 'lib/signalwire/datamap/data_map.rb', line 64 def description(desc) purpose(desc) end |
#error_keys(keys) ⇒ Object
Set error keys on the most-recently-added webhook, or at the top level if no webhook has been added yet.
206 207 208 209 210 211 212 213 |
# File 'lib/signalwire/datamap/data_map.rb', line 206 def error_keys(keys) if @webhooks.any? @webhooks.last["error_keys"] = keys else @global_error_keys = keys end self end |
#expression(test_value, pattern, output, nomatch_output: nil) ⇒ Object
Add an expression (pattern-matching rule).
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/signalwire/datamap/data_map.rb', line 107 def expression(test_value, pattern, output, nomatch_output: nil) pattern_str = pattern.is_a?(Regexp) ? pattern.source : pattern.to_s output_h = output.respond_to?(:to_h) ? output.to_h : output expr_def = { "string" => test_value, "pattern" => pattern_str, "output" => output_h } if nomatch_output nomatch_h = nomatch_output.respond_to?(:to_h) ? nomatch_output.to_h : nomatch_output expr_def["nomatch-output"] = nomatch_h end @expressions << expr_def self end |
#fallback_output(result) ⇒ Object
Set a fallback output used when all webhooks fail.
199 200 201 202 |
# File 'lib/signalwire/datamap/data_map.rb', line 199 def fallback_output(result) @fallback_output = result.respond_to?(:to_h) ? result.to_h : result self end |
#foreach(config) ⇒ Object
Configure array processing on the most-recently-added webhook response.
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/signalwire/datamap/data_map.rb', line 174 def foreach(config) raise ArgumentError, "Must add webhook before setting foreach" if @webhooks.empty? raise ArgumentError, "foreach config must be a Hash" unless config.is_a?(Hash) required_keys = %w[input_key output_key append] missing = required_keys - config.keys.map(&:to_s) raise ArgumentError, "foreach config missing required keys: #{missing.inspect}" unless missing.empty? @webhooks.last["foreach"] = config self end |
#global_error_keys(keys) ⇒ Object
Set top-level error keys (applies to all webhooks).
216 217 218 219 |
# File 'lib/signalwire/datamap/data_map.rb', line 216 def global_error_keys(keys) @global_error_keys = keys self end |
#output(result) ⇒ Object
Set the output result for the most-recently-added webhook.
189 190 191 192 193 194 |
# File 'lib/signalwire/datamap/data_map.rb', line 189 def output(result) raise ArgumentError, "Must add webhook before setting output" if @webhooks.empty? @webhooks.last["output"] = result.respond_to?(:to_h) ? result.to_h : result self end |
#parameter(name, type, desc, required: false, enum: nil) ⇒ Object
Add a typed parameter to the function signature — the desc is LLM-FACING.
Each parameter description is rendered into the OpenAI tool schema under parameters.properties.<name>.description and sent to the model. The model uses it to decide HOW to fill in the argument from user speech. It is prompt engineering, not developer FYI.
Bad vs good
BAD : .parameter("city", "string", "the city")
GOOD: .parameter("city", "string",
"The name of the city to get weather for, e.g. " \
"'San Francisco'. Ask the user if they did not " \
"provide one. Include the state or country if the " \
"city name is ambiguous.")
93 94 95 96 97 98 99 |
# File 'lib/signalwire/datamap/data_map.rb', line 93 def parameter(name, type, desc, required: false, enum: nil) param_def = { "type" => type, "description" => desc } param_def["enum"] = enum if enum && !enum.empty? @parameters[name] = param_def @required_params << name if required && !@required_params.include?(name) self end |
#params(data) ⇒ Object
Set request params for the most-recently-added webhook.
164 165 166 167 168 169 |
# File 'lib/signalwire/datamap/data_map.rb', line 164 def params(data) raise ArgumentError, "Must add webhook before setting params" if @webhooks.empty? @webhooks.last["params"] = data self end |
#purpose(desc) ⇒ Object
Set the LLM-facing tool description (a.k.a. “purpose”). *PROMPT ENGINEERING*, not developer documentation.
The description string is rendered into the OpenAI tool schema description field on every LLM turn. The model reads it to decide WHEN to call this tool. A vague purpose is the #1 cause of “the model has the right tool but doesn’t call it” failures with data-map tools.
Bad vs good
BAD : .purpose("weather api")
GOOD: .purpose("Get the current weather conditions and " \
"forecast for a specific city. Use this " \
"whenever the user asks about weather, " \
"temperature, rain, or similar conditions in a " \
"named location.")
56 57 58 59 |
# File 'lib/signalwire/datamap/data_map.rb', line 56 def purpose(desc) @purpose_text = desc self end |
#to_swaig_function ⇒ Hash
Serialize this DataMap into a complete SWAIG function definition Hash.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/signalwire/datamap/data_map.rb', line 224 def to_swaig_function # Build parameter schema if @parameters.any? param_schema = { "type" => "object", "properties" => @parameters.dup } param_schema["required"] = @required_params.dup if @required_params.any? else param_schema = { "type" => "object", "properties" => {} } end # Build data_map data_map = {} data_map["expressions"] = @expressions if @expressions.any? data_map["webhooks"] = @webhooks if @webhooks.any? data_map["output"] = @fallback_output if @fallback_output data_map["error_keys"] = @global_error_keys if @global_error_keys.any? { "function" => @function_name, "description" => @purpose_text.empty? ? "Execute #{@function_name}" : @purpose_text, "parameters" => param_schema, "data_map" => data_map } end |
#webhook(method, url, headers: nil, form_param: nil, input_args_as_params: false, require_args: nil) ⇒ Object
Add a webhook (HTTP call) to the data_map pipeline.
134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/signalwire/datamap/data_map.rb', line 134 def webhook(method, url, headers: nil, form_param: nil, input_args_as_params: false, require_args: nil) wh = { "url" => url, "method" => method.upcase } wh["headers"] = headers if headers wh["form_param"] = form_param if form_param wh["input_args_as_params"] = true if input_args_as_params wh["require_args"] = require_args if require_args @webhooks << wh self end |
#webhook_expressions(expressions) ⇒ Object
Add expressions to run after the most-recently-added webhook completes.
148 149 150 151 152 153 |
# File 'lib/signalwire/datamap/data_map.rb', line 148 def webhook_expressions(expressions) raise ArgumentError, "Must add webhook before setting webhook expressions" if @webhooks.empty? @webhooks.last["expressions"] = expressions self end |