Get an LLM's help interpreting a natural-language table search
Source:R/statcan_chat.R
statcan_chat.RdSends the query and the ranked candidates from statcan_find() to a
user-configured language-model provider, which explains which
candidate(s) best match and asks a clarifying question when the query is
ambiguous. The candidate table numbers and rankings always come from
statcan_find() itself; the language model only interprets and explains
them, and is never allowed to propose a table number of its own.
Arguments
- query
One non-empty character string describing the desired data. Passed to
statcan_find().- lang
Language of the table titles and of the model's reply:
"eng"or"fra".- n
Maximum number of candidates to request from
statcan_find().- refresh
Logical; forwarded to
statcan_find().- endpoint
Provider endpoint URL. Defaults to
getOption("statcanR.llm_endpoint"), thenSys.getenv("STATCANR_LLM_ENDPOINT"), then the chosen provider's own endpoint. Must behttps://, except for loopback hosts (for example,http://localhost).- api_key
API key. Defaults to
Sys.getenv("STATCANR_LLM_API_KEY"), then the provider's native variable (OPENAI_API_KEYfor"openai",ANTHROPIC_API_KEYfor"anthropic"). For safety it is not read fromoptions(). It is sent as anAuthorization: Bearerheader for"openai"and as anx-api-keyheader for"anthropic".- model
Model name sent to the provider (for example,
"gpt-4o-mini"for OpenAI or"claude-opus-4-8"for Anthropic). Defaults togetOption("statcanR.llm_model"), thenSys.getenv("STATCANR_LLM_MODEL").- provider
Which LLM provider to use:
"openai"(the default, also covering OpenAI-compatible and local servers) or"anthropic"(Claude).
Value
A statcan_chat_result object: a list with query, candidates
(the statcan_find() data frame), explanation, and
clarifying_question (NA when the model had none).
Details
Two providers ship built in, selected with the provider argument:
"openai"(the default): the OpenAI chat-completions format, using anAuthorization: BearerAPI key. This also covers any OpenAI-compatible server – Groq, Together, OpenRouter, Mistral, vLLM, or a local open-source model served by Ollama or LM Studio – by pointingendpointat it (a loopbackhttp://localhostendpoint is accepted for local models; pass any placeholderapi_keyfor servers that ignore it)."anthropic": the Claude Messages format, using anx-api-keyheader.
This is an optional feature. It requires no additional packages beyond
what statcanR already imports, but it does require you to configure an
API key and model (the endpoint defaults to the chosen provider), either
as arguments or through options() / environment variables:
endpoint:options(statcanR.llm_endpoint = ...)orSys.setenv(STATCANR_LLM_ENDPOINT = ...). Defaults to the provider's own endpoint when unset.api_key:Sys.setenv(STATCANR_LLM_API_KEY = ...)(or theapi_keyargument, or the provider's native variable –OPENAI_API_KEY/ANTHROPIC_API_KEY). Because it is a secret, the key is not read fromoptions(), which can be dumped, saved with a session, or recorded in.Rhistory.model:options(statcanR.llm_model = ...)orSys.setenv(STATCANR_LLM_MODEL = ...)
The endpoint must use https:// so the key is never sent in cleartext;
plain http:// is accepted only for loopback hosts (for example,
http://localhost for a local model).
No network request is made unless statcan_chat() is called directly.
Examples
if (FALSE) { # \dontrun{
# OpenAI (the default provider)
Sys.setenv(OPENAI_API_KEY = "sk-...")
result <- statcan_chat(
"R&D expenditures in Quebec since 2020",
model = "gpt-4o-mini"
)
# Anthropic (Claude)
Sys.setenv(ANTHROPIC_API_KEY = "sk-ant-...")
result <- statcan_chat(
"R&D expenditures in Quebec since 2020",
provider = "anthropic", model = "claude-opus-4-8"
)
# A local open-source model served by Ollama (no key over loopback http)
result <- statcan_chat(
"R&D expenditures in Quebec since 2020",
endpoint = "http://localhost:11434/v1/chat/completions",
api_key = "ollama", model = "llama3.1"
)
# statcan_chat() returns several ranked candidates, not a single table:
# the model explains them but never picks or invents one for you.
# The candidates are already a data frame, so you never retype an id.
result$candidates # the full statcan_find() data frame
result$candidates$id # every candidate id, best-ranked first
result$candidates$id[1] # just the top-ranked id
result$explanation # the model's plain-language explanation
# Feed the chosen id straight into statcan_data() -- nothing copied by hand.
table_data <- statcan_data(result$candidates$id[1], lang = "eng")
} # }