# R functions that answer questions about text

The ten functions work inside dplyr pipelines. Read the answer from `$value`.

## Install

| Channel |
| --- |
| `install.packages("thinkthen")` |

Set `THINKTHEN_API_KEY` in the environment.

## A first call

```
library(thinkthen)

question <- "Does the customer ask for a refund?"

broken <- "Please refund my order. It arrived broken."
is_refund <- tt_decide(question, broken)$value
stopifnot(identical(is_refund, TRUE))

send_back <- "I want to send this back."
is_refund <- tt_decide(
  question,
  send_back,
  threshold = "0.2:0.8"
)$value
stopifnot(identical(is_refund, NA))
```

Not sure comes back as `NA`. Branch on it and send those cases to a person. Each [function page](/functions/) shows its R call.

To apply one question to a list or a stream, read [Functional patterns](/learn/functional/).

## Data frames

A verb inside `mutate()` answers a whole column in one call. dplyr's `filter()` drops NA rows, so a not-sure answer leaves the pipeline on its own.

```
library(dplyr)
library(thinkthen)

question <- "Does the customer ask for a refund?"
messages <- tibble(
  body = c(
    "Please refund my order. It arrived broken.",
    "I want to send this back."
  )
)

answered <- messages |>
  mutate(
    is_refund = tt_decide(
      question,
      body,
      threshold = "0.2:0.8"
    )$value
  )
stopifnot(identical(answered$is_refund, c(TRUE, NA)))

refunds <- answered |> filter(is_refund)
stopifnot(identical(refunds$body, messages$body[1]))
```

## Run facts

`$facts` counts this call.

## Errors

Each failure arrives as an R condition named for its kind, such as `thinkthen_usage`. Each condition carries `retryable`.

## Settings and recording

`tt_engine()` takes each setting as an argument and reads the environment for the rest. `record =` writes a recording to a folder. `replay =` answers from that recording with no connection. The [Settings](/install/settings/) page lists every setting.

## Jev, Liquid d1 and Ollama from R

R reads its backend from the environment. Set `THINKTHEN_BACKEND` to the name below. Set the backend's key variable in the shell, and the same sample asks that backend.

| Backend | Name | Key variable |
| --- | --- | --- |
| [TypeSafe Jev](/install/backends/typesafe/) | `typesafe` | `TYPESAFE_API_KEY` |
| [Liquid d1](/install/backends/liquid/) | `liquid` | `LIQUIDAI_API_KEY`, then `LIQUID_API_KEY` |
| [Ollama](/install/backends/ollama/) | `ollama` | `OLLAMA_API_KEY`. A local address needs no key. |

*Ask TypeSafe Jev.*

```
export THINKTHEN_BACKEND=typesafe
export TYPESAFE_API_KEY=...
```

*Ask Liquid d1.*

```
export THINKTHEN_BACKEND=liquid
export LIQUIDAI_API_KEY=...
```

*Ask Ollama. This block names a second port, 11535, as the site's runs do. On the default port, leave THINKTHEN_BASE_URL out.*

```
export THINKTHEN_BACKEND=ollama
export THINKTHEN_BASE_URL=http://localhost:11535/v1
```

```
library(thinkthen)

question <- "Does the customer ask for a refund?"
texts <- c(
  "Please refund my order. It arrived broken.",
  "Thanks for the quick help yesterday!"
)
is_refund <- tt_decide(question, texts)$value
stopifnot(identical(is_refund, c(TRUE, FALSE)))
```

The configuration file's `backend` names a default backend for every run. The [Configuration](/install/configuration/#configuration-file) page shows the file.

### OpenAI Decisions API from R

OpenAI announced its Decisions API on 2026-09-29 in its [DevDay 2026 recap](https://openai.com/index/devday-2026-recap/). OpenAI has published no address, schema or price, so ThinkThen cannot call it. The [OpenAI Decisions API](/install/backends/openai/) page says what ThinkThen knows.

## What is different in R

- A column goes in and the answered column is in `$value`.

On GitHub: [github.com/botassembly/thinkthen](https://github.com/botassembly/thinkthen)
