Setting up large language models

Exercise

Exercise 1
Author

Emi Tanaka

Published

June 3, 2025

A 2D digital illustration shows a software installation process on a large computer monitor.



I will demonstrate using two methods via: Open AI and Ollama. You can choose to code along with one or both, or none!

install.packages(c('usethis', 'ellmer'))

1 Using LLM with ellmer

The ellmer R package provides a user-friendly interface to large language models.

Warning

The ellmer R package is in very active development. There may be breaking changes introduced in the near future (hopefully not during the workshop).

For this workshop we are using version 0.2.0 of ellmer.

1.1 LLM with Open AI

Setup for Open AI

First to use Open AI, you will need to get the Open AI API key. Register an account with Open AI and go here to generate a new API key and add some credit.

For workshop participants, you would have received a API key/token in your email. You can use this during the workshop, but please note it will stop working after the workshop so you will need to register your own account and API key as above afterwards.

You will need to store the API key as OPENAI_API_KEY in your .Renviron file.

  1. Find the .Renviron file. The easiest way to find .Renviron file is to use
usethis::edit_r_environ()

This should open the .Renviron file.

  1. Enter your API key in a new line in .Renviron like below.
.Renviron
OPENAI_API_KEY=<YOUR API KEY>
  1. Restart your R session so the above environmental variable takes effect.

To check if this worked, try running the command below to see if you see your API key.

Sys.getenv("OPENAI_API_KEY")
library(ellmer)

Once you have set up the API key, you can use the ellmer package to interact with the AI provider’s API.

chat <- chat_openai(model = "gpt-4o-mini")

chat$chat("Tell me a statistics joke!")
Why did the statistician bring a ladder to the bar?

Because they heard the drinks were on the house!

1.2 Local LLM with Ollama

A local LLM via Ollama:

  • require no payment for usage,
  • require no internet access after you download/pull the LLM, and
  • does not transfer your data to a provider.

You do require sufficient RAM to use these models locally (at least 16GB for 13b models), so it is preferable to use a more powerful computer.

Setup for Ollama

Download Ollama at https://ollama.com/download.

Download at least one local LLM. Please note that these local LLMs are large (several GB) and may take some time (>10 minutes) to download. I will mainly use llama3.1:8b which can be downloaded/pulled by using the command below in the terminal (not the R console):

ollama pull llama3.1:8b

If you do not have a large RAM (<8GB), you may try a smaller model such as llama3.2:1b (but its output may not be as accurate):

ollama pull llama3.2:1b

To check that Ollama is installed properly, open a terminal and check the version of Ollama

ollama --version
chat <- chat_ollama(model = "llama3.1:8b")

chat$chat("Tell me a statistics joke!")
Here's one:

Why was the math book looking so sad?

Because it had too many problems.

