Skip to content

C# SDK

The C# SDK gives your .NET code the ten functions as async methods on one client; each call gives back a plain C# value, such as true or false from decide, and throws when the answer is not sure.

Program.cs
using ThinkThen;
await using var client = new ThinkThenClient();
bool isRefund = await client.DecideAsync(
"Does the customer request a refund?",
"Please refund my order.");
Console.WriteLine(isRefund);

Output

True

True means yes: the message asks for a refund.

Install it (.NET 10 on Linux x64 or arm64 with glibc, macOS on Apple silicon, or Windows x64):

Terminal window
dotnet add package Botassembly.ThinkThen --version 0.2.0

Run it inside a console project made with dotnet new console, then dotnet run. The client uses the key from the Quickstart.

Set a low bar and a high bar with Threshold.Band. Between them, decide is not sure, and reading the answer as a bool throws UnsureException. When ThinkThen can't judge the message, it throws ThinkThenException instead. Neither one can pass for false.

Program.cs
using ThinkThen;
await using var client = new ThinkThenClient();
var bars = new DecideOptions
{
Threshold = Threshold.Band(0.2, 0.8),
};
try
{
bool isRefund = await client.DecideAsync(
"Does the customer request a refund?",
"I was charged twice for one invoice.", bars);
Console.WriteLine(isRefund);
}
catch (UnsureException unsure)
{
Console.WriteLine(
$"Not sure ({unsure.Probability}): ask a person.");
}
catch (ThinkThenException failure)
{
Console.WriteLine($"{failure.Code}: {failure.Message}");
}

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.

UnsureException.Probability is the chance of yes. ThinkThenException.Code names the error, such as record.empty; Troubleshooting lists each one. To check the outcome without an exception, keep the result as it comes back and test IsUnsure or IsFailed, or call TryGetAnswer.

Each function reads the same support-inbox messages as the function pages. Where a program reads message.txt or messages.txt, the text it holds is named above the program.

Yes or no about one message

message.txt holds Please refund my order.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
// Throws UnsureException when not sure,
// ThinkThenException when the message failed.
bool isRefund = await client.DecideAsync(
"Does the customer request a refund?", message);
Console.WriteLine(isRefund);

Output

True

Yes: the message asks for a refund. DecideOptions takes Threshold (one bar, or a low and a high bar with Threshold.Band), YesMeans and NoMeans. DecideStreamAsync takes many messages and gives one answer each, in order. decide shows every option.

The one choice that fits

message.txt holds Please refund my order.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
string topic = await client.ChooseAsync(
"What is the message about?", message,
["billing", "shipping", "other"]);
Console.WriteLine(topic);

Output

billing

A refund is about billing. ChooseOptions takes Descriptions, a dictionary from each choice to what it means, and Threshold, which can make choose not sure. choose shows every option.

Only the messages that answer yes

messages.txt holds three lines: Please refund my order., Where is my parcel? and I was charged twice for one invoice.

Program.cs
using ThinkThen;
await using var client = new ThinkThenClient();
// Only the messages that answer yes.
// A failed message throws after the kept ones.
await foreach (string refund in client.FilterAsync(
"Does the customer request a refund?", File.ReadLinesAsync("messages.txt")))
Console.WriteLine(refund);

Output

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

The parcel question is left out. FilterAsync gives back your own messages, and leaves out the ones it is not sure about. If any message fails, it throws ThinkThenException after the kept ones, and its FailedRows lists every failure. FilterStreamAsync gives every message with its outcome instead. filter shows every option.

Every label that fits

message.txt holds I was charged twice and my parcel is late again.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
ValueList<string> topics = await client.TagAsync(
"Which topics appear?", message,
["billing", "shipping"]);
Console.WriteLine(topics);

Output

[billing, shipping]

The message is about both billing and shipping. The answer is a ValueList<string>, empty when no label fits. tag shows every option.

A number from 0 to 1 on your scale

