Skip to content

Ruby SDK

The Ruby SDK gives your Ruby code the ten functions as methods on one client; decide? gives back true or false, and raises when the answer is not sure.

refund.rb
require "thinkthen"
ThinkThen::Client.open do |client|
is_refund = client.decide?("Does the customer request a refund?",
"Please refund my order.")
puts is_refund
end

Output

true

true means yes: the message asks for a refund.

Install the gem (CRuby 3.3 or later and RubyGems). It includes the native engine.

Terminal window
gem install thinkthen --version 0.2.0

With Bundler, run bundle add thinkthen --version 0.2.0. The client uses the key from the Quickstart.

Give threshold: a range, such as 0.2..0.8, to set a low bar and a high bar. Between them, decide is not sure, and decide? raises ThinkThen::UnsureError. When ThinkThen can't judge the message, it raises an error that includes ThinkThen::Error, such as ThinkThen::RecordError. Neither one can pass for false.

refund.rb
require "thinkthen"
message = "I was charged twice for one invoice."
ThinkThen::Client.open do |client|
is_refund = client.decide?("Does the customer request a refund?",
message, threshold: 0.2..0.8)
puts is_refund
rescue ThinkThen::UnsureError => unsure
puts "Not sure (#{unsure.probability}): ask a person."
rescue ThinkThen::Error => failure
puts "#{failure.code}: #{failure.message}"
end

Output

Not sure (0.7): ask a person.

A double charge might be a refund request, or might not, so the answer is not sure. Send it to a person.

UnsureError#probability is the chance of yes. UnsureError does not include ThinkThen::Error, so rescue ThinkThen::Error never catches a not sure by accident. The error's code names what went wrong, such as :"record.empty"; Troubleshooting lists each one.

To check the outcome without raising, call client.decide without the question mark. What it gives back is a ThinkThen::Answered, ThinkThen::Unsure or ThinkThen::Failed, so you can test unsure? and failed?, or match it with case ... in ThinkThen::Unsure. Reading its answer raises just as decide? does.

Each function asks the same support-inbox questions as the function pages.

Yes or no about one message
decide.rb
require "thinkthen"
ThinkThen::Client.open do |client|
# decide? raises ThinkThen::UnsureError when not sure,
# ThinkThen::RecordError on failure.
is_refund = client.decide?("Does the customer request a refund?",
"Please refund my order.")
puts is_refund
end

Output

true

Yes: the message asks for a refund. decide? takes threshold: (one bar, or a range for a low and a high bar), yes_means: and no_means:. decide shows every option.

decide_each takes many messages and yields one result for each, in order. A not sure or a failure is a result like any other, so the rest keep going:

decide_each.rb
require "thinkthen"
messages = [
{"id" => "a1", "text" => "Please refund my order."},
{"id" => "a2", "text" => "Where is my parcel?"},
{"id" => "a3", "text" => "I was charged twice for one invoice."},
]
question = "Does the customer request a refund?"
ThinkThen::Client.open do |client|
# One message: true or false, raising when not sure or failed.
is_refund = client.decide?(question, messages.first["text"])
puts "first message: #{is_refund}"
# Many messages in one call: one row each, in input order.
client.decide_each(question, messages, field: "/text") do |refund_check|
case refund_check
in ThinkThen::Answered then puts "#{refund_check.id} #{refund_check.yes?}"
in ThinkThen::Unsure then puts "#{refund_check.id} not sure: ask a person"
in ThinkThen::Failed then
puts "#{refund_check.id} failed: #{refund_check.error.code}"
end
end
end

Output

first message: true
a1 true
a2 false
a3 true

field: "/text" judges the text field of each Hash, and id is the message's own id. Each one-message method has an _each twin that works the same way.

The one choice that fits
choose.rb
require "thinkthen"
ThinkThen::Client.open do |client|
topic = client.choose("What is the message about?",
"Please refund my order.",
choices: ["billing", "shipping", "other"]).answer
puts topic
end

Output

billing

A refund is about billing. choose takes descriptions:, a Hash from each choice to what it means, and threshold:, which can make choose not sure. answer raises UnsureError then. choose shows every option.

Only the messages that answer yes
filter.rb
require "thinkthen"
messages = [
"Please refund my order.",
"Where is my parcel?",
"I was charged twice for one invoice.",
]
ThinkThen::Client.open do |client|
# Only the messages that answer yes.
# A failed message raises ThinkThen::RecordError after the kept ones.
refund_requests =
client.filter("Does the customer request a refund?", messages)
refund_requests.each { |refund_request| puts refund_request }
end

Output

Please refund my order.
I was charged twice for one invoice.

