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.
require "thinkthen"
ThinkThen::Client.open do |client| is_refund = client.decide?("Does the customer request a refund?", "Please refund my order.") puts is_refundendOutput
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.
gem install thinkthen --version 0.2.0With Bundler, run bundle add thinkthen --version 0.2.0. The client uses the key from the Quickstart.
Rescue not sure and failures
Section titled “Rescue not sure and failures”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.
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_refundrescue ThinkThen::UnsureError => unsure puts "Not sure (#{unsure.probability}): ask a person."rescue ThinkThen::Error => failure puts "#{failure.code}: #{failure.message}"endOutput
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.
The ten functions
Section titled “The ten functions”Each function asks the same support-inbox questions as the function pages.
decide
Section titled “decide”Yes or no about one message
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_refundendOutput
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:
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 endendOutput
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.
choose
Section titled “choose”The one choice that fits
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 topicendOutput
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.
filter
Section titled “filter”Only the messages that answer yes
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 }endOutput
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
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 topicsendOutput
["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
require "thinkthen"
ThinkThen::Client.open do |client| urgency = client.score("How urgent is the request?", "Please refund my order.", levels: ["routine", "urgent"]).answer puts urgencyendOutput
0.05
The levels run lowest first, so a plain refund request scores near routine. score shows every option.
Every message, highest first
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}" }endOutput
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
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"endOutput
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.
recognize
Section titled “recognize”Each name in the text and its kind
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}" }endOutput
person: Morgan
organization: Example Group
Each ThinkThen::Name holds text, kind, start, end and probability. recognize shows every option.
relate
Section titled “relate”Names linked by the relations you allow
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}" endendOutput
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.
annotate
Section titled “annotate”Several questions for each message
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}" endendOutput
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.
The full API
Section titled “The full API”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.