(Sorry, I know it's a bit of a statistically improbable pun...)

1.3 Other AI provider’s API

ellmer, as of version 0.2.0, supports the AI providers in Table 1. To use the API, you need to sign up for an account with the provider and obtain an API key or some other authentication or connection method. You may need to pay for some small credit to use it. The API key can be set in a similar manner to Open AI API key (described in Section 1.1), but using the corresponding environmental variable name.

Table 1: AI Providers
Provider Name Link Environmental Variable
Anthropic chat_anthropic() https://www.anthropic.com/claude ANTHROPIC_API_KEY
Amazon chat_aws_bedrock() https://aws.amazon.com/bedrock/
Azure OpenAI chat_azure_openai() https://azure.microsoft.com/en-us/products/ai-services/openai-service AZURE_OPENAI_API_KEY
CloudFlare chat_cloudflare() https://www.cloudflare.com/en-gb/developer-platform/products/workers-ai/ CLOUDFLARE_API_KEY
Cortex Analyst chat_cortex_analyst() https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-analyst
Databricks chat_databricks() https://www.databricks.com/
Deepseek chat_deepseek() https://platform.deepseek.com/ DEEPSEEK_API_KEY
GitHub chat_github() https://github.com/marketplace/models GITHUB_PAT
Google Gemini chat_google_gemini() https://aistudio.google.com// GEMINI_API_KEY or GOOGLE_API_KEY
Google Vertex AI chat_google_vertex() https://cloud.google.com/vertex-ai GOOGLE_API_KEY
Groq chat_groq() https://groq.com GROQ_API_KEY
Hugging Face chat_huggingface() https://huggingface.co/ HUGGINGFACE_API_KEY
Mistral chat_mistral() https://console.mistral.ai MISTRAL_API_KEY
Ollama chat_ollama() https://ollama.com/
Open AI chat_openai() https://platform.openai.com/ OPENAI_API_KEY
Openrouter chat_openrouter() https://openrouter.ai/ OPENROUTER_API_KEY
Perplexity AI chat_perplexity() https://www.perplexity.ai PERPLEXITY_API_KEY
Portkey AI chat_portkey() https://portkey.ai PORTKEY_API_KEY
Snowflake chat_snowflake() https://docs.snowflake.com/
vLLM chat_vllm() https://docs.vllm.ai VLLM_API_KEY

1.3.1 Gemini via ellmer

Setup for Gemini

The setup is similar to Open AI. First you need an account at https://aistudio.google.com/, then you need to get your API key at https://aistudio.google.com/apikey. Set this key as the environmental variable GOOGLE_API_KEY.

To check if it’s read correctly in R, run the following code to check.

Sys.getenv("GOOGLE_API_KEY")
chat <- chat_google_gemini(model = "gemini-2.0-flash")

chat$chat("Tell me a statistics joke!")
Why did the statistician bring a ladder to his math class?

Because he heard the class was going to be about higher powers!

1.3.2 Claude via ellmer

Setup for Gemini

The setup is similar as before. First you need an account at https://console.anthropic.com/, then you need to get your API key at https://console.anthropic.com/settings/keys. Set this key as the environmental variable ANTHROPIC_API_KEY.

Run the following code to check if the key is stored correctly.

Sys.getenv("ANTHROPIC_API_KEY")

Some people have claimed that Anthropic’s Claude model are better at R code tasks. Those interested can have a look at the vitals package, which provides a framework to evaluate LLM and contains a dataset with R coding tasks.

chat <- chat_anthropic(model = "claude-3-7-sonnet-latest")

chat$chat("Tell me a statistics joke!")
Why did the statistician get thrown out of the pool?

For performing an illegal z-dive!

I guess you could say the probability of that joke making you laugh was... 
statistically significant. Or maybe just mean. 😄

Which joke was funnier? 😄

2 The “hard” way with httr2

The httr2 package gives a method to create, modify, and perform HTTP requests. The ellmer package is a user friendly wrapper for making HTTP requests to get responses from the provider’s LLM. When the LLM is available in ellmer, it’s more convenient to use it, but sometimes there is a lag before it gets implemented in ellmer.

library(httr2)

2.1 Using Open AI

Open AI’s API reference shows an example HTTP request for chat completions like below.

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4.1",
    "messages": [
      {
        "role": "user",
        "content": "All models are wrong, but some models are"
      }
    ]
  }'

If you want to make a request like above in R, this is where it’s handy to use the httr2 package. Notice the similarity in the HTTP request above and the R code below.

json <- request(base_url = "https://api.openai.com/v1/chat/completions") |> 
  req_headers('Authorization' = paste('Bearer', Sys.getenv("OPENAI_API_KEY")),
              'Content-Type' = 'application/json') |> 
  req_body_json(data = list(model = "gpt-4.1",
                            messages = list(list(role = "user",
                                                 content = "All models are wrong, but some models are")))) |> 
  req_perform() |> 
  resp_body_json()

The benefit of using above is that above JSON response has been returned as a list object in R.

str(json)
List of 8
 $ id                : chr "chatcmpl-BeAhKfPmu9oJBQ1Rba9bFc8iKun36"
 $ object            : chr "chat.completion"
 $ created           : int 1748913942
 $ model             : chr "gpt-4.1-2025-04-14"
 $ choices           :List of 1
  ..$ :List of 4
  .. ..$ index        : int 0
  .. ..$ message      :List of 4
  .. .. ..$ role       : chr "assistant"
  .. .. ..$ content    : chr "useful.\n\nThis phrase is a well-known quote by the statistician **George Box**. The full quote is:\n\n> “All m"| __truncated__
  .. .. ..$ refusal    : NULL
  .. .. ..$ annotations: list()
  .. ..$ logprobs     : NULL
  .. ..$ finish_reason: chr "stop"
 $ usage             :List of 5
  ..$ prompt_tokens            : int 16
  ..$ completion_tokens        : int 86
  ..$ total_tokens             : int 102
  ..$ prompt_tokens_details    :List of 2
  .. ..$ cached_tokens: int 0
  .. ..$ audio_tokens : int 0
  ..$ completion_tokens_details:List of 4
  .. ..$ reasoning_tokens          : int 0
  .. ..$ audio_tokens              : int 0
  .. ..$ accepted_prediction_tokens: int 0
  .. ..$ rejected_prediction_tokens: int 0
 $ service_tier      : chr "default"
 $ system_fingerprint: chr "fp_51e1070cf2"

The response is found in one of the elements in the returned list object:

cat(json$choices[[1]]$message$content)
useful.

This phrase is a well-known quote by the statistician **George Box**. The full quote is:

> “All models are wrong, but some are useful.”

It means that while no mathematical model or theory can capture the full complexity of reality, models can still provide valuable insights and predictions. Therefore, models should not be judged solely by their accuracy but also by their utility in helping us understand and make decisions.

2.2 Using Ollama

Ollama has two APIs:

You may notice that the Ollama documentation is not well polished, so you may at times need to dig into the issues or ask the developer.

Let’s use the first endpoint as demonstration.

