C SDK
The C SDK gives your program the ten functions as plain C calls; each call returns a code that says whether there is an answer, and writes the answer into your own variable, such as a bool from decide.
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; bool is_refund = false; int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_decide(client, "Does the customer request a refund?", "Please refund my order.", THINKTHEN_NUL_TERMINATED, &is_refund, &error); if (rc == THINKTHEN_OK) puts(is_refund ? "true" : "false"); else report(&error); thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_OK ? EXIT_SUCCESS : EXIT_FAILURE;}Output
true
true means yes: the message asks for a refund.
It needs A C11 compiler and pkg-config. Extract the C archive for your platform from the 0.2.0 release into thinkthen, save the program as example.c, then compile it and run ./example:
cc -std=c11 -Wall -Wextra -Werror example.c -o example \ $(PKG_CONFIG_PATH="$PWD/thinkthen/lib/pkgconfig" \ pkg-config --cflags --libs thinkthen-c) \ -Wl,-rpath,"$PWD/thinkthen/lib"The -Wl,-rpath flag tells the program where to find the library when it runs. The client uses the key from the Quickstart.
Read not sure and failures from the return code
Section titled “Read not sure and failures from the return code”Every call returns THINKTHEN_OK when it wrote an answer. It returns THINKTHEN_UNSURE when the model is not sure, and then it writes no answer, so not sure can never read as false. When ThinkThen can't judge the text, it returns a negative number, and error holds the code and its message.
To get not sure from decide, build the question and set a low bar and a high bar with thinkthen_question_set_band. thinkthen_run_one then asks it and fills in the result.
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { thinkthen_client *client = NULL; thinkthen_question *refund = NULL; thinkthen_row result = THINKTHEN_ROW_INIT; thinkthen_error error = THINKTHEN_ERROR_INIT; int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_DECIDE, "Does the customer request a refund?", THINKTHEN_NUL_TERMINATED, &refund, &error); // Below 0.2 is no, 0.8 and above is yes, and between is not sure. if (rc == THINKTHEN_OK) rc = thinkthen_question_set_band(refund, 0.2, 0.8, &error); if (rc == THINKTHEN_OK) rc = thinkthen_run_one(client, refund, "I was charged twice for one invoice.", THINKTHEN_NUL_TERMINATED, THINKTHEN_RECORD_TEXT, &result, &error); if (rc == THINKTHEN_OK) puts(result.answer_bool ? "true" : "false"); else if (rc == THINKTHEN_UNSURE) printf("not sure (%g): ask a person\n", result.probability); else report(&error); thinkthen_row_free(&result); thinkthen_question_free(refund); thinkthen_error_free(&error); thinkthen_client_free(client); return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;}Output
not sure (0.7): ask a person
A double charge might be a refund request, or might not, so decide is not sure. Send it to a person. probability is the chance of yes.
- The negative number is minus the error code, such as
-THINKTHEN_CODE_RECORD_EMPTYfor blank text, which is never sent. Compare withTHINKTHEN_OK, never with-1. thinkthen_code_namegives the code's name, such asrecord.empty.error.messageis counted bytes, so print it with its length. Troubleshooting lists each code.- decide, filter and choose take a low and a high bar. choose and find can also be not sure without them. tag, score, rank, recognize and relate answer or fail. Each annotate answer follows its own question.
- With GCC and Clang, ignoring a return code draws a compiler warning.
Start every thinkthen_error with THINKTHEN_ERROR_INIT and every result with THINKTHEN_ROW_INIT. Passing one to another call frees what it held first. Free each with thinkthen_error_free or thinkthen_row_free after its last use. Clients, questions, calls and values each have their own free function, and every free function accepts NULL.
The ten functions
Section titled “The ten functions”Each program is a whole file that compiles with the line above, and asks the support-inbox question its function page asks.
decide
Section titled “decide”Yes or no about one message
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; bool is_refund = false; int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_decide(client, "Does the customer request a refund?", "Please refund my order.", THINKTHEN_NUL_TERMINATED, &is_refund, &error); if (rc == THINKTHEN_OK) puts(is_refund ? "true" : "false"); else report(&error); thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_OK ? EXIT_SUCCESS : EXIT_FAILURE;}Output
true
Yes: the message asks for a refund. thinkthen_decide writes the answer into your bool, and uses one bar at 0.5. For a low and a high bar, or to say what yes and no mean with thinkthen_question_set_yes_means and thinkthen_question_set_no_means, build a question and use thinkthen_run_one, as in the not sure example above. decide shows every option.
choose
Section titled “choose”The one choice that fits
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { const char *const topics[] = {"billing", "shipping", "other"}; thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; size_t topic = 0; int rc = thinkthen_client_open(NULL, &client, &error); // topic is the index of the chosen string in topics. if (rc == THINKTHEN_OK) rc = thinkthen_choose(client, "What is the message about?", topics, 3, "Please refund my order.", THINKTHEN_NUL_TERMINATED, &topic, &error); if (rc == THINKTHEN_OK) puts(topics[topic]); else if (rc == THINKTHEN_UNSURE) puts("not sure"); else report(&error); thinkthen_error_free(&error); thinkthen_client_free(client); return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;}Output
billing
A refund is about billing. thinkthen_choose writes the index of the chosen string in your choices, so your own string names the answer. It returns THINKTHEN_UNSURE when no choice reaches the bar. choose shows every option.
filter
Section titled “filter”Only the messages that answer yes
#include <thinkthen.h>#include <inttypes.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
static const char *const messages[] = { "Please refund my order.", "Where is my parcel?", "I was charged twice for one invoice.",};
int main(void) { thinkthen_record_input records[3]; for (size_t i = 0; i < 3; i++) { records[i].struct_size = sizeof records[i]; records[i].kind = THINKTHEN_RECORD_TEXT; records[i].data = messages[i]; records[i].len = THINKTHEN_NUL_TERMINATED; } thinkthen_client *client = NULL; thinkthen_question *asks_refund = NULL; thinkthen_call *call = NULL; thinkthen_row refund_request = THINKTHEN_ROW_INIT; thinkthen_error error = THINKTHEN_ERROR_INIT; int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_FILTER, "Does the customer request a refund?", THINKTHEN_NUL_TERMINATED, &asks_refund, &error); if (rc == THINKTHEN_OK) rc = thinkthen_call_open_records_question( client, asks_refund, NULL, records, 3, sizeof records[0], THINKTHEN_OFFSET_UTF8, &call, &error); // Only the messages that answer yes. A failed message returns its // negative code in its place, and the rest carry on. while (rc == THINKTHEN_OK || refund_request.status == THINKTHEN_STATUS_FAILED) { rc = thinkthen_call_next_kept(call, &refund_request, &error); if (rc == THINKTHEN_OK) { puts(messages[refund_request.position - 1]); } else if (refund_request.status == THINKTHEN_STATUS_FAILED) { fprintf(stderr, "message %" PRIu64 " failed: ", refund_request.position); report(&error); } } if (rc != THINKTHEN_END) report(&error); thinkthen_row_free(&refund_request); thinkthen_error_free(&error); thinkthen_call_free(call); thinkthen_question_free(asks_refund); thinkthen_client_free(client); return rc == THINKTHEN_END ? EXIT_SUCCESS : EXIT_FAILURE;}Output
Please refund my order.
I was charged twice for one invoice.
The parcel question is left out. A filter asks one question of many messages through a call. thinkthen_call_next_kept gives only the messages that answer yes, and returns THINKTHEN_END after the last. A message that fails returns its own negative code in its place, and the rest carry on. position is the message's place in your input, starting at 1, and thinkthen_row_original gives back the message itself. filter shows every option.
Every label that fits
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { const char *const labels[] = {"billing", "shipping"}; thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; bool appears[2] = {false, false}; int rc = thinkthen_client_open(NULL, &client, &error); // appears gets one answer per label, in label order. if (rc == THINKTHEN_OK) rc = thinkthen_tag(client, "Which topics appear?", labels, 2, "I was charged twice and my parcel is late again.", THINKTHEN_NUL_TERMINATED, appears, &error); if (rc == THINKTHEN_OK) { // Every label that fits, possibly none. for (size_t i = 0; i < 2; i++) if (appears[i]) puts(labels[i]); } else { report(&error); } thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_OK ? EXIT_SUCCESS : EXIT_FAILURE;}Output
billing
shipping
The message is about both billing and shipping. thinkthen_tag writes one bool per label, in label order, and all of them may be false. tag shows every option.
A number from 0 to 1 on your scale
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { const char *const levels[] = {"routine", "urgent"}; thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; double urgency = 0; int rc = thinkthen_client_open(NULL, &client, &error); // 0 means routine and 1 means urgent. if (rc == THINKTHEN_OK) rc = thinkthen_score(client, "How urgent is the request?", levels, 2, "Please refund my order.", THINKTHEN_NUL_TERMINATED, &urgency, &error); if (rc == THINKTHEN_OK) printf("%g\n", urgency); else report(&error); thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_OK ? EXIT_SUCCESS : EXIT_FAILURE;}Output
0.05
The levels run lowest first, so a plain refund request scores near routine. thinkthen_score writes a double from 0 for the first level to 1 for the last. score shows every option.
Every message, highest first
#include <thinkthen.h>#include <inttypes.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
static const char *const messages[] = { "Please refund my order.", "Where is my parcel?", "I was charged twice for one invoice.",};
int main(void) { thinkthen_record_input records[3]; for (size_t i = 0; i < 3; i++) { records[i].struct_size = sizeof records[i]; records[i].kind = THINKTHEN_RECORD_TEXT; records[i].data = messages[i]; records[i].len = THINKTHEN_NUL_TERMINATED; } thinkthen_client *client = NULL; thinkthen_question *by_urgency = NULL; thinkthen_call *call = NULL; thinkthen_row ranked = THINKTHEN_ROW_INIT; thinkthen_error error = THINKTHEN_ERROR_INIT; int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_RANK, "How urgent is the request?", THINKTHEN_NUL_TERMINATED, &by_urgency, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_add_level(by_urgency, "routine", THINKTHEN_NUL_TERMINATED, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_add_level(by_urgency, "urgent", THINKTHEN_NUL_TERMINATED, &error); if (rc == THINKTHEN_OK) rc = thinkthen_call_open_records_question( client, by_urgency, NULL, records, 3, sizeof records[0], THINKTHEN_OFFSET_UTF8, &call, &error); // Rank 1 is the most urgent. position is the message's place in // messages, starting at 1. A failed message is a row of its own. while (rc == THINKTHEN_OK && (rc = thinkthen_call_next(call, &ranked, &error)) == THINKTHEN_OK) { if (ranked.status == THINKTHEN_STATUS_ANSWERED) printf("%" PRIu64 " %s\n", ranked.rank, messages[ranked.position - 1]); else fprintf(stderr, "message %" PRIu64 " failed: %s\n", ranked.position, thinkthen_code_name(ranked.error_code)); } if (rc != THINKTHEN_END) report(&error); thinkthen_row_free(&ranked); thinkthen_error_free(&error); thinkthen_call_free(call); thinkthen_question_free(by_urgency); thinkthen_client_free(client); return rc == THINKTHEN_END ? EXIT_SUCCESS : EXIT_FAILURE;}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. Rank compares many messages, so it runs through a call. Each result's rank is its answer, 1 for the most urgent, and its position points back to your message. thinkthen_question_set_top keeps only the first few. rank shows every option.
The line that answers the question
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { const char *message = "Morgan works for Example Group.\nPlease refund my order."; thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; thinkthen_span action = {.present = 0}; int rc = thinkthen_client_open(NULL, &client, &error); // action points into message, so keep message while you read it. if (rc == THINKTHEN_OK) rc = thinkthen_find(client, "Which line states the requested action?", message, THINKTHEN_NUL_TERMINATED, &action, &error); if (rc == THINKTHEN_OK && action.present) { fwrite(action.text.data, 1, action.text.len, stdout); putchar('\n'); } else if (rc == THINKTHEN_OK) { puts("no line answers"); } else if (rc == THINKTHEN_UNSURE) { puts("not sure"); } else { report(&error); } thinkthen_error_free(&error); thinkthen_client_free(client); return rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS;}Output
Please refund my order.
The second line states the action. thinkthen_find writes a span whose present is 0 when no line answers. Its start and end are byte offsets, and its text points into your own text, so keep that text while you read the span. find shows every option.
recognize
Section titled “recognize”Each name in the text and its kind
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
int main(void) { const char *const kinds[] = {"person", "organization"}; thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; thinkthen_value *names = NULL; size_t count = 0; // recognize takes kinds, not question wording. int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_recognize(client, kinds, 2, "Morgan works for Example Group.", THINKTHEN_NUL_TERMINATED, &names, &error); if (rc == THINKTHEN_OK) rc = thinkthen_value_name_count(names, &count, &error); for (size_t i = 0; rc == THINKTHEN_OK && i < count; i++) { thinkthen_name name; rc = thinkthen_value_name_at(names, i, &name, &error); if (rc == THINKTHEN_OK) { fwrite(name.kind.data, 1, name.kind.len, stdout); fputs(": ", stdout); fwrite(name.text.data, 1, name.text.len, stdout); putchar('\n'); } } if (rc != THINKTHEN_OK) report(&error); thinkthen_value_free(names); thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_OK ? EXIT_SUCCESS : EXIT_FAILURE;}Output
person: Morgan
organization: Example Group
thinkthen_recognize writes a new value. Read its names with thinkthen_value_name_count and thinkthen_value_name_at, before you free it with thinkthen_value_free. recognize shows every option.
relate
Section titled “relate”Names linked by the relations you allow
#include <thinkthen.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
static void print_text(thinkthen_text text) { fwrite(text.data, 1, text.len, stdout);}
int main(void) { const char *message = "Morgan works for Example Group.\nPlease refund my order."; const thinkthen_rule works_for = { .relation = {"works_for", THINKTHEN_NUL_TERMINATED}, .source = {"person", THINKTHEN_NUL_TERMINATED}, .target = {"organization", THINKTHEN_NUL_TERMINATED}, }; thinkthen_client *client = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; thinkthen_value *relations = NULL; size_t count = 0; // relate takes rules, not question wording. int rc = thinkthen_client_open(NULL, &client, &error); if (rc == THINKTHEN_OK) rc = thinkthen_relate(client, &works_for, 1, message, THINKTHEN_NUL_TERMINATED, &relations, &error); if (rc == THINKTHEN_OK) rc = thinkthen_value_relation_count(relations, &count, &error); for (size_t i = 0; rc == THINKTHEN_OK && i < count; i++) { thinkthen_relation relation; rc = thinkthen_value_relation_at(relations, i, &relation, &error); if (rc == THINKTHEN_OK) { print_text(relation.source.text); fputs(" --", stdout); print_text(relation.relation); fputs("--> ", stdout); print_text(relation.target.text); putchar('\n'); } } if (rc != THINKTHEN_OK) report(&error); thinkthen_value_free(relations); thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_OK ? EXIT_SUCCESS : EXIT_FAILURE;}Output
Morgan --works_for--> Example Group
A thinkthen_rule names the kind of name at each end and the relation between them. thinkthen_relate takes an array of rules and writes a new value; read it with thinkthen_value_relation_count and thinkthen_value_relation_at. relate shows every option.
annotate
Section titled “annotate”Several questions for each message
#include <thinkthen.h>#include <inttypes.h>#include <stdio.h>#include <stdlib.h>
static void report(const thinkthen_error *error) { fprintf(stderr, "%s: ", thinkthen_code_name(error->code)); fwrite(error->message.data, 1, error->message.len, stderr); fputc('\n', stderr);}
// Print the answer named name, "not sure", or "failed" with its code.static void print_answer(const thinkthen_value *form, const char *name) { thinkthen_error error = THINKTHEN_ERROR_INIT; thinkthen_value *answer = NULL; thinkthen_function function = THINKTHEN_FUNCTION_DECIDE; uint8_t is_yes = 0; thinkthen_text label = {.len = 0}; double number = 0; int rc = thinkthen_value_annotation_get(form, name, THINKTHEN_NUL_TERMINATED, &answer, &error); if (rc == THINKTHEN_OK) rc = thinkthen_value_function(answer, &function, &error); if (rc == THINKTHEN_OK && function == THINKTHEN_FUNCTION_DECIDE) rc = thinkthen_value_bool(answer, &is_yes, &error); else if (rc == THINKTHEN_OK && function == THINKTHEN_FUNCTION_CHOOSE) rc = thinkthen_value_choice(answer, &label, &error); else if (rc == THINKTHEN_OK) rc = thinkthen_value_number(answer, &number, &error); if (rc == THINKTHEN_UNSURE) fputs("not sure", stdout); else if (rc != THINKTHEN_OK) printf("failed (%s)", thinkthen_code_name(error.code)); else if (function == THINKTHEN_FUNCTION_DECIDE) fputs(is_yes ? "true" : "false", stdout); else if (function == THINKTHEN_FUNCTION_CHOOSE) fwrite(label.data, 1, label.len, stdout); else printf("%g", number); thinkthen_value_free(answer); thinkthen_error_free(&error);}
static const char *const messages[] = { "Please refund my order.", "Where is my parcel?", "I was charged twice for one invoice.",};
int main(void) { thinkthen_record_input records[3]; for (size_t i = 0; i < 3; i++) { records[i].struct_size = sizeof records[i]; records[i].kind = THINKTHEN_RECORD_TEXT; records[i].data = messages[i]; records[i].len = THINKTHEN_NUL_TERMINATED; } const char *const names[] = {"refund", "topic", "urgency"}; const char *const choices[] = {"billing", "shipping", "other"}; const char *const levels[] = {"routine", "urgent"}; thinkthen_client *client = NULL; thinkthen_question *refund = NULL, *topic = NULL, *urgency = NULL; thinkthen_question *triage = NULL; thinkthen_call *call = NULL; thinkthen_row annotated = THINKTHEN_ROW_INIT; thinkthen_value *form = NULL; thinkthen_error error = THINKTHEN_ERROR_INIT; int rc = thinkthen_client_open(NULL, &client, &error); // The form: three named questions, each asked of every message. if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_DECIDE, "Does the customer request a refund?", THINKTHEN_NUL_TERMINATED, &refund, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_CHOOSE, "What is the message about?", THINKTHEN_NUL_TERMINATED, &topic, &error); for (size_t i = 0; rc == THINKTHEN_OK && i < 3; i++) rc = thinkthen_question_add_choice(topic, choices[i], THINKTHEN_NUL_TERMINATED, &error); if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_SCORE, "How urgent is the request?", THINKTHEN_NUL_TERMINATED, &urgency, &error); for (size_t i = 0; rc == THINKTHEN_OK && i < 2; i++) rc = thinkthen_question_add_level(urgency, levels[i], THINKTHEN_NUL_TERMINATED, &error); // annotate takes named questions, not question wording. if (rc == THINKTHEN_OK) rc = thinkthen_question_new(THINKTHEN_FUNCTION_ANNOTATE, "", 0, &triage, &error); const thinkthen_question *const parts[] = {refund, topic, urgency}; for (size_t n = 0; rc == THINKTHEN_OK && n < 3; n++) rc = thinkthen_question_add_entry(triage, names[n], THINKTHEN_NUL_TERMINATED, parts[n], &error); if (rc == THINKTHEN_OK) rc = thinkthen_call_open_records_question( client, triage, NULL, records, 3, sizeof records[0], THINKTHEN_OFFSET_UTF8, &call, &error); // One line per message: refund, topic and urgency. A failed message // is a row of its own, and the rest carry on. while (rc == THINKTHEN_OK && (rc = thinkthen_call_next(call, &annotated, &error)) == THINKTHEN_OK) { if (annotated.status == THINKTHEN_STATUS_FAILED) { printf("message %" PRIu64 " failed (%s)\n", annotated.position, thinkthen_code_name(annotated.error_code)); continue; } thinkthen_value_free(form); rc = thinkthen_row_values(&annotated, &form, &error); if (rc != THINKTHEN_OK) break; for (size_t n = 0; n < 3; n++) { if (n > 0) putchar(' '); print_answer(form, names[n]); } putchar('\n'); } if (rc != THINKTHEN_END) report(&error); thinkthen_value_free(form); thinkthen_row_free(&annotated); thinkthen_call_free(call); thinkthen_question_free(triage); thinkthen_question_free(refund); thinkthen_question_free(topic); thinkthen_question_free(urgency); thinkthen_error_free(&error); thinkthen_client_free(client); return rc == THINKTHEN_END ? EXIT_SUCCESS : EXIT_FAILURE;}Output
true billing 0.05
false shipping 0.14
true billing 0.45
An annotate question holds several named questions, added with thinkthen_question_add_entry. Over many messages it runs through a call; thinkthen_annotate asks it of one message. thinkthen_row_values gives a message's answers, and thinkthen_value_annotation_get reads one by name. Each answer returns THINKTHEN_UNSURE or its own failure code instead of writing an answer that isn't there. annotate shows every option.
The full API
Section titled “The full API”For a JSON message, pass THINKTHEN_RECORD_JSON, and point the question at one field with thinkthen_question_set_field, such as "/text". thinkthen_call_open_question takes messages one at a time, for input you don't hold up front. One thread drives a call, thinkthen_call_cancel stops it from any thread, and threads may share one client. The C header lists every type and function.