The parcel question is left out. filter gives back your own messages, and leaves out the ones it is not sure about. If any message fails, it raises one RecordError after the kept ones, and its rows lists every failure. failures: :row hands the failures over after the kept messages instead, and filter_each gives every message with its outcome. filter shows every option.

Every label that fits
tag.rb
require "thinkthen"
ThinkThen::Client.open do |client|
topics = client.tag("Which topics appear?",
"I was charged twice and my parcel is late again.",
labels: ["billing", "shipping"]).answer
p topics
end

Output

["billing", "shipping"]

The message is about both billing and shipping. The answer is an Array of the labels that fit. tag shows every option.

A number from 0 to 1 on your scale
score.rb
require "thinkthen"
ThinkThen::Client.open do |client|
urgency = client.score("How urgent is the request?",
"Please refund my order.",
levels: ["routine", "urgent"]).answer
puts urgency
end

Output

0.05

The levels run lowest first, so a plain refund request scores near routine. score shows every option.

Every message, highest first
rank.rb
require "thinkthen"
messages = [
"Please refund my order.",
"Where is my parcel?",
"I was charged twice for one invoice.",
]
ThinkThen::Client.open do |client|
# Every message, most urgent first. rank starts at 1.
by_urgency = client.rank("How urgent is the request?", messages,
levels: ["routine", "urgent"])
by_urgency.each { |ranked| puts "#{ranked.rank} #{ranked.item}" }
end

Output

1 I was charged twice for one invoice.
2 Where is my parcel?
3 Please refund my order.

The double charge is the most urgent. Each result holds a copy of your message as item and its place as rank, starting at 1. Reading rank raises UnsureError for a message it is not sure about; that message keeps its place in the order. top: keeps only the first few. rank shows every option.

The line that answers the question
find.rb
require "thinkthen"
message = "Morgan works for Example Group.\nPlease refund my order."
ThinkThen::Client.open do |client|
# nil when no line answers the question.
action = client.find("Which line states the requested action?",
message).answer
puts action ? action.text : "no line answers"
end

Output

Please refund my order.

The second line states the action. The answer is a ThinkThen::Found with text, start and end, or nil when no line answers. find shows every option.

Each name in the text and its kind
recognize.rb
require "thinkthen"
ThinkThen::Client.open do |client|
names = client.recognize("Morgan works for Example Group.",
kinds: ["person", "organization"]).answer
names.each { |name| puts "#{name.kind}: #{name.text}" }
end

Output

person: Morgan
organization: Example Group

Each ThinkThen::Name holds text, kind, start, end and probability. recognize shows every option.

Names linked by the relations you allow
relate.rb
require "thinkthen"
message = "Morgan works for Example Group.\nPlease refund my order."
works_for = ThinkThen::RelationRule.new(source: "person",
target: "organization",
relation: "works_for")
ThinkThen::Client.open do |client|
relations = client.relate(message, rules: [works_for]).answer
relations.each do |relation|
puts "#{relation.source.text} --#{relation.relation}--> " \
"#{relation.target.text}"
end
end

Output

Morgan --works_for--> Example Group

A ThinkThen::RelationRule names the kind of name at each end and the relation between them. It also takes phrase: and symmetric: true. relate shows every option.

Several questions for each message
annotate.rb
require "thinkthen"
messages = [
"Please refund my order.",
"Where is my parcel?",
"I was charged twice for one invoice.",
]
questions = {
"refund" => ThinkThen::DecideQuestion.new(
"Does the customer request a refund?"
),
"topic" => ThinkThen::ChooseQuestion.new(
"What is the message about?",
choices: ["billing", "shipping", "other"]
),
"urgency" => ThinkThen::ScoreQuestion.new(
"How urgent is the request?",
levels: ["routine", "urgent"]
),
}
ThinkThen::Client.open do |client|
client.annotate_each(questions, messages) do |form|
is_refund = form[:refund].answer
topic = form[:topic].answer
urgency = form[:urgency].answer
puts "#{is_refund} #{topic} #{urgency}"
end
end

Output

true billing 0.05
false shipping 0.14
true billing 0.45

form[:refund] takes a String or Symbol name and raises KeyError for a name you never added. Each answer raises UnsureError when not sure, as decide does. For one message, call client.annotate(questions, message). annotate shows every option.

A message is a String, or a Hash with String or Symbol keys; field: "/text" judges one field. Every call also takes name: and cancel:. Pass cancel: ThinkThen::CancelToken.new, and cancel on the token stops the call; Ctrl-C does the same, and both raise ThinkThen::CancelledError. ThinkThen.configure sets up one shared client for the module calls, such as ThinkThen.decide?.

The Ruby signatures list every public name, and the Ruby API reference explains each one.