curl http://localhost:11434/api/generate -d '{
  "model": "llama3.1:8b",
  "prompt": "All models are wrong, but some models are",
  "stream": false
}'

The corresponding code in R would be as follows.

json <- request(base_url = "http://localhost:11434/api/generate") |> 
  req_body_json(data = list(model = "llama3.1:8b",
                            prompt = "All models are wrong, but some models are",
                            stream = FALSE)) |> 
  req_perform() |> 
  resp_body_json()

Notice that the return object is a different structure to before.

str(json)
List of 12
 $ model               : chr "llama3.1:8b"
 $ created_at          : chr "2025-06-03T01:25:48.275688Z"
 $ response            : chr "A classic quote!\n\nThe phrase \"All models are wrong, but some models are useful\" is a paraphrased version of"| __truncated__
 $ done                : logi TRUE
 $ done_reason         : chr "stop"
 $ context             :List of 276
  ..$ : int 128006
  ..$ : int 882
  ..$ : int 128007
  ..$ : int 271
  ..$ : int 2460
  ..$ : int 4211
  ..$ : int 527
  ..$ : int 5076
  ..$ : int 11
  ..$ : int 719
  ..$ : int 1063
  ..$ : int 4211
  ..$ : int 527
  ..$ : int 128009
  ..$ : int 128006
  ..$ : int 78191
  ..$ : int 128007
  ..$ : int 271
  ..$ : int 32
  ..$ : int 11670
  ..$ : int 12929
  ..$ : int 2268
  ..$ : int 791
  ..$ : int 17571
  ..$ : int 330
  ..$ : int 2460
  ..$ : int 4211
  ..$ : int 527
  ..$ : int 5076
  ..$ : int 11
  ..$ : int 719
  ..$ : int 1063
  ..$ : int 4211
  ..$ : int 527
  ..$ : int 5505
  ..$ : int 1
  ..$ : int 374
  ..$ : int 264
  ..$ : int 63330
  ..$ : int 81
  ..$ : int 1503
  ..$ : int 2373
  ..$ : int 315
  ..$ : int 264
  ..$ : int 11495
  ..$ : int 5224
  ..$ : int 1903
  ..$ : int 555
  ..$ : int 10058
  ..$ : int 8425
  ..$ : int 11
  ..$ : int 264
  ..$ : int 8013
  ..$ : int 43589
  ..$ : int 1122
  ..$ : int 323
  ..$ : int 24490
  ..$ : int 382
  ..$ : int 644
  ..$ : int 813
  ..$ : int 220
  ..$ : int 4468
  ..$ : int 21
  ..$ : int 5684
  ..$ : int 330
  ..$ : int 36500
  ..$ : int 323
  ..$ : int 25647
  ..$ : int 1359
  ..$ : int 8425
  ..$ : int 6267
  ..$ : int 1473
  ..$ : int 68149
  ..$ : int 4211
  ..$ : int 527
  ..$ : int 10049
  ..$ : int 97476
  ..$ : int 13
  ..$ : int 763
  ..$ : int 279
  ..$ : int 1972
  ..$ : int 1917
  ..$ : int 682
  ..$ : int 24654
  ..$ : int 527
  ..$ : int 3917
  ..$ : int 311
  ..$ : int 6103
  ..$ : int 13
  ..$ : int 2052
  ..$ : int 4211
  ..$ : int 527
  ..$ : int 5076
  ..$ : int 2266
  ..$ : int 4071
  ..$ : int 1243
  ..$ : int 568
  ..$ : int 3779
  ..$ : int 1473
  .. [list output truncated]
 $ total_duration      : num 4.33e+09
 $ load_duration       : int 33130500
 $ prompt_eval_count   : int 19
 $ prompt_eval_duration: int 217737916
 $ eval_count          : int 259
 $ eval_duration       : num 4.07e+09

To access the model response, you can find this in the response element:

cat(json$response)
A classic quote!

The phrase "All models are wrong, but some models are useful" is a paraphrased version of a famous statement made by George Box, a British statistician and engineer.

In his 1976 paper "Science and Statistics," Box wrote:

"All models are approximations. In the real world all observations are subject to errors. All models are wrong."

But then he added:

"But some models are more useful than others."

This phrase has since become a mantra in many fields, including science, statistics, economics, and business, emphasizing that while no model can perfectly capture reality, some models can still provide valuable insights, predictions, or guidance.

The idea is that even though all models have limitations and make assumptions about the world, some models are more accurate, reliable, or useful than others. This might be due to factors like:

* The quality of data used to build the model
* The simplicity or complexity of the model itself
* The domain expertise and knowledge of the people who created the model

So, while all models may be wrong in some sense, Box's quote encourages us to seek out models that are still useful, even if they're not perfect.

Do you have any specific context where this phrase might apply? I'm here to help!

🎉 Aha! You’ve reached the end of this exercise! You are bee’s knees! Proceed to …

next slide

next exercise