message.txt holds Please refund my order.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
double urgency = await client.ScoreAsync(
"How urgent is the request?", message,
["routine", "urgent"]);
Console.WriteLine(urgency);

Output

0.05

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

Every message, highest first

messages.txt holds the three messages from filter.

Program.cs
using ThinkThen;
await using var client = new ThinkThenClient();
// Every message, most urgent first. Each answer is a rank, starting at 1.
await foreach (var ranked in client.RankAsync(
"How urgent is the request?", File.ReadLinesAsync("messages.txt"),
["routine", "urgent"]))
{
long rank = ranked.Row;
Console.WriteLine($"{rank} {ranked.Item}");
}

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 RankedItem holds your message as Item and its rank as Row, starting at 1. RankOptions takes Top to keep only the first few. rank shows every option.

The line that answers the question

message.txt holds two lines: Morgan works for Example Group. and Please refund my order.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
// Null when no line answers the question.
TextSpan? action = await client.FindAsync(
"Which line states the requested action?", message);
Console.WriteLine(action?.Text ?? "no line answers");

Output

Please refund my order.

The second line states the action. The answer is a TextSpan with Text, Start and End, or null when no line answers. Offsets count UTF-16 units, as .NET strings do. find shows every option.

Each name in the text and its kind

message.txt holds Morgan works for Example Group.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
ValueList<Entity> names = await client.RecognizeAsync(
message, ["person", "organization"]);
foreach (Entity name in names)
Console.WriteLine($"{name.Mention.Kind}: {name.Mention.Span.Text}");

Output

person: Morgan
organization: Example Group

Each Entity holds a Mention, with the name's Span and Kind, and its Probability. recognize shows every option.

Names linked by the relations you allow

message.txt holds the two lines from find.

Program.cs
using ThinkThen;
string message = File.ReadAllText("message.txt");
await using var client = new ThinkThenClient();
var worksFor = new RelationRule
{
Source = "person",
Target = "organization",
Name = "works_for",
};
ValueList<Relation> relations = await client.RelateAsync(message, [worksFor]);
foreach (Relation relation in relations)
Console.WriteLine($"{relation.Source.Span.Text} --{relation.Name}--> "
+ $"{relation.Target.Span.Text}");

Output

Morgan --works_for--> Example Group

A RelationRule names the kind of name at each end and the relation between them. relate shows every option.

Several questions for each message

messages.txt holds the three messages from filter.

Program.cs
using ThinkThen;
await using var client = new ThinkThenClient();
using var refundQuestion = DecideQuestion.Create(
"Does the customer request a refund?");
using var topicQuestion = ChooseQuestion.Create("What is the message about?",
["billing", "shipping", "other"]);
using var urgencyQuestion = ScoreQuestion.Create("How urgent is the request?",
["routine", "urgent"]);
using var questions = AnnotationQuestions.Create(
new Dictionary<string, AnnotationQuestion>
{
["refund"] = new AnnotationQuestion.Decide(refundQuestion),
["topic"] = new AnnotationQuestion.Choose(topicQuestion),
["urgency"] = new AnnotationQuestion.Score(urgencyQuestion),
});
await foreach (var form in client.AnnotateStreamAsync(
questions, File.ReadLinesAsync("messages.txt")))
{
// Each answer throws UnsureException when not sure, as a row does.
ValueMap<string, AnnotationEntry> answers = form;
bool isRefund = answers["refund"];
string topic = answers["topic"];
double urgency = answers["urgency"];
Console.WriteLine($"{isRefund} {topic} {urgency}");
}

Output

True billing 0.05
False shipping 0.14
True billing 0.45

Each answer is read by name and converts to its own type, and throws UnsureException when not sure, as decide does. For one message, call AnnotateAsync(questions, message). annotate shows every option.

Each options type takes Field, such as Pointer.From("/text"), to judge one field of a structured message. Every call also takes a final CancellationToken, and cancelling it throws OperationCanceledException. In ASP.NET, services.AddThinkThen(...) registers one shared client. The C# API reference lists every type and method.